diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 00000000000..df6ae9a1c1f
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,4 @@
+* text eol=lf
+*.png binary
+*.jpg binary
+*.pfx binary
\ No newline at end of file
diff --git a/.github/PAUL.yaml b/.github/PAUL.yaml
index 2585031c2a9..307f134de3f 100644
--- a/.github/PAUL.yaml
+++ b/.github/PAUL.yaml
@@ -5,7 +5,6 @@ maintainers:
- sebagomez
- rodrmartinez
- IdanAdar
-- shuheiktgw
- skarlso
- rogertuma
# Emeritus Approvers
diff --git a/.github/actions/e2e/action.yml b/.github/actions/e2e/action.yml
index 7d794c8dd42..08c1a3278d4 100644
--- a/.github/actions/e2e/action.yml
+++ b/.github/actions/e2e/action.yml
@@ -58,7 +58,6 @@ runs:
go version
ginkgo version
cd e2e && go mod tidy && git status && git diff
-
- name: Run e2e Tests
shell: bash
env:
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 81d295d7c2f..586a461da15 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -9,8 +9,8 @@ on:
env:
# Common versions
- GOLANGCI_VERSION: 'v1.57.2'
- KUBERNETES_VERSION: '1.30.x'
+ GOLANGCI_VERSION: 'v1.61.0'
+ KUBERNETES_VERSION: '1.31.x'
# Sonar
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
@@ -46,10 +46,10 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup Go
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
+ uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
id: setup-go
with:
go-version-file: "go.mod"
@@ -59,7 +59,7 @@ jobs:
run: go mod download
- name: Lint
- uses: golangci/golangci-lint-action@3cfe3a4abbb849e10058ce4af15d205b6da42804 # v4.0.0
+ uses: golangci/golangci-lint-action@ec5d18412c0aeab7936cb16880d708ba2a64e1ae # v6.2.0
with:
version: ${{ env.GOLANGCI_VERSION }}
skip-pkg-cache: true
@@ -72,10 +72,10 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Setup Go
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
+ uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
id: setup-go
with:
go-version-file: "go.mod"
@@ -100,13 +100,13 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Fetch History
run: git fetch --prune --unshallow
- name: Setup Go
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
+ uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
id: setup-go
with:
go-version-file: "go.mod"
@@ -116,7 +116,7 @@ jobs:
run: go mod download
- name: Cache envtest binaries
- uses: actions/cache@0c45773b623bea8c8e75f6c82b208c3cf94ea4f9 # v4.0.2
+ uses: actions/cache@1bd1e32a3bdc45362d1e726936510720a7c30a57 # v4.2.0
with:
path: bin/k8s
key: ${{ runner.os }}-envtest-${{env.KUBERNETES_VERSION}}
@@ -126,7 +126,7 @@ jobs:
make test
- name: Publish Unit Test Coverage
- uses: codecov/codecov-action@84508663e988701840491b86de86b666e8a86bed # v4.3.0
+ uses: codecov/codecov-action@1e68e06f1dbfde0e4cefc87efeba9e4643565303 # v5.1.2
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
with:
@@ -145,18 +145,18 @@ jobs:
include:
- dockerfile: "Dockerfile"
build-args: "CGO_ENABLED=0"
- build-arch: "amd64 arm64 s390x"
- build-platform: "linux/amd64,linux/arm64,linux/s390x"
+ build-arch: "amd64 arm64 s390x ppc64le"
+ build-platform: "linux/amd64,linux/arm64,linux/s390x,linux/ppc64le"
tag-suffix: "" # distroless
- dockerfile: "Dockerfile.ubi"
build-args: "CGO_ENABLED=0"
- build-arch: "amd64 arm64"
- build-platform: "linux/amd64,linux/arm64"
+ build-arch: "amd64 arm64 ppc64le"
+ build-platform: "linux/amd64,linux/arm64,linux/ppc64le"
tag-suffix: "-ubi"
- dockerfile: "Dockerfile.ubi"
build-args: "CGO_ENABLED=0 GOEXPERIMENT=boringcrypto"
- build-arch: "amd64"
- build-platform: "linux/amd64"
+ build-arch: "amd64 ppc64le"
+ build-platform: "linux/amd64,linux/ppc64le"
tag-suffix: "-ubi-boringssl"
with:
dockerfile: ${{ matrix.dockerfile }}
diff --git a/.github/workflows/dlc.yml b/.github/workflows/dlc.yml
index a26f8448eb4..bff5dbbe671 100644
--- a/.github/workflows/dlc.yml
+++ b/.github/workflows/dlc.yml
@@ -14,15 +14,15 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: "Checkout Code"
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: "Run FOSSA Scan"
- uses: fossas/fossa-action@47ef11b1e1e3812e88dae436ccbd2d0cbd1adab0 # main
+ uses: fossas/fossa-action@93a52ecf7c3ac7eb40f5de77fd69b1a19524de94 # main
with:
api-key: ${{secrets.FOSSA_API_KEY}}
- name: "Run FOSSA Test"
- uses: fossas/fossa-action@47ef11b1e1e3812e88dae436ccbd2d0cbd1adab0 # main
+ uses: fossas/fossa-action@93a52ecf7c3ac7eb40f5de77fd69b1a19524de94 # main
with:
api-key: ${{secrets.FOSSA_API_KEY}}
run-tests: true
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 2d622795ff0..1b0bc7878d9 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -15,12 +15,12 @@ jobs:
permissions:
contents: write
steps:
- - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Setup Go
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
+ uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
with:
go-version-file: "go.mod"
diff --git a/.github/workflows/e2e-managed.yml b/.github/workflows/e2e-managed.yml
index 11088cc5d31..279bf7a06a9 100644
--- a/.github/workflows/e2e-managed.yml
+++ b/.github/workflows/e2e-managed.yml
@@ -64,7 +64,7 @@ jobs:
# Check out merge commit
- name: Fork based /ok-to-test-managed checkout
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: 'refs/pull/${{ env.GITHUB_PR_NUMBER }}/merge'
diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml
index 004788a0d9c..6c75701eac7 100644
--- a/.github/workflows/e2e.yml
+++ b/.github/workflows/e2e.yml
@@ -6,7 +6,10 @@ on:
permissions:
contents: read
-
+ issues: write
+ pull-requests: write
+ checks: write
+ statuses: read
name: e2e tests
env:
@@ -20,6 +23,7 @@ env:
# Common users. We can't run a step 'if secrets.GHCR_USERNAME != ""' but we can run
# a step 'if env.GHCR_USERNAME' != ""', so we copy these to succinctly test whether
# credentials have been provided before trying to run steps that need them.
+ TARGET_SHA: ${{ github.event.client_payload.slash_command.args.named.sha }}
GHCR_USERNAME: ${{ secrets.GHCR_USERNAME }}
GCP_SM_SA_JSON: ${{ secrets.GCP_SM_SA_JSON}}
GCP_GKE_ZONE: ${{ secrets.GCP_GKE_ZONE}}
@@ -29,13 +33,15 @@ env:
AWS_REGION: "eu-central-1"
AWS_OIDC_ROLE_ARN: ${{ secrets.AWS_OIDC_ROLE_ARN }}
+ AWS_SA_NAME: ${{ secrets.AWS_SA_NAME }}
+ AWS_SA_NAMESPACE: ${{ secrets.AWS_SA_NAMESPACE }}
TFC_AZURE_CLIENT_ID: ${{ secrets.TFC_AZURE_CLIENT_ID}}
TFC_AZURE_CLIENT_SECRET: ${{ secrets.TFC_AZURE_CLIENT_SECRET }}
TFC_AZURE_TENANT_ID: ${{ secrets.TFC_AZURE_TENANT_ID}}
TFC_AZURE_SUBSCRIPTION_ID: ${{ secrets.TFC_AZURE_SUBSCRIPTION_ID }}
TFC_VAULT_URL: ${{ secrets.TFC_VAULT_URL}}
-
+
SCALEWAY_API_URL: ${{ secrets.SCALEWAY_API_URL }}
SCALEWAY_REGION: ${{ secrets.SCALEWAY_REGION }}
SCALEWAY_PROJECT_ID: ${{ secrets.SCALEWAY_PROJECT_ID }}
@@ -46,6 +52,10 @@ env:
DELINEA_TENANT: ${{ secrets.DELINEA_TENANT }}
DELINEA_CLIENT_ID: ${{ secrets.DELINEA_CLIENT_ID }}
DELINEA_CLIENT_SECRET: ${{ secrets.DELINEA_CLIENT_SECRET }}
+
+ SECRETSERVER_USERNAME: ${{ secrets.SECRETSERVER_USERNAME }}
+ SECRETSERVER_PASSWORD: ${{ secrets.SECRETSERVER_PASSWORD }}
+ SECRETSERVER_URL: ${{ secrets.SECRETSERVER_URL }}
jobs:
integration-trusted:
@@ -58,7 +68,7 @@ jobs:
steps:
- name: Branch based PR checkout
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Fetch History
run: git fetch --prune --unshallow
@@ -77,15 +87,20 @@ jobs:
# Check out merge commit
- name: Fork based /ok-to-test checkout
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
- ref: 'refs/pull/${{ github.event.client_payload.pull_request.number }}/merge'
+ ref: '${{ env.TARGET_SHA }}'
- name: Fetch History
run: git fetch --prune --unshallow
- - uses: ./.github/actions/e2e
-
+ - id: e2e
+ uses: ./.github/actions/e2e
+ - id: create_token
+ uses: tibdex/github-app-token@3beb63f4bd073e61482598c45c71c1019b59b73a # v2.1.0
+ with:
+ app_id: ${{ secrets.APP_ID }}
+ private_key: ${{ secrets.PRIVATE_KEY }}
# Update check run called "integration-fork"
- uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
id: update-check-run
@@ -119,3 +134,19 @@ jobs:
conclusion: process.env.conclusion
});
return result;
+ - name: Update on Succeess
+ if: always() && steps.e2e.conclusion == 'success'
+ uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
+ with:
+ token: ${{ steps.create_token.outputs.token }}
+ issue-number: ${{ github.event.client_payload.pull_request.number }}
+ body: |
+ [Bot] - :white_check_mark: [e2e for ${{ env.TARGET_SHA }} passed](https://github.com/external-secrets/external-secrets/actions/runs/${{ github.run_id }})
+ - name: Update on Failure
+ if: always() && steps.e2e.conclusion != 'success'
+ uses: peter-evans/create-or-update-comment@71345be0265236311c031f5c7866368bd1eff043 # v4.0.0
+ with:
+ token: ${{ steps.create_token.outputs.token }}
+ issue-number: ${{ github.event.client_payload.pull_request.number }}
+ body: |
+ [Bot] - :x: [e2e for ${{ env.TARGET_SHA }} failed](https://github.com/external-secrets/external-secrets/actions/runs/${{ github.run_id }})
diff --git a/.github/workflows/helm.yml b/.github/workflows/helm.yml
index 41a30e40416..2f468bd81f9 100644
--- a/.github/workflows/helm.yml
+++ b/.github/workflows/helm.yml
@@ -22,7 +22,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
@@ -34,9 +34,9 @@ jobs:
with:
version: v3.14.2 # remember to also update for the second job (release)
- - uses: actions/setup-python@82c7e631bb3cdc910f68e0081d67478d79c6982d # v5.1.0
+ - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
with:
- python-version: 3.7
+ python-version: 3.11
- name: Set up chart-testing
uses: helm/chart-testing-action@e6669bcd63d7cb57cb4380c33043eebe5d111992 # v2.6.1
@@ -56,7 +56,7 @@ jobs:
run: ct lint --config=.github/ci/ct.yaml
- name: Create kind cluster
- uses: helm/kind-action@99576bfa6ddf9a8e612d83b513da5a75875caced # v1.9.0
+ uses: helm/kind-action@a1b0e391336a6ee6713a0583f8c6240d70863de3 # v1.12.0
if: steps.list-changed.outputs.changed == 'true'
- name: Run chart-testing (install)
@@ -71,10 +71,15 @@ jobs:
permissions:
contents: write # for helm/chart-releaser-action to push chart release and create a release
packages: write # to push OCI chart package to GitHub Registry
+ id-token: write # gives the action the ability to mint the OIDC token necessary to request a Sigstore signing certificate
+ attestations: write # this permission is necessary to persist the attestation
runs-on: ubuntu-latest
+ if: |
+ github.ref == 'refs/heads/main' ||
+ startsWith(github.ref, 'refs/heads/release-')
steps:
- name: Checkout
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
@@ -88,24 +93,18 @@ jobs:
version: v3.4.2
- name: Generate chart
+ run: make helm.generate
+ - name: Import GPG key
run: |
- make helm.generate
- ## Temporarily removing - This is making the release break.
- # - name: Import GPG key
- # run: |
- # echo "${{ secrets.GPG_PRIVATE_KEY }}" | gpg --dearmor --output keyring.gpg
- # echo "${{ secrets.GPG_PASSPHRASE }}" > passphrase-file.txt
+ echo "${{ secrets.GPG_PRIVATE_KEY }}" | gpg --dearmor --output keyring.gpg
+ echo -n "${{ secrets.GPG_PASSPHRASE }}" > passphrase-file.txt
- name: Run chart-releaser
- uses: helm/chart-releaser-action@a917fd15b20e8b64b94d9158ad54cd6345335584 # v1.6.0
- if: |
- github.ref == 'refs/heads/main' ||
- startsWith(github.ref, 'refs/heads/release-')
+ uses: helm/chart-releaser-action@cae68fefc6b5f367a0275617c9f83181ba54714f # v1.7.0
env:
- ## Temporarily removing - This is making the release break
- # CR_KEY: external-secrets
- # CR_KEYRING: keyring.gpg
- # CR_PASSPHRASE_FILE: passphrase-file.txt
- # CR_SIGN: true
+ CR_KEY: external-secrets
+ CR_KEYRING: keyring.gpg
+ CR_PASSPHRASE_FILE: passphrase-file.txt
+ CR_SIGN: true
CR_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
CR_RELEASE_NAME_TEMPLATE: "helm-chart-{{ .Version }}"
with:
@@ -119,18 +118,47 @@ jobs:
version: v3.14.2 # remember to also update for the first job (lint-and-test)
- name: Login to GHCR
- uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
+ uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
+ - name: Install cosign
+ uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
+ with:
+ cosign-release: 'v2.4.1'
+
- name: Push chart to GHCR
+ id: push_chart
run: |
shopt -s nullglob
for pkg in .cr-release-packages/*.tgz; do
if [ -z "${pkg:-}" ]; then
break
fi
- helm push "${pkg}" "oci://ghcr.io/${GITHUB_REPOSITORY_OWNER}/charts"
+ chart_name=$(helm show chart "${pkg}" | yq .name)
+ # helm push fails when registry path contains Uppercase letters
+ chart_registry="ghcr.io/${GITHUB_REPOSITORY_OWNER}/charts"
+
+ helm_push_output=$(helm push "${pkg}" "oci://${chart_registry}" 2>&1)
+ digest=$(echo "$helm_push_output" | grep -o 'sha256:[a-z0-9]*')
+ echo "$helm_push_output"
+
+ artifact_digest_uri="${chart_registry}/${chart_name}@${digest}"
+ cosign sign --yes "$artifact_digest_uri"
+ cosign verify "$artifact_digest_uri" \
+ --certificate-identity-regexp "https://github.com/$GITHUB_REPOSITORY/*" \
+ --certificate-oidc-issuer https://token.actions.githubusercontent.com
+
+ echo "digest=${digest}" >> "$GITHUB_OUTPUT"
+ echo "chart_name=${chart_name}" >> "$GITHUB_OUTPUT"
+ echo "registry=${chart_registry}" >> "$GITHUB_OUTPUT"
done
+
+ - name: Generate provenance attestation and push to OCI registry
+ uses: actions/attest-build-provenance@7668571508540a607bdfd90a87a560489fe372eb # v2.1.0
+ with:
+ push-to-registry: true
+ subject-name: ${{ steps.push_chart.outputs.registry }}/${{ steps.push_chart.outputs.chart_name }}
+ subject-digest: ${{ steps.push_chart.outputs.digest }}
diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 2f98bcfa46a..deaeb3ae13f 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -50,23 +50,23 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
ref: ${{ inputs.ref }}
- name: Setup QEMU
- uses: docker/setup-qemu-action@68827325e0b33c7199eb31dd4e31fbe9023e06e3 # v3.0.0
+ uses: docker/setup-qemu-action@53851d14592bedcffcf25ea515637cff71ef929a # v3.3.0
with:
platforms: all
- name: Setup Docker Buildx
- uses: docker/setup-buildx-action@d70bba72b1f3fd22344832f00baa16ece964efeb # v3.3.0
+ uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 # v3.8.0
with:
version: 'v0.4.2'
install: true
- name: Setup Go
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
+ uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
id: setup-go
with:
go-version-file: "go.mod"
@@ -80,7 +80,7 @@ jobs:
run: git fetch --prune --unshallow
- name: Login to Docker
- uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
+ uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
if: env.IS_FORK == 'false'
with:
registry: ghcr.io
@@ -126,7 +126,7 @@ jobs:
run: make docker.build
- name: Run Trivy vulnerability scanner
- uses: aquasecurity/trivy-action@d710430a6722f083d3b36b8339ff66b32f22ee55 # master
+ uses: aquasecurity/trivy-action@18f2510ee396bbf400402947b394f2dd8c87dbb0 # master
with:
image-ref: ${{ inputs.image-name }}:${{ steps.container_info.outputs.image-tag }}
format: 'table'
@@ -140,7 +140,7 @@ jobs:
needs: build-publish
steps:
- name: Checkout
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
- name: Sign image
if: env.IS_FORK == 'false'
uses: ./.github/actions/sign
diff --git a/.github/workflows/rebuild-image.yml b/.github/workflows/rebuild-image.yml
index 045f96bd8cb..503952805d7 100644
--- a/.github/workflows/rebuild-image.yml
+++ b/.github/workflows/rebuild-image.yml
@@ -20,7 +20,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
ref: ${{ github.event.inputs.ref }}
@@ -42,18 +42,18 @@ jobs:
include:
- dockerfile: "Dockerfile"
build-args: "CGO_ENABLED=0"
- build-arch: "amd64 arm64"
- build-platform: "linux/amd64,linux/arm64"
+ build-arch: "amd64 arm64 ppc64le"
+ build-platform: "linux/amd64,linux/arm64,linux/ppc64le"
tag-suffix: "-${{ needs.checkout.outputs.timestamp }}" # distroless
- dockerfile: "Dockerfile.ubi"
build-args: "CGO_ENABLED=0"
- build-arch: "amd64 arm64"
- build-platform: "linux/amd64,linux/arm64"
+ build-arch: "amd64 arm64 ppc64le"
+ build-platform: "linux/amd64,linux/arm64,linux/ppc64le"
tag-suffix: "-ubi-${{ needs.checkout.outputs.timestamp }}" # ubi
- dockerfile: "Dockerfile.ubi"
build-args: "CGO_ENABLED=0 GOEXPERIMENT=boringcrypto" # fips
- build-arch: "amd64"
- build-platform: "linux/amd64"
+ build-arch: "amd64 ppc64le"
+ build-platform: "linux/amd64,linux/ppc64le"
tag-suffix: "-ubi-boringssl-${{ needs.checkout.outputs.timestamp }}"
with:
dockerfile: ${{ matrix.dockerfile }}
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 24d473e2120..10b77ba65ec 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -22,13 +22,13 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
ref: ${{ github.event.inputs.source_ref }}
- name: Create Release
- uses: softprops/action-gh-release@9d7c94cfd0a1f3ed45544c887983e9fa900f0564 # v2.0.4
+ uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1
with:
tag_name: ${{ github.event.inputs.version }}
target_commitish: ${{ github.event.inputs.source_ref }}
@@ -71,12 +71,12 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
- name: Setup Go
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
+ uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
id: setup-go
with:
go-version-file: "go.mod"
@@ -86,7 +86,7 @@ jobs:
run: go mod download
- name: Login to Docker
- uses: docker/login-action@e92390c5fb421da1463c202d546fed0ec5c39f20 # v3.1.0
+ uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 # v3.3.0
with:
registry: ghcr.io
username: ${{ secrets.GHCR_USERNAME }}
@@ -113,7 +113,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Update Release
- uses: softprops/action-gh-release@9d7c94cfd0a1f3ed45544c887983e9fa900f0564 # v2.0.4
+ uses: softprops/action-gh-release@c95fe1489396fe8a9eb87c0abf8aa5b2ef267fda # v2.2.1
with:
tag_name: ${{ github.event.inputs.version }}
files: |
diff --git a/.github/workflows/release_esoctl.yml b/.github/workflows/release_esoctl.yml
new file mode 100644
index 00000000000..910d23d7175
--- /dev/null
+++ b/.github/workflows/release_esoctl.yml
@@ -0,0 +1,72 @@
+name: Create Release for esoctl
+
+on:
+ workflow_dispatch:
+ inputs:
+ version:
+ description: 'version to release, e.g. v0.1.0-esoctl'
+ required: true
+ default: 'v0.1.0-esoctl'
+ source_ref:
+ description: 'source ref to publish from. E.g.: main or release-x.y'
+ required: true
+ default: 'main'
+
+jobs:
+ release:
+ name: Create Release for esoctl
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - name: Checkout
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
+ with:
+ fetch-depth: 0
+ ref: ${{ github.event.inputs.source_ref }}
+
+ - name: Setup Go
+ uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
+ id: setup-go
+ with:
+ go-version-file: "go.mod"
+
+ - name: Download Go modules
+ if: ${{ steps.setup-go.outputs.cache-hit != 'true' }}
+ run: go mod download
+
+ - name: Install Syft
+ uses: anchore/sbom-action/download-syft@f325610c9f50a54015d37c8d16cb3b0e2c8f4de0 # v0.18.0
+
+ - name: Import GPG key
+ id: import_gpg
+ uses: crazy-max/ghaction-import-gpg@cb9bde2e2525e640591a934b1fd28eef1dcaf5e5 # v6.2.0
+ with:
+ gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
+ passphrase: ${{ secrets.GPG_PASSPHRASE }}
+
+ - name: Check if Tag Exists
+ id: check_tag
+ run: |
+ if git rev-parse "${{ github.event.inputs.version }}" >/dev/null 2>&1; then
+ echo "Tag exists."
+ exit 1
+ fi
+
+ - name: Create Tag if Not Exists
+ if: success()
+ run: |
+ TAG="${{ github.event.inputs.version }}"
+ git tag $TAG
+ git push origin $TAG
+
+ - name: Run GoReleaser
+ uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0
+ with:
+ version: '~> v2'
+ args: release --clean
+ workdir: cmd/esoctl
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ GORELEASER_CURRENT_TAG: ${{ github.event.inputs.version }}
+ GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }}
diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml
index 63830b0a07b..26f7b41da18 100644
--- a/.github/workflows/scorecard.yml
+++ b/.github/workflows/scorecard.yml
@@ -20,12 +20,12 @@ jobs:
steps:
- name: "Checkout code"
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
persist-credentials: false
- name: "Run analysis"
- uses: ossf/scorecard-action@0864cf19026789058feabb7e87baa5f140aac736 # v2.3.1
+ uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
with:
results_file: results.sarif
results_format: sarif
@@ -33,6 +33,6 @@ jobs:
# Upload the results to GitHub's code scanning dashboard.
- name: "Upload to code-scanning"
- uses: github/codeql-action/upload-sarif@c7f9125735019aa87cfc361530512d50ea439c71 # v3.25.1
+ uses: github/codeql-action/upload-sarif@b6a472f63d85b9c78a3ac5e89422239fc15e9b3c # v3.28.1
with:
sarif_file: results.sarif
diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml
index 5d1d3650f35..cdb4a3aa754 100644
--- a/.github/workflows/stale.yml
+++ b/.github/workflows/stale.yml
@@ -13,7 +13,7 @@ jobs:
pull-requests: write # for actions/stale to close stale PRs
runs-on: ubuntu-latest
steps:
- - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
+ - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'This issue is stale because it has been open 90 days with no activity. Remove stale label or comment or this will be closed in 30 days.'
diff --git a/.github/workflows/update-deps.yml b/.github/workflows/update-deps.yml
index 12f4f48311d..63dd5067bd4 100644
--- a/.github/workflows/update-deps.yml
+++ b/.github/workflows/update-deps.yml
@@ -20,7 +20,7 @@ jobs:
steps:
- name: Checkout
- uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+ uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
fetch-depth: 0
ref: ${{ github.event.inputs.ref }}
@@ -40,7 +40,7 @@ jobs:
branch: ${{ fromJson(needs.branches.outputs.branches) }}
steps:
- name: Setup Go
- uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0
+ uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0
with:
go-version: "1.21"
@@ -52,7 +52,7 @@ jobs:
with:
app_id: ${{ secrets.APP_ID }}
private_key: ${{ secrets.PRIVATE_KEY }}
- - uses: actions/checkout@1d96c772d19495a3b5c517cd2bc0cb401ea0529f # v4.1.3
+ - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
with:
token: ${{ steps.generate_token.outputs.token }}
ref: ${{ matrix.branch }}
diff --git a/.gitignore b/.gitignore
index 71d041a64ee..c8fc6a371cf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,8 @@
/webhook/bin
/webhook/certcontroller/bin
/bin
+/cmd/esoctl/bin
+/cmd/esoctl/dist
/vendor
cover.out
diff --git a/.golangci.yaml b/.golangci.yaml
index c1af2d9175a..ab37d7b863e 100644
--- a/.golangci.yaml
+++ b/.golangci.yaml
@@ -56,7 +56,7 @@ linters:
- errcheck
- errorlint
- exhaustive
- - exportloopref
+ - copyloopvar
- gci
- goheader
- goconst
diff --git a/.sonarcloud.properties b/.sonarcloud.properties
new file mode 100644
index 00000000000..cfb2c0e9166
--- /dev/null
+++ b/.sonarcloud.properties
@@ -0,0 +1,17 @@
+sonar.organization=external-secrets
+sonar.projectKey=external-secrets_external-secrets
+
+# Path to sources
+sonar.sources=.
+sonar.exclusions=**/*_test.go, **/zz_generated.deepcopy.go, e2e/**
+
+# Path to tests
+sonar.tests=.
+sonar.test.inclusions=**/*_test.go, e2e/**
+
+# Issues to ignore
+sonar.issue.ignore.multicriteria=g1
+
+# Ignore "Define a constant instead of duplicating this literal" in tests
+sonar.issue.ignore.multicriteria.g1.ruleKey=go:S1192
+sonar.issue.ignore.multicriteria.g1.resourceKey=**/*_test.go, e2e/**
diff --git a/ADOPTERS.md b/ADOPTERS.md
index 46ce154bbd1..9ca077878f8 100644
--- a/ADOPTERS.md
+++ b/ADOPTERS.md
@@ -7,10 +7,13 @@
- [Container Solutions](http://container-solutions.com/)
- [DaangnPay](https://www.daangnpay.com/)
- [Epidemic Sound](https://www.epidemicsound.com/)
+- [Elastic](https://www.elastic.co/)
- [Fivetran](https://www.fivetran.com)
- [Form3](https://www.form3.tech/)
- [GoTo](https://www.goto.com/)
+- [Grafana Labs](https://grafana.com/)
- [Heureka Group](https://heureka.group)
+- [Hostinger](https://www.hostinger.com/)
- [K8S Website Infra](https://k8s.io/)
- [Mercedes-Benz Tech Innovation](https://www.mercedes-benz-techinnovation.com/)
- [Mixpanel](https://mixpanel.com)
diff --git a/DEPRECATING.md b/DEPRECATING.md
new file mode 100644
index 00000000000..8bc824b0568
--- /dev/null
+++ b/DEPRECATING.md
@@ -0,0 +1,108 @@
+# External Secrets Operator Deprecation Policy
+
+This document defines the Deprecation Policy for External Secrets Operator components.
+
+## Overview
+
+**External Secrets Operator** is a Kubernetes operator that integrates external
+secret management systems like [AWS Secrets
+Manager](https://aws.amazon.com/secrets-manager/), [HashiCorp
+Vault](https://www.vaultproject.io/), [Google Secrets
+Manager](https://cloud.google.com/secret-manager), [Azure Key
+Vault](https://azure.microsoft.com/en-us/services/key-vault/), [CyberArk Conjur](https://www.conjur.org) and many more. The
+operator reads information from external APIs and automatically injects the
+values into a [Kubernetes
+Secret](https://kubernetes.io/docs/concepts/configuration/secret/).
+
+## Deprecation Policy
+
+We follow the [Kubernetes Deprecation Policy](https://kubernetes.io/docs/reference/using-api/deprecation-policy/) and [API Versioning Scheme](https://kubernetes.io/docs/reference/using-api/#api-versioning): alpha, beta, GA.
+
+The project is currently in `beta` state. Please try the `beta` features and provide feedback. After the features exits beta, it may not be practical to make more changes.
+
+* alpha
+ * The support for a feature may be dropped at any time without notice.
+ * The API may change in incompatible ways in a later software release without notice.
+ * The software is recommended for use only in short-lived testing clusters, due to increased risk of bugs and lack of long-term support.
+
+* beta
+ * The software is well tested. Enabling a feature is considered safe. Features are enabled by default.
+ * The support for a feature will not be dropped, though the details may change.
+ * The schema and/or semantics of objects may change in incompatible ways in a subsequent beta or stable release. When this happens, migration instructions are provided. Schema changes may require deleting, editing, and re-creating API objects. The editing process may not be straightforward. The migration may require downtime for applications that rely on the feature.
+ * The software is not recommended for production uses. Subsequent releases may introduce incompatible changes. If you have multiple clusters which can be upgraded independently, you may be able to relax this restriction.
+* GA
+ * The stable versions of features appear in released software for many subsequent versions.
+ * Use it in production ;)
+
+## API Surface
+
+We define the following scope that is covered by our deprecation policy. We follow the [9 Rules of the Kubernetes Deprecation Policy](https://kubernetes.io/docs/reference/using-api/deprecation-policy/).
+
+### Scope
+* API Objects and fields: `.Spec`, `.Status` and `.Status.Conditions[]`
+* Enums and constant values
+* Controller Configuration: CLI flags & environment variables
+* Metrics as defined in the [Kubernetes docs](https://kubernetes.io/docs/reference/using-api/deprecation-policy/#deprecating-a-metric)
+* The following features or specific behavior:
+ * `ExternalSecret` [update mechanics](http://localhost:8000/api-externalsecret/#update-behavior)
+
+### Non-Scope
+Everything not listed in scope is not subject to this deprecation policy and it is subject to breaking changes, updates at any point in time, and deprecation - **as long as it follows the Deprecation Process listed below**.
+
+This includes, but isn't limited to :
+* Any feature / specific behavior not in Scope.
+* Source code imports
+* Helm Charts
+* Release process
+* Docker Images (including multi-arch builds)
+* Image Signature (including provenance, providers, keys)
+* OLM-specific builds
+
+## Including features and behaviors to the Deprecation Policy
+Any maintainer may propose including a feature, component, or behavior out of scope to be in scope of the deprecation policy.
+
+The proposal must clearly outline the rationale for inclusion, the impact on users, stability, long term maintenance plan, and day-to-day activities, if such.
+
+The proposal must be formalized by submitting a `design` document as a Pull Request.
+
+## Deprecation Process
+### Nomination of Deprecation
+
+Any maintainer may propose deprecating a feature, component, or behavior (both in and out of scope). In Scope changes must abide to the Deprecation Policy above.
+
+The proposal must clearly outline the rationale for deprecation, the impact on users, and any alternatives, if such.
+
+The proposal must be formalized by submiting a `design` document as a Pull Request.
+
+### Showcase to Maintainers
+
+The proposing maintainer must present the proposed deprecation to the maintainer group. This can be done synchronously during a community meeting or asynchronously, through a GitHub Pull Request.
+
+### Voting
+
+A majority vote of maintainers is required to approve the deprecation.
+Votes may be conducted asynchronously, with a reasonable deadline for responses (e.g., one week). Lazy Consensus applies if the reasonable deadline is extended, with a minimal of at least one other maintainer approving the changes.
+
+### Implementation
+
+Upon approval, the proposing maintainer is responsible for implementing the changes required to mark the feature as deprecated. This includes:
+
+* Updating the codebase with deprecation warnings where applicable.
+* Documenting the deprecation in release notes and relevant documentation.
+* Updating APIs, metrics, or behaviors per the Kubernetes Deprecation Policy if in scope.
+* If the feature is entirely deprecated (e.g., OLM-specific builds), archival of any associated repositories.
+
+### Deprecation Notice in Release
+
+Deprecation must be introduced in the next release. The release must follow semantic versioning:
+* If the project is in the 0.x stage, a minor version bump is required.
+* For projects 1.x and beyond, a major version bump is required.
+
+The release notes must prominently include:
+* A deprecation notice for the feature.
+* The expected timeline for removal (if applicable).
+
+### Full Deprecation and Removal
+
+When a deprecated feature is removed, it must be communicated in the release notes of the removal version.
+The removal must follow standard Kubernetes deprecation timelines if the feature is in scope.
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index ac464f8c268..065f404e58e 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM gcr.io/distroless/static@sha256:6d31326376a7834b106f281b04f67b5d015c31732f594930f2ea81365f99d60c
+FROM gcr.io/distroless/static@sha256:3f2b64ef97bd285e36132c684e6b2ae8f2723293d09aae046196cca64251acac
ARG TARGETOS
ARG TARGETARCH
COPY bin/external-secrets-${TARGETOS}-${TARGETARCH} /bin/external-secrets
diff --git a/Dockerfile.standalone b/Dockerfile.standalone
index 52e62570905..446b34c2db3 100644
--- a/Dockerfile.standalone
+++ b/Dockerfile.standalone
@@ -1,6 +1,6 @@
# This version of Dockerfile is for building without external dependencies.
# Build a multi-platform image e.g. `docker buildx build --push --platform linux/arm64,linux/amd64 --tag external-secrets:dev --file Dockerfile.standalone .`
-FROM golang:1.22.2-alpine@sha256:cdc86d9f363e8786845bea2040312b4efa321b828acdeb26f393faa864d887b0 AS builder
+FROM golang:1.23.5-alpine@sha256:47d337594bd9e667d35514b241569f95fb6d95727c24b19468813d596d5ae596 AS builder
ARG TARGETOS
ARG TARGETARCH
ENV CGO_ENABLED=0 GOOS=${TARGETOS} GOARCH=${TARGETARCH}
@@ -12,7 +12,7 @@ COPY . /app/
RUN go build -o external-secrets main.go
-FROM gcr.io/distroless/static@sha256:6d31326376a7834b106f281b04f67b5d015c31732f594930f2ea81365f99d60c AS app
+FROM gcr.io/distroless/static@sha256:3f2b64ef97bd285e36132c684e6b2ae8f2723293d09aae046196cca64251acac AS app
COPY --from=builder /app/external-secrets /bin/external-secrets
# Run as UID for nobody
diff --git a/Dockerfile.ubi b/Dockerfile.ubi
index 4c225ffc143..069a9d2b3be 100644
--- a/Dockerfile.ubi
+++ b/Dockerfile.ubi
@@ -1,11 +1,32 @@
-FROM registry.access.redhat.com/ubi8/ubi-minimal@sha256:f30dbf77b075215f6c827c269c073b5e0973e5cea8dacdf7ecb6a19c868f37f2
+FROM registry.access.redhat.com/ubi8/ubi@sha256:2e863fb65e595839633113300a0c6709e9af81523a30001290068265c121a927 AS minimal-ubi
+
ARG TARGETOS
ARG TARGETARCH
-COPY bin/external-secrets-${TARGETOS}-${TARGETARCH} /bin/external-secrets
+RUN dnf update -y && dnf install -y binutils
+# prep target rootfs for scratch container
+WORKDIR /
+RUN mkdir /image && \
+ ln -s usr/bin /image/bin && \
+ ln -s usr/sbin /image/sbin && \
+ ln -s usr/lib64 /image/lib64 && \
+ ln -s usr/lib /image/lib && \
+ mkdir -p /image/{usr/bin,usr/lib64,usr/lib,root,home,proc,etc,sys,var,dev}
-RUN microdnf update
+COPY ubi-build-files-${TARGETARCH}.txt /tmp
+# Copy all the required files from the base UBI image into the image directory
+# As the go binary is not statically compiled this includes everything needed for CGO to work, cacerts, tzdata and RH release files
+RUN tar cf /tmp/files.tar -T /tmp/ubi-build-files-${TARGETARCH}.txt && tar xf /tmp/files.tar -C /image/ \
+ && strip --strip-unneeded /image/usr/lib64/*[0-9].so && rpm --root /image --initdb \
+ && PACKAGES=$(rpm -qf $(cat /tmp/ubi-build-files-${TARGETARCH}.txt) | grep -v "is not owned by any package" | sort -u) \
+ && echo dnf install -y 'dnf-command(download)' \
+ && dnf download --destdir / ${PACKAGES} \
+ && rpm --root /image -ivh --justdb --nodeps `for i in ${PACKAGES}; do echo $i.rpm; done`
-# Run as UID for nobody
+FROM scratch
+# Copy all required files + rpm database so the image is scannable
+COPY --from=minimal-ubi /image/ /
USER 65534
-
+ARG TARGETOS
+ARG TARGETARCH
+COPY bin/external-secrets-${TARGETOS}-${TARGETARCH} /bin/external-secrets
ENTRYPOINT ["/bin/external-secrets"]
diff --git a/MAINTAINERS.md b/MAINTAINERS.md
index a9ee31f7d2e..c4c579e572c 100644
--- a/MAINTAINERS.md
+++ b/MAINTAINERS.md
@@ -5,14 +5,13 @@ describes governance guidelines and maintainer responsibilities.
## Maintainers
-| Maintainer | GitHub ID | Affiliation |
-| --------------- | --------- | ----------- |
-| RealName Here | [knelasevero](https://github.com/knelasevero) | [Company](https://www.github.com/Company/) |
+| Maintainer | GitHub ID | Affiliation |
+| --------------- |-------------------------------------------------| ----------- |
+| RealName Here | [knelasevero](https://github.com/knelasevero) | [Company](https://www.github.com/Company/) |
| RealName Here | [gusfcarvalho](https://github.com/gusfcarvalho) | [Company](https://www.github.com/Company/) |
-| RealName Here | [moolen](https://github.com/moolen) | [Company](https://www.github.com/Company/) |
-| RealName Here | [sebagomez](https://github.com/sebagomez) | [Company](https://www.github.com/Company/) |
-| RealName Here | [rodrmartinez](https://github.com/rodrmartinez) | [Company](https://www.github.com/Company/) |
-| RealName Here | [IdanAdar](https://github.com/IdanAdar) | [Company](https://www.github.com/Company/) |
+| RealName Here | [moolen](https://github.com/moolen) | [Company](https://www.github.com/Company/) |
+| RealName Here | [IdanAdar](https://github.com/IdanAdar) | [Company](https://www.github.com/Company/) |
+| RealName Here | [Skarlso](https://github.com/Skarlso) | [Company](https://www.github.com/Company/) |
## External Secrets Operator Core Contributors & Stakeholders
@@ -41,3 +40,5 @@ describes governance guidelines and maintainer responsibilities.
* RealName Here, [mcavoyk](https://github.com/mcavoyk)
* RealName Here, [riccardomc](https://github.com/riccardomc)
* RealName Here, [jonatasbaldin](https://github.com/jonatasbaldin)
+* RealName Here, [sebagomez](https://github.com/sebagomez)
+* RealName Here, [rodrmartinez](https://github.com/rodrmartinez)
diff --git a/Makefile b/Makefile
index 0c60f5bdc3c..6e019d7e0b6 100644
--- a/Makefile
+++ b/Makefile
@@ -5,7 +5,7 @@ SHELL := /bin/bash
MAKEFLAGS += --warn-undefined-variables
.SHELLFLAGS := -euo pipefail -c
-ARCH ?= amd64 arm64
+ARCH ?= amd64 arm64 ppc64le
BUILD_ARGS ?= CGO_ENABLED=0
DOCKER_BUILD_ARGS ?=
DOCKERFILE ?= Dockerfile
@@ -72,7 +72,7 @@ FAIL = (echo ${TIME} ${RED}[FAIL]${CNone} && false)
# ====================================================================================
# Conformance
-reviewable: generate docs manifests helm.generate helm.docs lint ## Ensure a PR is ready for review.
+reviewable: generate docs manifests helm.generate helm.schema.update helm.docs lint ## Ensure a PR is ready for review.
@go mod tidy
@cd e2e/ && go mod tidy
@@ -146,6 +146,7 @@ run: generate ## Run app locally (without a k8s cluster)
manifests: helm.generate ## Generate manifests from helm chart
mkdir -p $(OUTPUT_DIR)/deploy/manifests
+ helm dependency build $(HELM_DIR)
helm template external-secrets $(HELM_DIR) -f deploy/manifests/helm-values.yaml > $(OUTPUT_DIR)/deploy/manifests/external-secrets.yaml
crds.install: generate ## Install CRDs into a cluster. This is for convenience
@@ -162,7 +163,7 @@ tilt-up: tilt manifests ## Generates the local manifests that tilt will use to d
helm.docs: ## Generate helm docs
@cd $(HELM_DIR); \
- docker run --rm -v $(shell pwd)/$(HELM_DIR):/helm-docs -u $(shell id -u) jnorwood/helm-docs:v1.5.0
+ docker run --rm -v $(shell pwd)/$(HELM_DIR):/helm-docs -u $(shell id -u) jnorwood/helm-docs:v1.7.0
HELM_VERSION ?= $(shell helm show chart $(HELM_DIR) | grep 'version:' | sed 's/version: //g')
@@ -172,6 +173,16 @@ helm.build: helm.generate ## Build helm chart
@mv $(OUTPUT_DIR)/chart/external-secrets-$(HELM_VERSION).tgz $(OUTPUT_DIR)/chart/external-secrets.tgz
@$(OK) helm package
+helm.schema.plugin:
+ @$(INFO) Installing helm-values-schema-json plugin
+ @helm plugin install https://github.com/losisin/helm-values-schema-json.git || true
+ @$(OK) Installed helm-values-schema-json plugin
+
+helm.schema.update: helm.schema.plugin
+ @$(INFO) Generating values.schema.json
+ @helm schema -input $(HELM_DIR)/values.yaml -output $(HELM_DIR)/values.schema.json
+ @$(OK) Generated values.schema.json
+
helm.generate:
./hack/helm.generate.sh $(BUNDLE_DIR) $(HELM_DIR)
@$(OK) Finished generating helm chart files
@@ -257,22 +268,22 @@ docker.promote: ## Promote the docker image to the registry
# ====================================================================================
# Terraform
-tf.plan.%: ## Runs terrform plan for a provider
+tf.plan.%: ## Runs terraform plan for a provider
@cd $(TF_DIR)/$*; \
terraform init; \
terraform plan
-tf.apply.%: ## Runs terrform apply for a provider
+tf.apply.%: ## Runs terraform apply for a provider
@cd $(TF_DIR)/$*; \
terraform init; \
terraform apply -auto-approve
-tf.destroy.%: ## Runs terrform destroy for a provider
+tf.destroy.%: ## Runs terraform destroy for a provider
@cd $(TF_DIR)/$*; \
terraform init; \
terraform destroy -auto-approve
-tf.show.%: ## Runs terrform show for a provider and outputs to a file
+tf.show.%: ## Runs terraform show for a provider and outputs to a file
@cd $(TF_DIR)/$*; \
terraform init; \
terraform plan -out tfplan.binary; \
@@ -321,9 +332,9 @@ ENVTEST ?= $(LOCALBIN)/setup-envtest
GOLANGCI_LINT ?= $(LOCALBIN)/golangci-lint
## Tool Versions
-GOLANGCI_VERSION := 1.57.2
+GOLANGCI_VERSION := 1.61.0
KUBERNETES_VERSION := 1.30.x
-TILT_VERSION := 0.33.10
+TILT_VERSION := 0.33.21
.PHONY: envtest
envtest: $(ENVTEST) ## Download envtest-setup locally if necessary.
diff --git a/README.md b/README.md
index cca2f17d282..a32d224c7e7 100644
--- a/README.md
+++ b/README.md
@@ -64,9 +64,10 @@ You can find the roadmap in our documentation: https://external-secrets.io/main/
## Sponsored by
-![](assets/CS_logo_1.png)
-![](assets/form3_logo.png)
-![](assets/pento_logo.png)
+![External Secrets Inc.](assets/ESI_Logo.svg)
+![Container Solutions](assets/CS_logo_1.png)
+![Form 3](assets/form3_logo.png)
+![Pento ](assets/pento_logo.png)
## License
diff --git a/Tiltfile b/Tiltfile
index d123330817c..1f6f491bc64 100644
--- a/Tiltfile
+++ b/Tiltfile
@@ -80,7 +80,7 @@ if settings.get('debug').get('enabled'):
docker_build_with_restart(
- 'ghcr.io/external-secrets/external-secrets',
+ 'oci.external-secrets.io/external-secrets/external-secrets',
'.',
dockerfile = dockerfile,
entrypoint = entrypoint,
diff --git a/apis/externalsecrets/v1alpha1/externalsecret_conversion_test.go b/apis/externalsecrets/v1alpha1/externalsecret_conversion_test.go
index 67af85f99e8..4dccc21ba11 100644
--- a/apis/externalsecrets/v1alpha1/externalsecret_conversion_test.go
+++ b/apis/externalsecrets/v1alpha1/externalsecret_conversion_test.go
@@ -25,7 +25,8 @@ import (
)
const (
- keyName = "my-key"
+ keyName = "my-key"
+ testTarget = "test-target"
)
func newExternalSecretV1Alpha1() *ExternalSecret {
@@ -45,7 +46,7 @@ func newExternalSecretV1Alpha1() *ExternalSecret {
},
},
Binding: corev1.LocalObjectReference{
- Name: "test-target",
+ Name: testTarget,
},
},
Spec: ExternalSecretSpec{
@@ -54,7 +55,7 @@ func newExternalSecretV1Alpha1() *ExternalSecret {
Kind: "ClusterSecretStore",
},
Target: ExternalSecretTarget{
- Name: "test-target",
+ Name: testTarget,
CreationPolicy: Owner,
Immutable: false,
Template: &ExternalSecretTemplate{
@@ -130,7 +131,7 @@ func newExternalSecretV1Beta1() *esv1beta1.ExternalSecret {
},
},
Binding: corev1.LocalObjectReference{
- Name: "test-target",
+ Name: testTarget,
},
},
Spec: esv1beta1.ExternalSecretSpec{
@@ -139,7 +140,7 @@ func newExternalSecretV1Beta1() *esv1beta1.ExternalSecret {
Kind: "ClusterSecretStore",
},
Target: esv1beta1.ExternalSecretTarget{
- Name: "test-target",
+ Name: testTarget,
CreationPolicy: esv1beta1.CreatePolicyOwner,
Immutable: false,
Template: &esv1beta1.ExternalSecretTemplate{
diff --git a/apis/externalsecrets/v1alpha1/externalsecret_types.go b/apis/externalsecrets/v1alpha1/externalsecret_types.go
index b277741d1d8..c1795ee922a 100644
--- a/apis/externalsecrets/v1alpha1/externalsecret_types.go
+++ b/apis/externalsecrets/v1alpha1/externalsecret_types.go
@@ -22,11 +22,15 @@ import (
// SecretStoreRef defines which SecretStore to fetch the ExternalSecret data.
type SecretStoreRef struct {
// Name of the SecretStore resource
- Name string `json:"name"`
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ Name string `json:"name,omitempty"`
// Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
// Defaults to `SecretStore`
// +optional
+ // +kubebuilder:validation:Enum=SecretStore;ClusterSecretStore
Kind string `json:"kind,omitempty"`
}
@@ -92,25 +96,37 @@ type TemplateFrom struct {
}
type TemplateRef struct {
- Name string `json:"name"`
+ // The name of the ConfigMap/Secret resource
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ Name string `json:"name"`
+
+ // A list of keys in the ConfigMap/Secret to use as templates for Secret data
Items []TemplateRefItem `json:"items"`
}
type TemplateRefItem struct {
+ // A key in the ConfigMap/Secret
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[-._a-zA-Z0-9]+$
Key string `json:"key"`
}
// ExternalSecretTarget defines the Kubernetes Secret to be created
// There can be only one target per ExternalSecret.
type ExternalSecretTarget struct {
- // Name defines the name of the Secret resource to be managed
- // This field is immutable
+ // The name of the Secret resource to be managed.
// Defaults to the .metadata.name of the ExternalSecret resource
// +optional
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
Name string `json:"name,omitempty"`
- // CreationPolicy defines rules on how to create the resulting Secret
- // Defaults to 'Owner'
+ // CreationPolicy defines rules on how to create the resulting Secret.
+ // Defaults to "Owner"
// +optional
// +kubebuilder:default="Owner"
CreationPolicy ExternalSecretCreationPolicy `json:"creationPolicy,omitempty"`
@@ -126,6 +142,10 @@ type ExternalSecretTarget struct {
// ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data.
type ExternalSecretData struct {
+ // The key in the Kubernetes Secret to store the value.
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[-._a-zA-Z0-9]+$
SecretKey string `json:"secretKey"`
RemoteRef ExternalSecretDataRemoteRef `json:"remoteRef"`
@@ -140,11 +160,12 @@ type ExternalSecretDataRemoteRef struct {
// +optional
Version string `json:"version,omitempty"`
- // +optional
// Used to select a specific property of the Provider value (if a map), if supported
- Property string `json:"property,omitempty"`
// +optional
+ Property string `json:"property,omitempty"`
+
// Used to define a conversion Strategy
+ // +optional
// +kubebuilder:default="Default"
ConversionStrategy ExternalSecretConversionStrategy `json:"conversionStrategy,omitempty"`
}
@@ -235,7 +256,8 @@ type ExternalSecretStatus struct {
// ExternalSecret is the Schema for the external-secrets API.
// +kubebuilder:subresource:status
// +kubebuilder:deprecatedversion
-// +kubebuilder:resource:scope=Namespaced,categories={externalsecrets},shortName=es
+// +kubebuilder:resource:scope=Namespaced,categories={external-secrets},shortName=es
+// +kubebuilder:printcolumn:name="Store",type=string,JSONPath=`.spec.secretStoreRef.kind`
// +kubebuilder:printcolumn:name="Store",type=string,JSONPath=`.spec.secretStoreRef.name`
// +kubebuilder:printcolumn:name="Refresh Interval",type=string,JSONPath=`.spec.refreshInterval`
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`
diff --git a/apis/externalsecrets/v1alpha1/pushsecret_types.go b/apis/externalsecrets/v1alpha1/pushsecret_types.go
index 3fc221620d5..c30848aaa0d 100644
--- a/apis/externalsecrets/v1alpha1/pushsecret_types.go
+++ b/apis/externalsecrets/v1alpha1/pushsecret_types.go
@@ -30,14 +30,19 @@ const (
type PushSecretStoreRef struct {
// Optionally, sync to the SecretStore of the given name
// +optional
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
Name string `json:"name,omitempty"`
+
// Optionally, sync to secret stores with label selector
// +optional
LabelSelector *metav1.LabelSelector `json:"labelSelector,omitempty"`
+
// Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
- // Defaults to `SecretStore`
- // +kubebuilder:default="SecretStore"
// +optional
+ // +kubebuilder:default="SecretStore"
+ // +kubebuilder:validation:Enum=SecretStore;ClusterSecretStore
Kind string `json:"kind,omitempty"`
}
@@ -68,33 +73,50 @@ const (
// PushSecretSpec configures the behavior of the PushSecret.
type PushSecretSpec struct {
// The Interval to which External Secrets will try to push a secret definition
- RefreshInterval *metav1.Duration `json:"refreshInterval,omitempty"`
+ RefreshInterval *metav1.Duration `json:"refreshInterval,omitempty"`
+
SecretStoreRefs []PushSecretStoreRef `json:"secretStoreRefs"`
- // UpdatePolicy to handle Secrets in the provider. Possible Values: "Replace/IfNotExists". Defaults to "Replace".
+
+ // UpdatePolicy to handle Secrets in the provider.
// +kubebuilder:default="Replace"
// +optional
UpdatePolicy PushSecretUpdatePolicy `json:"updatePolicy,omitempty"`
- // Deletion Policy to handle Secrets in the provider. Possible Values: "Delete/None". Defaults to "None".
+
+ // Deletion Policy to handle Secrets in the provider.
// +kubebuilder:default="None"
// +optional
DeletionPolicy PushSecretDeletionPolicy `json:"deletionPolicy,omitempty"`
+
// The Secret Selector (k8s source) for the Push Secret
Selector PushSecretSelector `json:"selector"`
+
// Secret Data that should be pushed to providers
Data []PushSecretData `json:"data,omitempty"`
+
// Template defines a blueprint for the created Secret resource.
// +optional
Template *esv1beta1.ExternalSecretTemplate `json:"template,omitempty"`
}
type PushSecretSecret struct {
- // Name of the Secret. The Secret must exist in the same namespace as the PushSecret manifest.
+ // Name of the Secret.
+ // The Secret must exist in the same namespace as the PushSecret manifest.
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
Name string `json:"name"`
}
+// +kubebuilder:validation:MinProperties=1
+// +kubebuilder:validation:MaxProperties=1
type PushSecretSelector struct {
// Select a Secret to Push.
- Secret PushSecretSecret `json:"secret"`
+ // +optional
+ Secret *PushSecretSecret `json:"secret,omitempty"`
+
+ // Point to a generator to create a Secret.
+ // +optional
+ GeneratorRef *esv1beta1.GeneratorRef `json:"generatorRef,omitempty"`
}
type PushSecretRemoteRef struct {
@@ -198,7 +220,8 @@ type PushSecretStatus struct {
// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp"
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`
// +kubebuilder:subresource:status
-// +kubebuilder:resource:scope=Namespaced,categories={pushsecrets}
+// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
+// +kubebuilder:resource:scope=Namespaced,categories={external-secrets}
type PushSecret struct {
metav1.TypeMeta `json:",inline"`
diff --git a/apis/externalsecrets/v1alpha1/secretstore_kubernetes_types.go b/apis/externalsecrets/v1alpha1/secretstore_kubernetes_types.go
index d9738909645..2bbe7f5c901 100644
--- a/apis/externalsecrets/v1alpha1/secretstore_kubernetes_types.go
+++ b/apis/externalsecrets/v1alpha1/secretstore_kubernetes_types.go
@@ -50,8 +50,11 @@ type KubernetesProvider struct {
Auth KubernetesAuth `json:"auth"`
// Remote namespace to fetch the secrets from
- // +kubebuilder:default= default
// +optional
+ // +kubebuilder:default=default
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=63
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
RemoteNamespace string `json:"remoteNamespace,omitempty"`
}
diff --git a/apis/externalsecrets/v1alpha1/secretstore_types.go b/apis/externalsecrets/v1alpha1/secretstore_types.go
index d539449445e..c04f2ea253e 100644
--- a/apis/externalsecrets/v1alpha1/secretstore_types.go
+++ b/apis/externalsecrets/v1alpha1/secretstore_types.go
@@ -136,7 +136,7 @@ type SecretStoreStatus struct {
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`
// +kubebuilder:subresource:status
// +kubebuilder:deprecatedversion
-// +kubebuilder:resource:scope=Namespaced,categories={externalsecrets},shortName=ss
+// +kubebuilder:resource:scope=Namespaced,categories={external-secrets},shortName=ss
type SecretStore struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
@@ -161,7 +161,7 @@ type SecretStoreList struct {
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`
// +kubebuilder:deprecatedversion
// +kubebuilder:subresource:status
-// +kubebuilder:resource:scope=Cluster,categories={externalsecrets},shortName=css
+// +kubebuilder:resource:scope=Cluster,categories={external-secrets},shortName=css
type ClusterSecretStore struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
diff --git a/apis/externalsecrets/v1alpha1/secretstore_vault_types.go b/apis/externalsecrets/v1alpha1/secretstore_vault_types.go
index 934359b4750..88b4c56d781 100644
--- a/apis/externalsecrets/v1alpha1/secretstore_vault_types.go
+++ b/apis/externalsecrets/v1alpha1/secretstore_vault_types.go
@@ -39,14 +39,23 @@ type CAProvider struct {
Type CAProviderType `json:"type"`
// The name of the object located at the provider type.
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
Name string `json:"name"`
- // The key the value inside of the provider type to use, only used with "Secret" type
+ // The key where the CA certificate can be found in the Secret or ConfigMap.
// +kubebuilder:validation:Optional
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[-._a-zA-Z0-9]+$
Key string `json:"key,omitempty"`
// The namespace the Provider type is in.
// +optional
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=63
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
Namespace *string `json:"namespace,omitempty"`
}
diff --git a/apis/externalsecrets/v1alpha1/secretstore_webhook_types.go b/apis/externalsecrets/v1alpha1/secretstore_webhook_types.go
index 2c5e6150ce1..bf4e2b8b12f 100644
--- a/apis/externalsecrets/v1alpha1/secretstore_webhook_types.go
+++ b/apis/externalsecrets/v1alpha1/secretstore_webhook_types.go
@@ -75,14 +75,23 @@ type WebhookCAProvider struct {
Type WebhookCAProviderType `json:"type"`
// The name of the object located at the provider type.
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
Name string `json:"name"`
- // The key the value inside of the provider type to use, only used with "Secret" type
+ // The key where the CA certificate can be found in the Secret or ConfigMap.
// +kubebuilder:validation:Optional
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[-._a-zA-Z0-9]+$
Key string `json:"key,omitempty"`
// The namespace the Provider type is in.
// +optional
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=63
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
Namespace *string `json:"namespace,omitempty"`
}
diff --git a/apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go b/apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go
index d7f0e66d4b9..da4595be36e 100644
--- a/apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go
+++ b/apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go
@@ -1208,7 +1208,16 @@ func (in *PushSecretSecret) DeepCopy() *PushSecretSecret {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PushSecretSelector) DeepCopyInto(out *PushSecretSelector) {
*out = *in
- out.Secret = in.Secret
+ if in.Secret != nil {
+ in, out := &in.Secret, &out.Secret
+ *out = new(PushSecretSecret)
+ **out = **in
+ }
+ if in.GeneratorRef != nil {
+ in, out := &in.GeneratorRef, &out.GeneratorRef
+ *out = new(v1beta1.GeneratorRef)
+ **out = **in
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PushSecretSelector.
@@ -1236,7 +1245,7 @@ func (in *PushSecretSpec) DeepCopyInto(out *PushSecretSpec) {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
- out.Selector = in.Selector
+ in.Selector.DeepCopyInto(&out.Selector)
if in.Data != nil {
in, out := &in.Data, &out.Data
*out = make([]PushSecretData, len(*in))
diff --git a/apis/externalsecrets/v1beta1/clusterexternalsecret_types.go b/apis/externalsecrets/v1beta1/clusterexternalsecret_types.go
index 70e230ba3c6..68c2a9a2b62 100644
--- a/apis/externalsecrets/v1beta1/clusterexternalsecret_types.go
+++ b/apis/externalsecrets/v1beta1/clusterexternalsecret_types.go
@@ -24,8 +24,12 @@ type ClusterExternalSecretSpec struct {
// The spec for the ExternalSecrets to be created
ExternalSecretSpec ExternalSecretSpec `json:"externalSecretSpec"`
- // The name of the external secrets to be created defaults to the name of the ClusterExternalSecret
+ // The name of the external secrets to be created.
+ // Defaults to the name of the ClusterExternalSecret
// +optional
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
ExternalSecretName string `json:"externalSecretName,omitempty"`
// The metadata of the external secrets to be created
@@ -43,6 +47,9 @@ type ClusterExternalSecretSpec struct {
// Choose namespaces by name. This field is ORed with anything that NamespaceSelectors ends up choosing.
// +optional
+ // +kubebuilder:validation:items:MinLength:=1
+ // +kubebuilder:validation:items:MaxLength:=63
+ // +kubebuilder:validation:items:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
Namespaces []string `json:"namespaces,omitempty"`
// The time in which the controller should reconcile its objects and recheck namespaces for labels.
@@ -100,8 +107,9 @@ type ClusterExternalSecretStatus struct {
// +kubebuilder:object:root=true
// +kubebuilder:storageversion
-// +kubebuilder:resource:scope=Cluster,categories={externalsecrets},shortName=ces
+// +kubebuilder:resource:scope=Cluster,categories={external-secrets},shortName=ces
// +kubebuilder:subresource:status
+// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
// +kubebuilder:printcolumn:name="Store",type=string,JSONPath=`.spec.externalSecretSpec.secretStoreRef.name`
// +kubebuilder:printcolumn:name="Refresh Interval",type=string,JSONPath=`.spec.refreshTime`
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
diff --git a/apis/externalsecrets/v1beta1/externalsecret_types.go b/apis/externalsecrets/v1beta1/externalsecret_types.go
index b876cfcb64f..b34c43e6068 100644
--- a/apis/externalsecrets/v1beta1/externalsecret_types.go
+++ b/apis/externalsecrets/v1beta1/externalsecret_types.go
@@ -22,11 +22,15 @@ import (
// SecretStoreRef defines which SecretStore to fetch the ExternalSecret data.
type SecretStoreRef struct {
// Name of the SecretStore resource
- Name string `json:"name"`
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ Name string `json:"name,omitempty"`
// Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
// Defaults to `SecretStore`
// +optional
+ // +kubebuilder:validation:Enum=SecretStore;ClusterSecretStore
Kind string `json:"kind,omitempty"`
}
@@ -92,12 +96,16 @@ type ExternalSecretTemplate struct {
// template specified in .data and .templateFrom[].
// +kubebuilder:default="v2"
EngineVersion TemplateEngineVersion `json:"engineVersion,omitempty"`
+
// +optional
Metadata ExternalSecretTemplateMetadata `json:"metadata,omitempty"`
+
// +kubebuilder:default="Replace"
MergePolicy TemplateMergePolicy `json:"mergePolicy,omitempty"`
+
// +optional
Data map[string]string `json:"data,omitempty"`
+
// +optional
TemplateFrom []TemplateFrom `json:"templateFrom,omitempty"`
}
@@ -121,10 +129,11 @@ const (
type TemplateFrom struct {
ConfigMap *TemplateRef `json:"configMap,omitempty"`
Secret *TemplateRef `json:"secret,omitempty"`
- // +optional
+
// +optional
// +kubebuilder:default="Data"
Target TemplateTarget `json:"target,omitempty"`
+
// +optional
Literal *string `json:"literal,omitempty"`
}
@@ -147,12 +156,23 @@ const (
)
type TemplateRef struct {
- Name string `json:"name"`
+ // The name of the ConfigMap/Secret resource
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ Name string `json:"name"`
+
+ // A list of keys in the ConfigMap/Secret to use as templates for Secret data
Items []TemplateRefItem `json:"items"`
}
type TemplateRefItem struct {
+ // A key in the ConfigMap/Secret
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[-._a-zA-Z0-9]+$
Key string `json:"key"`
+
// +kubebuilder:default="Values"
TemplateAs TemplateScope `json:"templateAs,omitempty"`
}
@@ -160,22 +180,26 @@ type TemplateRefItem struct {
// ExternalSecretTarget defines the Kubernetes Secret to be created
// There can be only one target per ExternalSecret.
type ExternalSecretTarget struct {
- // Name defines the name of the Secret resource to be managed
- // This field is immutable
+ // The name of the Secret resource to be managed.
// Defaults to the .metadata.name of the ExternalSecret resource
// +optional
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
Name string `json:"name,omitempty"`
- // CreationPolicy defines rules on how to create the resulting Secret
- // Defaults to 'Owner'
+ // CreationPolicy defines rules on how to create the resulting Secret.
+ // Defaults to "Owner"
// +optional
// +kubebuilder:default="Owner"
CreationPolicy ExternalSecretCreationPolicy `json:"creationPolicy,omitempty"`
- // DeletionPolicy defines rules on how to delete the resulting Secret
- // Defaults to 'Retain'
+
+ // DeletionPolicy defines rules on how to delete the resulting Secret.
+ // Defaults to "Retain"
// +optional
// +kubebuilder:default="Retain"
DeletionPolicy ExternalSecretDeletionPolicy `json:"deletionPolicy,omitempty"`
+
// Template defines a blueprint for the created Secret resource.
// +optional
Template *ExternalSecretTemplate `json:"template,omitempty"`
@@ -187,8 +211,10 @@ type ExternalSecretTarget struct {
// ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data.
type ExternalSecretData struct {
- // SecretKey defines the key in which the controller stores
- // the value. This is the key in the Kind=Secret
+ // The key in the Kubernetes Secret to store the value.
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[-._a-zA-Z0-9]+$
SecretKey string `json:"secretKey"`
// RemoteRef points to the remote secret and defines
@@ -196,7 +222,7 @@ type ExternalSecretData struct {
RemoteRef ExternalSecretDataRemoteRef `json:"remoteRef"`
// SourceRef allows you to override the source
- // from which the value will pulled from.
+ // from which the value will be pulled.
SourceRef *StoreSourceRef `json:"sourceRef,omitempty"`
}
@@ -338,12 +364,15 @@ type FindName struct {
type ExternalSecretSpec struct {
// +optional
SecretStoreRef SecretStoreRef `json:"secretStoreRef,omitempty"`
+
// +kubebuilder:default={creationPolicy:Owner,deletionPolicy:Retain}
// +optional
Target ExternalSecretTarget `json:"target,omitempty"`
- // RefreshInterval is the amount of time before the values are read again from the SecretStore provider
+ // RefreshInterval is the amount of time before the values are read again from the SecretStore provider,
+ // specified as Golang Duration strings.
// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h"
+ // Example values: "1h", "2h30m", "5d", "10s"
// May be set to zero to fetch and create it once. Defaults to 1h.
// +kubebuilder:default="1h"
RefreshInterval *metav1.Duration `json:"refreshInterval,omitempty"`
@@ -362,6 +391,7 @@ type ExternalSecretSpec struct {
// from which the secret will be pulled from.
// You can define at maximum one property.
// +kubebuilder:validation:MaxProperties=1
+// +kubebuilder:validation:MinProperties=1
type StoreSourceRef struct {
// +optional
SecretStoreRef SecretStoreRef `json:"storeRef,omitempty"`
@@ -377,6 +407,7 @@ type StoreSourceRef struct {
// from which the secret will be pulled from.
// You can define at maximum one property.
// +kubebuilder:validation:MaxProperties=1
+// +kubebuilder:validation:MinProperties=1
type StoreGeneratorSourceRef struct {
// +optional
SecretStoreRef *SecretStoreRef `json:"storeRef,omitempty"`
@@ -391,9 +422,15 @@ type GeneratorRef struct {
// Specify the apiVersion of the generator resource
// +kubebuilder:default="generators.external-secrets.io/v1alpha1"
APIVersion string `json:"apiVersion,omitempty"`
- // Specify the Kind of the resource, e.g. Password, ACRAccessToken etc.
+
+ // Specify the Kind of the generator resource
+ // +kubebuilder:validation:Enum=ACRAccessToken;ClusterGenerator;ECRAuthorizationToken;Fake;GCRAccessToken;GithubAccessToken;QuayAccessToken;Password;STSSessionToken;UUID;VaultDynamicSecret;Webhook
Kind string `json:"kind"`
+
// Specify the name of the generator resource
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
Name string `json:"name"`
}
@@ -425,12 +462,15 @@ const (
ConditionReasonSecretSyncedError = "SecretSyncedError"
// ConditionReasonSecretDeleted indicates that the secret has been deleted.
ConditionReasonSecretDeleted = "SecretDeleted"
-
- ReasonUpdateFailed = "UpdateFailed"
- ReasonDeprecated = "ParameterDeprecated"
- ReasonCreated = "Created"
- ReasonUpdated = "Updated"
- ReasonDeleted = "Deleted"
+ // ConditionReasonSecretMissing indicates that the secret is missing.
+ ConditionReasonSecretMissing = "SecretMissing"
+
+ ReasonUpdateFailed = "UpdateFailed"
+ ReasonDeprecated = "ParameterDeprecated"
+ ReasonCreated = "Created"
+ ReasonUpdated = "Updated"
+ ReasonDeleted = "Deleted"
+ ReasonMissingProviderSecret = "MissingProviderSecret"
)
type ExternalSecretStatus struct {
@@ -453,7 +493,9 @@ type ExternalSecretStatus struct {
// +kubebuilder:storageversion
// ExternalSecret is the Schema for the external-secrets API.
// +kubebuilder:subresource:status
-// +kubebuilder:resource:scope=Namespaced,categories={externalsecrets},shortName=es
+// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
+// +kubebuilder:resource:scope=Namespaced,categories={external-secrets},shortName=es
+// +kubebuilder:printcolumn:name="StoreType",type=string,JSONPath=`.spec.secretStoreRef.kind`
// +kubebuilder:printcolumn:name="Store",type=string,JSONPath=`.spec.secretStoreRef.name`
// +kubebuilder:printcolumn:name="Refresh Interval",type=string,JSONPath=`.spec.refreshInterval`
// +kubebuilder:printcolumn:name="Status",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].reason`
@@ -467,10 +509,14 @@ type ExternalSecret struct {
}
const (
- // AnnotationDataHash is used to ensure consistency.
+ // AnnotationDataHash all secrets managed by an ExternalSecret have this annotation with the hash of their data.
AnnotationDataHash = "reconcile.external-secrets.io/data-hash"
- // LabelOwner points to the owning ExternalSecret resource
- // and is used to manage the lifecycle of a Secret
+
+ // LabelManaged all secrets managed by an ExternalSecret will have this label equal to "true".
+ LabelManaged = "reconcile.external-secrets.io/managed"
+ LabelManagedValue = "true"
+
+ // LabelOwner points to the owning ExternalSecret resource when CreationPolicy=Owner.
LabelOwner = "reconcile.external-secrets.io/created-by"
)
diff --git a/apis/externalsecrets/v1beta1/externalsecret_validator.go b/apis/externalsecrets/v1beta1/externalsecret_validator.go
index 6560c893e5f..c92e848e0f2 100644
--- a/apis/externalsecrets/v1beta1/externalsecret_validator.go
+++ b/apis/externalsecrets/v1beta1/externalsecret_validator.go
@@ -40,35 +40,29 @@ func (esv *ExternalSecretValidator) ValidateDelete(_ context.Context, _ runtime.
func validateExternalSecret(obj runtime.Object) (admission.Warnings, error) {
es, ok := obj.(*ExternalSecret)
if !ok {
- return nil, fmt.Errorf("unexpected type")
+ return nil, errors.New("unexpected type")
}
var errs error
- if (es.Spec.Target.DeletionPolicy == DeletionPolicyDelete && es.Spec.Target.CreationPolicy == CreatePolicyMerge) ||
- (es.Spec.Target.DeletionPolicy == DeletionPolicyDelete && es.Spec.Target.CreationPolicy == CreatePolicyNone) {
- errs = errors.Join(errs, fmt.Errorf("deletionPolicy=Delete must not be used when the controller doesn't own the secret. Please set creationPolicy=Owner"))
- }
-
- if es.Spec.Target.DeletionPolicy == DeletionPolicyMerge && es.Spec.Target.CreationPolicy == CreatePolicyNone {
- errs = errors.Join(errs, fmt.Errorf("deletionPolicy=Merge must not be used with creationPolicy=None. There is no Secret to merge with"))
+ if err := validatePolicies(es); err != nil {
+ errs = errors.Join(errs, err)
}
if len(es.Spec.Data) == 0 && len(es.Spec.DataFrom) == 0 {
- errs = errors.Join(errs, fmt.Errorf("either data or dataFrom should be specified"))
+ errs = errors.Join(errs, errors.New("either data or dataFrom should be specified"))
}
for _, ref := range es.Spec.DataFrom {
- generatorRef := ref.SourceRef != nil && ref.SourceRef.GeneratorRef != nil
- if (ref.Find != nil && (ref.Extract != nil || generatorRef)) || (ref.Extract != nil && (ref.Find != nil || generatorRef)) || (generatorRef && (ref.Find != nil || ref.Extract != nil)) {
- errs = errors.Join(errs, fmt.Errorf("extract, find, or generatorRef cannot be set at the same time"))
+ if err := validateExtractFindGenerator(ref); err != nil {
+ errs = errors.Join(errs, err)
}
- if ref.Find == nil && ref.Extract == nil && ref.SourceRef == nil {
- errs = errors.Join(errs, fmt.Errorf("either extract, find, or sourceRef must be set to dataFrom"))
+ if err := validateFindExtractSourceRef(ref); err != nil {
+ errs = errors.Join(errs, err)
}
- if ref.SourceRef != nil && ref.SourceRef.GeneratorRef == nil && ref.SourceRef.SecretStoreRef == nil {
- errs = errors.Join(errs, fmt.Errorf("generatorRef or storeRef must be set when using sourceRef in dataFrom"))
+ if err := validateSourceRef(ref); err != nil {
+ errs = errors.Join(errs, err)
}
}
@@ -76,6 +70,45 @@ func validateExternalSecret(obj runtime.Object) (admission.Warnings, error) {
return nil, errs
}
+func validateSourceRef(ref ExternalSecretDataFromRemoteRef) error {
+ if ref.SourceRef != nil && ref.SourceRef.GeneratorRef == nil && ref.SourceRef.SecretStoreRef == nil {
+ return errors.New("generatorRef or storeRef must be set when using sourceRef in dataFrom")
+ }
+
+ return nil
+}
+
+func validateFindExtractSourceRef(ref ExternalSecretDataFromRemoteRef) error {
+ if ref.Find == nil && ref.Extract == nil && ref.SourceRef == nil {
+ return errors.New("either extract, find, or sourceRef must be set to dataFrom")
+ }
+
+ return nil
+}
+
+func validateExtractFindGenerator(ref ExternalSecretDataFromRemoteRef) error {
+ generatorRef := ref.SourceRef != nil && ref.SourceRef.GeneratorRef != nil
+ if (ref.Find != nil && (ref.Extract != nil || generatorRef)) || (ref.Extract != nil && (ref.Find != nil || generatorRef)) || (generatorRef && (ref.Find != nil || ref.Extract != nil)) {
+ return errors.New("extract, find, or generatorRef cannot be set at the same time")
+ }
+
+ return nil
+}
+
+func validatePolicies(es *ExternalSecret) error {
+ var errs error
+ if (es.Spec.Target.DeletionPolicy == DeletionPolicyDelete && es.Spec.Target.CreationPolicy == CreatePolicyMerge) ||
+ (es.Spec.Target.DeletionPolicy == DeletionPolicyDelete && es.Spec.Target.CreationPolicy == CreatePolicyNone) {
+ errs = errors.Join(errs, errors.New("deletionPolicy=Delete must not be used when the controller doesn't own the secret. Please set creationPolicy=Owner"))
+ }
+
+ if es.Spec.Target.DeletionPolicy == DeletionPolicyMerge && es.Spec.Target.CreationPolicy == CreatePolicyNone {
+ errs = errors.Join(errs, errors.New("deletionPolicy=Merge must not be used with creationPolicy=None. There is no Secret to merge with"))
+ }
+
+ return errs
+}
+
func validateDuplicateKeys(es *ExternalSecret, errs error) error {
if es.Spec.Target.DeletionPolicy == DeletionPolicyRetain {
seenKeys := make(map[string]struct{})
diff --git a/apis/externalsecrets/v1beta1/externalsecret_validator_test.go b/apis/externalsecrets/v1beta1/externalsecret_validator_test.go
index 611ad6ae612..0391b663b99 100644
--- a/apis/externalsecrets/v1beta1/externalsecret_validator_test.go
+++ b/apis/externalsecrets/v1beta1/externalsecret_validator_test.go
@@ -20,6 +20,10 @@ import (
"k8s.io/apimachinery/pkg/runtime"
)
+const (
+ errExtractFindGenerator = "extract, find, or generatorRef cannot be set at the same time"
+)
+
func TestValidateExternalSecret(t *testing.T) {
tests := []struct {
name string
@@ -80,7 +84,7 @@ func TestValidateExternalSecret(t *testing.T) {
},
},
},
- expectedErr: "extract, find, or generatorRef cannot be set at the same time",
+ expectedErr: errExtractFindGenerator,
},
{
name: "generator with find",
@@ -96,7 +100,7 @@ func TestValidateExternalSecret(t *testing.T) {
},
},
},
- expectedErr: "extract, find, or generatorRef cannot be set at the same time",
+ expectedErr: errExtractFindGenerator,
},
{
name: "generator with extract",
@@ -112,7 +116,7 @@ func TestValidateExternalSecret(t *testing.T) {
},
},
},
- expectedErr: "extract, find, or generatorRef cannot be set at the same time",
+ expectedErr: errExtractFindGenerator,
},
{
name: "empty dataFrom",
diff --git a/apis/externalsecrets/v1beta1/provider.go b/apis/externalsecrets/v1beta1/provider.go
index f8e5b7498cb..be7799c26c2 100644
--- a/apis/externalsecrets/v1beta1/provider.go
+++ b/apis/externalsecrets/v1beta1/provider.go
@@ -105,3 +105,13 @@ type NoSecretError struct{}
func (NoSecretError) Error() string {
return "Secret does not exist"
}
+
+var NotModifiedErr = NotModifiedError{}
+
+// NotModifiedError to signal that the webhook received no changes,
+// and it should just return without doing anything.
+type NotModifiedError struct{}
+
+func (NotModifiedError) Error() string {
+ return "not modified"
+}
diff --git a/apis/externalsecrets/v1beta1/provider_schema.go b/apis/externalsecrets/v1beta1/provider_schema.go
index 8c7f37ad692..f990480d169 100644
--- a/apis/externalsecrets/v1beta1/provider_schema.go
+++ b/apis/externalsecrets/v1beta1/provider_schema.go
@@ -16,6 +16,7 @@ package v1beta1
import (
"encoding/json"
+ "errors"
"fmt"
"sync"
)
@@ -73,6 +74,9 @@ func GetProvider(s GenericStore) (Provider, error) {
}
spec := s.GetSpec()
if spec == nil {
+ // Note, this condition can never be reached, because
+ // the Spec is not a pointer in Kubernetes. It will
+ // always exist.
return nil, fmt.Errorf("no spec found in %#v", s)
}
storeName, err := getProviderName(spec.Provider)
@@ -113,5 +117,5 @@ func getProviderName(storeSpec *SecretStoreProvider) (string, error) {
return k, nil
}
- return "", fmt.Errorf("failed to find registered store backend")
+ return "", errors.New("failed to find registered store backend")
}
diff --git a/apis/externalsecrets/v1beta1/secretsstore_bitwarden_types.go b/apis/externalsecrets/v1beta1/secretsstore_bitwarden_types.go
new file mode 100644
index 00000000000..5bc06c3e6dc
--- /dev/null
+++ b/apis/externalsecrets/v1beta1/secretsstore_bitwarden_types.go
@@ -0,0 +1,50 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1beta1
+
+import (
+ esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+)
+
+// BitwardenSecretsManagerProvider configures a store to sync secrets with a Bitwarden Secrets Manager instance.
+type BitwardenSecretsManagerProvider struct {
+ APIURL string `json:"apiURL,omitempty"`
+ IdentityURL string `json:"identityURL,omitempty"`
+ BitwardenServerSDKURL string `json:"bitwardenServerSDKURL,omitempty"`
+ // Base64 encoded certificate for the bitwarden server sdk. The sdk MUST run with HTTPS to make sure no MITM attack
+ // can be performed.
+ // +optional
+ CABundle string `json:"caBundle,omitempty"`
+ // see: https://external-secrets.io/latest/spec/#external-secrets.io/v1alpha1.CAProvider
+ // +optional
+ CAProvider *CAProvider `json:"caProvider,omitempty"`
+ // OrganizationID determines which organization this secret store manages.
+ OrganizationID string `json:"organizationID"`
+ // ProjectID determines which project this secret store manages.
+ ProjectID string `json:"projectID"`
+ // Auth configures how secret-manager authenticates with a bitwarden machine account instance.
+ // Make sure that the token being used has permissions on the given secret.
+ Auth BitwardenSecretsManagerAuth `json:"auth"`
+}
+
+// BitwardenSecretsManagerAuth contains the ref to the secret that contains the machine account token.
+type BitwardenSecretsManagerAuth struct {
+ SecretRef BitwardenSecretsManagerSecretRef `json:"secretRef"`
+}
+
+// BitwardenSecretsManagerSecretRef contains the credential ref to the bitwarden instance.
+type BitwardenSecretsManagerSecretRef struct {
+ // AccessToken used for the bitwarden instance.
+ // +required
+ Credentials esmeta.SecretKeySelector `json:"credentials"`
+}
diff --git a/apis/externalsecrets/v1beta1/secretsstore_infisical_types.go b/apis/externalsecrets/v1beta1/secretsstore_infisical_types.go
new file mode 100644
index 00000000000..8e4428f3460
--- /dev/null
+++ b/apis/externalsecrets/v1beta1/secretsstore_infisical_types.go
@@ -0,0 +1,56 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1beta1
+
+import (
+ esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+)
+
+type UniversalAuthCredentials struct {
+ // +kubebuilder:validation:Required
+ ClientID esmeta.SecretKeySelector `json:"clientId"`
+ // +kubebuilder:validation:Required
+ ClientSecret esmeta.SecretKeySelector `json:"clientSecret"`
+}
+
+type InfisicalAuth struct {
+ // +optional
+ UniversalAuthCredentials *UniversalAuthCredentials `json:"universalAuthCredentials,omitempty"`
+}
+
+type MachineIdentityScopeInWorkspace struct {
+ // +kubebuilder:default="/"
+ // +optional
+ SecretsPath string `json:"secretsPath,omitempty"`
+ // +kubebuilder:default=false
+ // +optional
+ Recursive bool `json:"recursive,omitempty"`
+ // +kubebuilder:validation:Required
+ EnvironmentSlug string `json:"environmentSlug"`
+ // +kubebuilder:validation:Required
+ ProjectSlug string `json:"projectSlug"`
+}
+
+// InfisicalProvider configures a store to sync secrets using the Infisical provider.
+type InfisicalProvider struct {
+ // Auth configures how the Operator authenticates with the Infisical API
+ // +kubebuilder:validation:Required
+ Auth InfisicalAuth `json:"auth"`
+ // +kubebuilder:validation:Required
+ SecretsScope MachineIdentityScopeInWorkspace `json:"secretsScope"`
+ // +kubebuilder:default="https://app.infisical.com/api"
+ // +optional
+ HostAPI string `json:"hostAPI,omitempty"`
+}
diff --git a/apis/externalsecrets/v1beta1/secretsstore_secretserver_types.go b/apis/externalsecrets/v1beta1/secretsstore_secretserver_types.go
new file mode 100644
index 00000000000..41d75dade08
--- /dev/null
+++ b/apis/externalsecrets/v1beta1/secretsstore_secretserver_types.go
@@ -0,0 +1,45 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1beta1
+
+import esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+
+type SecretServerProviderRef struct {
+
+ // Value can be specified directly to set a value without using a secret.
+ // +optional
+ Value string `json:"value,omitempty"`
+
+ // SecretRef references a key in a secret that will be used as value.
+ // +optional
+ SecretRef *esmeta.SecretKeySelector `json:"secretRef,omitempty"`
+}
+
+// See https://github.com/DelineaXPM/tss-sdk-go/blob/main/server/server.go.
+type SecretServerProvider struct {
+
+ // Username is the secret server account username.
+ // +required
+ Username *SecretServerProviderRef `json:"username"`
+
+ // Password is the secret server account password.
+ // +required
+ Password *SecretServerProviderRef `json:"password"`
+
+ // ServerURL
+ // URL to your secret server installation
+ // +required
+ ServerURL string `json:"serverURL"`
+}
diff --git a/apis/externalsecrets/v1beta1/secretstore_aws_types.go b/apis/externalsecrets/v1beta1/secretstore_aws_types.go
index ebc4c7976ae..0455c39cca6 100644
--- a/apis/externalsecrets/v1beta1/secretstore_aws_types.go
+++ b/apis/externalsecrets/v1beta1/secretstore_aws_types.go
@@ -124,4 +124,8 @@ type AWSProvider struct {
// AWS STS assume role transitive session tags. Required when multiple rules are used with the provider
// +optional
TransitiveTagKeys []*string `json:"transitiveTagKeys,omitempty"`
+
+ // Prefix adds a prefix to all retrieved values.
+ // +optional
+ Prefix string `json:"prefix,omitempty"`
}
diff --git a/apis/externalsecrets/v1beta1/secretstore_azurekv_types.go b/apis/externalsecrets/v1beta1/secretstore_azurekv_types.go
index 333b7757a87..b94e347a34d 100644
--- a/apis/externalsecrets/v1beta1/secretstore_azurekv_types.go
+++ b/apis/externalsecrets/v1beta1/secretstore_azurekv_types.go
@@ -99,4 +99,8 @@ type AzureKVAuth struct {
// The Azure ClientSecret of the service principle used for authentication.
// +optional
ClientSecret *smmeta.SecretKeySelector `json:"clientSecret,omitempty"`
+
+ // The Azure ClientCertificate of the service principle used for authentication.
+ // +optional
+ ClientCertificate *smmeta.SecretKeySelector `json:"clientCertificate,omitempty"`
}
diff --git a/apis/externalsecrets/v1beta1/secretstore_beyondtrust_types.go b/apis/externalsecrets/v1beta1/secretstore_beyondtrust_types.go
new file mode 100644
index 00000000000..9c94b6583a6
--- /dev/null
+++ b/apis/externalsecrets/v1beta1/secretstore_beyondtrust_types.go
@@ -0,0 +1,65 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1beta1
+
+import esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+
+type BeyondTrustProviderSecretRef struct {
+
+ // Value can be specified directly to set a value without using a secret.
+ // +optional
+ Value string `json:"value,omitempty"`
+
+ // SecretRef references a key in a secret that will be used as value.
+ // +optional
+ SecretRef *esmeta.SecretKeySelector `json:"secretRef,omitempty"`
+}
+
+// Configures a store to sync secrets using BeyondTrust Password Safe.
+type BeyondtrustAuth struct {
+ // APIKey If not provided then ClientID/ClientSecret become required.
+ APIKey *BeyondTrustProviderSecretRef `json:"apiKey,omitempty"`
+ // ClientID is the API OAuth Client ID.
+ ClientID *BeyondTrustProviderSecretRef `json:"clientId,omitempty"`
+ // ClientSecret is the API OAuth Client Secret.
+ ClientSecret *BeyondTrustProviderSecretRef `json:"clientSecret,omitempty"`
+ // Certificate (cert.pem) for use when authenticating with an OAuth client Id using a Client Certificate.
+ Certificate *BeyondTrustProviderSecretRef `json:"certificate,omitempty"`
+ // Certificate private key (key.pem). For use when authenticating with an OAuth client Id
+ CertificateKey *BeyondTrustProviderSecretRef `json:"certificateKey,omitempty"`
+}
+
+// Configures a store to sync secrets using BeyondTrust Password Safe.
+type BeyondtrustServer struct {
+ // +required - BeyondTrust Password Safe API URL. https://example.com:443/beyondtrust/api/public/V3.
+ APIURL string `json:"apiUrl"`
+ // The secret retrieval type. SECRET = Secrets Safe (credential, text, file). MANAGED_ACCOUNT = Password Safe account associated with a system.
+ RetrievalType string `json:"retrievalType,omitempty"`
+ // A character that separates the folder names.
+ Separator string `json:"separator,omitempty"`
+ // +required - Indicates whether to verify the certificate authority on the Secrets Safe instance. Warning - false is insecure, instructs the BT provider not to verify the certificate authority.
+ VerifyCA bool `json:"verifyCA"`
+ // Timeout specifies a time limit for requests made by this Client. The timeout includes connection time, any redirects, and reading the response body. Defaults to 45 seconds.
+ ClientTimeOutSeconds int `json:"clientTimeOutSeconds,omitempty"`
+}
+
+type BeyondtrustProvider struct {
+
+ // Auth configures how the operator authenticates with Beyondtrust.
+ Auth *BeyondtrustAuth `json:"auth"`
+
+ // Auth configures how API server works.
+ Server *BeyondtrustServer `json:"server"`
+}
diff --git a/apis/externalsecrets/v1beta1/secretstore_device42_types.go b/apis/externalsecrets/v1beta1/secretstore_device42_types.go
new file mode 100644
index 00000000000..c311f75c55f
--- /dev/null
+++ b/apis/externalsecrets/v1beta1/secretstore_device42_types.go
@@ -0,0 +1,38 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1beta1
+
+import (
+ esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+)
+
+// Device42Provider configures a store to sync secrets with a Device42 instance.
+type Device42Provider struct {
+ // URL configures the Device42 instance URL.
+ Host string `json:"host"`
+
+ // Auth configures how secret-manager authenticates with a Device42 instance.
+ Auth Device42Auth `json:"auth"`
+}
+
+type Device42Auth struct {
+ SecretRef Device42SecretRef `json:"secretRef"`
+}
+
+type Device42SecretRef struct {
+ // Username / Password is used for authentication.
+ // +optional
+ Credentials esmeta.SecretKeySelector `json:"credentials,omitempty"`
+}
diff --git a/apis/externalsecrets/v1beta1/secretstore_gcpsm_types.go b/apis/externalsecrets/v1beta1/secretstore_gcpsm_types.go
index e2eff4633ad..6bf3710165d 100644
--- a/apis/externalsecrets/v1beta1/secretstore_gcpsm_types.go
+++ b/apis/externalsecrets/v1beta1/secretstore_gcpsm_types.go
@@ -46,4 +46,7 @@ type GCPSMProvider struct {
// ProjectID project where secret is located
ProjectID string `json:"projectID,omitempty"`
+
+ // Location optionally defines a location for a secret
+ Location string `json:"location,omitempty"`
}
diff --git a/apis/externalsecrets/v1beta1/secretstore_kubernetes_types.go b/apis/externalsecrets/v1beta1/secretstore_kubernetes_types.go
index fefd6d07b50..a718a3be3c1 100644
--- a/apis/externalsecrets/v1beta1/secretstore_kubernetes_types.go
+++ b/apis/externalsecrets/v1beta1/secretstore_kubernetes_types.go
@@ -37,14 +37,23 @@ type KubernetesServer struct {
// Configures a store to sync secrets with a Kubernetes instance.
type KubernetesProvider struct {
// configures the Kubernetes server Address.
+ // +optional
Server KubernetesServer `json:"server,omitempty"`
// Auth configures how secret-manager authenticates with a Kubernetes instance.
+ // +optional
Auth KubernetesAuth `json:"auth"`
+ // A reference to a secret that contains the auth information.
+ // +optional
+ AuthRef *esmeta.SecretKeySelector `json:"authRef,omitempty"`
+
// Remote namespace to fetch the secrets from
- // +kubebuilder:default= default
// +optional
+ // +kubebuilder:default=default
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=63
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
RemoteNamespace string `json:"remoteNamespace,omitempty"`
}
diff --git a/apis/externalsecrets/v1beta1/secretstore_previder_types.go b/apis/externalsecrets/v1beta1/secretstore_previder_types.go
new file mode 100644
index 00000000000..03035982fa1
--- /dev/null
+++ b/apis/externalsecrets/v1beta1/secretstore_previder_types.go
@@ -0,0 +1,38 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1beta1
+
+import (
+ esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+)
+
+// PreviderProvider configures a store to sync secrets using the Previder Secret Manager provider.
+type PreviderProvider struct {
+ Auth PreviderAuth `json:"auth"`
+ // +optional
+ BaseURI string `json:"baseUri,omitempty"`
+}
+
+// PreviderAuth contains a secretRef for credentials.
+type PreviderAuth struct {
+ // +optional
+ SecretRef *PreviderAuthSecretRef `json:"secretRef,omitempty"`
+}
+
+// PreviderAuthSecretRef holds secret references for Previder Vault credentials.
+type PreviderAuthSecretRef struct {
+ // The AccessToken is used for authentication
+ AccessToken esmeta.SecretKeySelector `json:"accessToken"`
+}
diff --git a/apis/externalsecrets/v1beta1/secretstore_pulumi_types.go b/apis/externalsecrets/v1beta1/secretstore_pulumi_types.go
index 4e6d69092bd..48c4b5ed804 100644
--- a/apis/externalsecrets/v1beta1/secretstore_pulumi_types.go
+++ b/apis/externalsecrets/v1beta1/secretstore_pulumi_types.go
@@ -20,7 +20,7 @@ import (
type PulumiProvider struct {
// APIURL is the URL of the Pulumi API.
- // +kubebuilder:default="https://api.pulumi.com"
+ // +kubebuilder:default="https://api.pulumi.com/api/esc"
APIURL string `json:"apiUrl,omitempty"`
// AccessToken is the access tokens to sign in to the Pulumi Cloud Console.
@@ -30,6 +30,8 @@ type PulumiProvider struct {
// To create a new organization, visit https://app.pulumi.com/ and click "New Organization".
Organization string `json:"organization"`
+ // Project is the name of the Pulumi ESC project the environment belongs to.
+ Project string `json:"project"`
// Environment are YAML documents composed of static key-value pairs, programmatic expressions,
// dynamically retrieved values from supported providers including all major clouds,
// and other Pulumi ESC environments.
diff --git a/apis/externalsecrets/v1beta1/secretstore_types.go b/apis/externalsecrets/v1beta1/secretstore_types.go
index b05d32e4471..c5be4ca736f 100644
--- a/apis/externalsecrets/v1beta1/secretstore_types.go
+++ b/apis/externalsecrets/v1beta1/secretstore_types.go
@@ -50,7 +50,15 @@ type ClusterSecretStoreCondition struct {
NamespaceSelector *metav1.LabelSelector `json:"namespaceSelector,omitempty"`
// Choose namespaces by name
+ // +optional
+ // +kubebuilder:validation:items:MinLength:=1
+ // +kubebuilder:validation:items:MaxLength:=63
+ // +kubebuilder:validation:items:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
Namespaces []string `json:"namespaces,omitempty"`
+
+ // Choose namespaces by using regex matching
+ // +optional
+ NamespaceRegexes []string `json:"namespaceRegexes,omitempty"`
}
// SecretStoreProvider contains the provider-specific configuration.
@@ -69,6 +77,10 @@ type SecretStoreProvider struct {
// +optional
Akeyless *AkeylessProvider `json:"akeyless,omitempty"`
+ // BitwardenSecretsManager configures this store to sync secrets using BitwardenSecretsManager provider
+ // +optional
+ BitwardenSecretsManager *BitwardenSecretsManagerProvider `json:"bitwardensecretsmanager,omitempty"`
+
// Vault configures this store to sync secrets using Hashi provider
// +optional
Vault *VaultProvider `json:"vault,omitempty"`
@@ -129,6 +141,10 @@ type SecretStoreProvider struct {
// +optional
Doppler *DopplerProvider `json:"doppler,omitempty"`
+ // Previder configures this store to sync secrets using the Previder provider
+ // +optional
+ Previder *PreviderProvider `json:"previder,omitempty"`
+
// Onboardbase configures this store to sync secrets using the Onboardbase provider
// +optional
Onboardbase *OnboardbaseProvider `json:"onboardbase,omitempty"`
@@ -146,6 +162,11 @@ type SecretStoreProvider struct {
// +optional
Delinea *DelineaProvider `json:"delinea,omitempty"`
+ // SecretServer configures this store to sync secrets using SecretServer provider
+ // https://docs.delinea.com/online-help/secret-server/start.htm
+ // +optional
+ SecretServer *SecretServerProvider `json:"secretserver,omitempty"`
+
// Chef configures this store to sync secrets with chef server
// +optional
Chef *ChefProvider `json:"chef,omitempty"`
@@ -163,6 +184,18 @@ type SecretStoreProvider struct {
// +optional
Passbolt *PassboltProvider `json:"passbolt,omitempty"`
+
+ // Device42 configures this store to sync secrets using the Device42 provider
+ // +optional
+ Device42 *Device42Provider `json:"device42,omitempty"`
+
+ // Infisical configures this store to sync secrets using the Infisical provider
+ // +optional
+ Infisical *InfisicalProvider `json:"infisical,omitempty"`
+
+ // Beyondtrust configures this store to sync secrets using Password Safe provider.
+ // +optional
+ Beyondtrust *BeyondtrustProvider `json:"beyondtrust,omitempty"`
}
type CAProviderType string
@@ -181,15 +214,24 @@ type CAProvider struct {
Type CAProviderType `json:"type"`
// The name of the object located at the provider type.
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
Name string `json:"name"`
// The key where the CA certificate can be found in the Secret or ConfigMap.
// +kubebuilder:validation:Optional
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[-._a-zA-Z0-9]+$
Key string `json:"key,omitempty"`
// The namespace the Provider type is in.
// Can only be defined when used in a ClusterSecretStore.
// +optional
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=63
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
Namespace *string `json:"namespace,omitempty"`
}
@@ -249,7 +291,8 @@ type SecretStoreStatus struct {
// +kubebuilder:printcolumn:name="Capabilities",type=string,JSONPath=`.status.capabilities`
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
// +kubebuilder:subresource:status
-// +kubebuilder:resource:scope=Namespaced,categories={externalsecrets},shortName=ss
+// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
+// +kubebuilder:resource:scope=Namespaced,categories={external-secrets},shortName=ss
type SecretStore struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
@@ -276,7 +319,8 @@ type SecretStoreList struct {
// +kubebuilder:printcolumn:name="Capabilities",type=string,JSONPath=`.status.capabilities`
// +kubebuilder:printcolumn:name="Ready",type=string,JSONPath=`.status.conditions[?(@.type=="Ready")].status`
// +kubebuilder:subresource:status
-// +kubebuilder:resource:scope=Cluster,categories={externalsecrets},shortName=css
+// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
+// +kubebuilder:resource:scope=Cluster,categories={external-secrets},shortName=css
type ClusterSecretStore struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
diff --git a/apis/externalsecrets/v1beta1/secretstore_validator.go b/apis/externalsecrets/v1beta1/secretstore_validator.go
index aa48978a52c..ecdb96234c6 100644
--- a/apis/externalsecrets/v1beta1/secretstore_validator.go
+++ b/apis/externalsecrets/v1beta1/secretstore_validator.go
@@ -16,7 +16,9 @@ package v1beta1
import (
"context"
+ "errors"
"fmt"
+ "regexp"
"k8s.io/apimachinery/pkg/runtime"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
@@ -34,7 +36,7 @@ type GenericStoreValidator struct{}
func (r *GenericStoreValidator) ValidateCreate(_ context.Context, obj runtime.Object) (admission.Warnings, error) {
st, ok := obj.(GenericStore)
if !ok {
- return nil, fmt.Errorf(errInvalidStore)
+ return nil, errors.New(errInvalidStore)
}
return validateStore(st)
}
@@ -43,7 +45,7 @@ func (r *GenericStoreValidator) ValidateCreate(_ context.Context, obj runtime.Ob
func (r *GenericStoreValidator) ValidateUpdate(_ context.Context, _, newObj runtime.Object) (admission.Warnings, error) {
st, ok := newObj.(GenericStore)
if !ok {
- return nil, fmt.Errorf(errInvalidStore)
+ return nil, errors.New(errInvalidStore)
}
return validateStore(st)
}
@@ -54,9 +56,27 @@ func (r *GenericStoreValidator) ValidateDelete(_ context.Context, _ runtime.Obje
}
func validateStore(store GenericStore) (admission.Warnings, error) {
+ if err := validateConditions(store); err != nil {
+ return nil, err
+ }
+
provider, err := GetProvider(store)
if err != nil {
return nil, err
}
+
return provider.ValidateStore(store)
}
+
+func validateConditions(store GenericStore) error {
+ var errs error
+ for ci, condition := range store.GetSpec().Conditions {
+ for ri, r := range condition.NamespaceRegexes {
+ if _, err := regexp.Compile(r); err != nil {
+ errs = errors.Join(errs, fmt.Errorf("failed to compile %dth namespace regex in %dth condition: %w", ri, ci, err))
+ }
+ }
+ }
+
+ return errs
+}
diff --git a/apis/externalsecrets/v1beta1/secretstore_validator_test.go b/apis/externalsecrets/v1beta1/secretstore_validator_test.go
new file mode 100644
index 00000000000..f146d5aa9df
--- /dev/null
+++ b/apis/externalsecrets/v1beta1/secretstore_validator_test.go
@@ -0,0 +1,150 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1beta1
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+)
+
+// ValidationProvider is a simple provider that we can use without cyclic import.
+type ValidationProvider struct {
+ Provider
+}
+
+func (v *ValidationProvider) ValidateStore(_ GenericStore) (admission.Warnings, error) {
+ return nil, nil
+}
+
+func TestValidateSecretStore(t *testing.T) {
+ tests := []struct {
+ name string
+ obj *SecretStore
+ mock func()
+ assertErr func(t *testing.T, err error)
+ }{
+ {
+ name: "valid regex",
+ obj: &SecretStore{
+ Spec: SecretStoreSpec{
+ Conditions: []ClusterSecretStoreCondition{
+ {
+ NamespaceRegexes: []string{`.*`},
+ },
+ },
+ Provider: &SecretStoreProvider{
+ AWS: &AWSProvider{},
+ },
+ },
+ },
+ mock: func() {
+ ForceRegister(&ValidationProvider{}, &SecretStoreProvider{
+ AWS: &AWSProvider{},
+ })
+ },
+ assertErr: func(t *testing.T, err error) {
+ require.NoError(t, err)
+ },
+ },
+ {
+ name: "invalid regex",
+ obj: &SecretStore{
+ Spec: SecretStoreSpec{
+ Conditions: []ClusterSecretStoreCondition{
+ {
+ NamespaceRegexes: []string{`\1`},
+ },
+ },
+ Provider: &SecretStoreProvider{
+ AWS: &AWSProvider{},
+ },
+ },
+ },
+ mock: func() {
+ ForceRegister(&ValidationProvider{}, &SecretStoreProvider{
+ AWS: &AWSProvider{},
+ })
+ },
+ assertErr: func(t *testing.T, err error) {
+ assert.EqualError(t, err, "failed to compile 0th namespace regex in 0th condition: error parsing regexp: invalid escape sequence: `\\1`")
+ },
+ },
+ {
+ name: "multiple errors",
+ obj: &SecretStore{
+ Spec: SecretStoreSpec{
+ Conditions: []ClusterSecretStoreCondition{
+ {
+ NamespaceRegexes: []string{`\1`, `\2`},
+ },
+ },
+ Provider: &SecretStoreProvider{
+ AWS: &AWSProvider{},
+ },
+ },
+ },
+ mock: func() {
+ ForceRegister(&ValidationProvider{}, &SecretStoreProvider{
+ AWS: &AWSProvider{},
+ })
+ },
+ assertErr: func(t *testing.T, err error) {
+ assert.EqualError(t, err, "failed to compile 0th namespace regex in 0th condition: error parsing regexp: invalid escape sequence: `\\1`\nfailed to compile 1th namespace regex in 0th condition: error parsing regexp: invalid escape sequence: `\\2`")
+ },
+ },
+ {
+ name: "secret store must have only a single backend",
+ obj: &SecretStore{
+ Spec: SecretStoreSpec{
+ Provider: &SecretStoreProvider{
+ AWS: &AWSProvider{},
+ GCPSM: &GCPSMProvider{},
+ },
+ },
+ },
+ assertErr: func(t *testing.T, err error) {
+ assert.EqualError(t, err, "store error for : secret stores must only have exactly one backend specified, found 2")
+ },
+ },
+ {
+ name: "no registered store backend",
+ obj: &SecretStore{
+ Spec: SecretStoreSpec{
+ Conditions: []ClusterSecretStoreCondition{
+ {
+ Namespaces: []string{"default"},
+ },
+ },
+ },
+ },
+ assertErr: func(t *testing.T, err error) {
+ assert.EqualError(t, err, "store error for : secret stores must only have exactly one backend specified, found 0")
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ if tt.mock != nil {
+ tt.mock()
+ }
+
+ _, err := validateStore(tt.obj)
+ tt.assertErr(t, err)
+ })
+ }
+}
diff --git a/apis/externalsecrets/v1beta1/secretstore_vault_types.go b/apis/externalsecrets/v1beta1/secretstore_vault_types.go
index d221001d063..73f9b28fa01 100644
--- a/apis/externalsecrets/v1beta1/secretstore_vault_types.go
+++ b/apis/externalsecrets/v1beta1/secretstore_vault_types.go
@@ -86,6 +86,10 @@ type VaultProvider struct {
// https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header
// +optional
ForwardInconsistent bool `json:"forwardInconsistent,omitempty"`
+
+ // Headers to be added in Vault request
+ // +optional
+ Headers map[string]string `json:"headers,omitempty"`
}
// VaultClientTLS is the configuration used for client side related TLS communication,
diff --git a/apis/externalsecrets/v1beta1/secretstore_webhook_types.go b/apis/externalsecrets/v1beta1/secretstore_webhook_types.go
index d78050e919d..34cd6242dca 100644
--- a/apis/externalsecrets/v1beta1/secretstore_webhook_types.go
+++ b/apis/externalsecrets/v1beta1/secretstore_webhook_types.go
@@ -75,14 +75,23 @@ type WebhookCAProvider struct {
Type WebhookCAProviderType `json:"type"`
// The name of the object located at the provider type.
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
Name string `json:"name"`
- // The key the value inside of the provider type to use, only used with "Secret" type
+ // The key where the CA certificate can be found in the Secret or ConfigMap.
// +kubebuilder:validation:Optional
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[-._a-zA-Z0-9]+$
Key string `json:"key,omitempty"`
// The namespace the Provider type is in.
// +optional
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=63
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
Namespace *string `json:"namespace,omitempty"`
}
diff --git a/apis/externalsecrets/v1beta1/zz_generated.deepcopy.go b/apis/externalsecrets/v1beta1/zz_generated.deepcopy.go
index 97259d5109e..8208b7f521a 100644
--- a/apis/externalsecrets/v1beta1/zz_generated.deepcopy.go
+++ b/apis/externalsecrets/v1beta1/zz_generated.deepcopy.go
@@ -329,6 +329,11 @@ func (in *AzureKVAuth) DeepCopyInto(out *AzureKVAuth) {
*out = new(metav1.SecretKeySelector)
(*in).DeepCopyInto(*out)
}
+ if in.ClientCertificate != nil {
+ in, out := &in.ClientCertificate, &out.ClientCertificate
+ *out = new(metav1.SecretKeySelector)
+ (*in).DeepCopyInto(*out)
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AzureKVAuth.
@@ -386,6 +391,159 @@ func (in *AzureKVProvider) DeepCopy() *AzureKVProvider {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BeyondTrustProviderSecretRef) DeepCopyInto(out *BeyondTrustProviderSecretRef) {
+ *out = *in
+ if in.SecretRef != nil {
+ in, out := &in.SecretRef, &out.SecretRef
+ *out = new(metav1.SecretKeySelector)
+ (*in).DeepCopyInto(*out)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BeyondTrustProviderSecretRef.
+func (in *BeyondTrustProviderSecretRef) DeepCopy() *BeyondTrustProviderSecretRef {
+ if in == nil {
+ return nil
+ }
+ out := new(BeyondTrustProviderSecretRef)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BeyondtrustAuth) DeepCopyInto(out *BeyondtrustAuth) {
+ *out = *in
+ if in.APIKey != nil {
+ in, out := &in.APIKey, &out.APIKey
+ *out = new(BeyondTrustProviderSecretRef)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.ClientID != nil {
+ in, out := &in.ClientID, &out.ClientID
+ *out = new(BeyondTrustProviderSecretRef)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.ClientSecret != nil {
+ in, out := &in.ClientSecret, &out.ClientSecret
+ *out = new(BeyondTrustProviderSecretRef)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.Certificate != nil {
+ in, out := &in.Certificate, &out.Certificate
+ *out = new(BeyondTrustProviderSecretRef)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.CertificateKey != nil {
+ in, out := &in.CertificateKey, &out.CertificateKey
+ *out = new(BeyondTrustProviderSecretRef)
+ (*in).DeepCopyInto(*out)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BeyondtrustAuth.
+func (in *BeyondtrustAuth) DeepCopy() *BeyondtrustAuth {
+ if in == nil {
+ return nil
+ }
+ out := new(BeyondtrustAuth)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BeyondtrustProvider) DeepCopyInto(out *BeyondtrustProvider) {
+ *out = *in
+ if in.Auth != nil {
+ in, out := &in.Auth, &out.Auth
+ *out = new(BeyondtrustAuth)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.Server != nil {
+ in, out := &in.Server, &out.Server
+ *out = new(BeyondtrustServer)
+ **out = **in
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BeyondtrustProvider.
+func (in *BeyondtrustProvider) DeepCopy() *BeyondtrustProvider {
+ if in == nil {
+ return nil
+ }
+ out := new(BeyondtrustProvider)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BeyondtrustServer) DeepCopyInto(out *BeyondtrustServer) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BeyondtrustServer.
+func (in *BeyondtrustServer) DeepCopy() *BeyondtrustServer {
+ if in == nil {
+ return nil
+ }
+ out := new(BeyondtrustServer)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BitwardenSecretsManagerAuth) DeepCopyInto(out *BitwardenSecretsManagerAuth) {
+ *out = *in
+ in.SecretRef.DeepCopyInto(&out.SecretRef)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BitwardenSecretsManagerAuth.
+func (in *BitwardenSecretsManagerAuth) DeepCopy() *BitwardenSecretsManagerAuth {
+ if in == nil {
+ return nil
+ }
+ out := new(BitwardenSecretsManagerAuth)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BitwardenSecretsManagerProvider) DeepCopyInto(out *BitwardenSecretsManagerProvider) {
+ *out = *in
+ if in.CAProvider != nil {
+ in, out := &in.CAProvider, &out.CAProvider
+ *out = new(CAProvider)
+ (*in).DeepCopyInto(*out)
+ }
+ in.Auth.DeepCopyInto(&out.Auth)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BitwardenSecretsManagerProvider.
+func (in *BitwardenSecretsManagerProvider) DeepCopy() *BitwardenSecretsManagerProvider {
+ if in == nil {
+ return nil
+ }
+ out := new(BitwardenSecretsManagerProvider)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *BitwardenSecretsManagerSecretRef) DeepCopyInto(out *BitwardenSecretsManagerSecretRef) {
+ *out = *in
+ in.Credentials.DeepCopyInto(&out.Credentials)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BitwardenSecretsManagerSecretRef.
+func (in *BitwardenSecretsManagerSecretRef) DeepCopy() *BitwardenSecretsManagerSecretRef {
+ if in == nil {
+ return nil
+ }
+ out := new(BitwardenSecretsManagerSecretRef)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *CAProvider) DeepCopyInto(out *CAProvider) {
*out = *in
@@ -677,6 +835,11 @@ func (in *ClusterSecretStoreCondition) DeepCopyInto(out *ClusterSecretStoreCondi
*out = make([]string, len(*in))
copy(*out, *in)
}
+ if in.NamespaceRegexes != nil {
+ in, out := &in.NamespaceRegexes, &out.NamespaceRegexes
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterSecretStoreCondition.
@@ -862,6 +1025,54 @@ func (in *DelineaProviderSecretRef) DeepCopy() *DelineaProviderSecretRef {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Device42Auth) DeepCopyInto(out *Device42Auth) {
+ *out = *in
+ in.SecretRef.DeepCopyInto(&out.SecretRef)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Device42Auth.
+func (in *Device42Auth) DeepCopy() *Device42Auth {
+ if in == nil {
+ return nil
+ }
+ out := new(Device42Auth)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Device42Provider) DeepCopyInto(out *Device42Provider) {
+ *out = *in
+ in.Auth.DeepCopyInto(&out.Auth)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Device42Provider.
+func (in *Device42Provider) DeepCopy() *Device42Provider {
+ if in == nil {
+ return nil
+ }
+ out := new(Device42Provider)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Device42SecretRef) DeepCopyInto(out *Device42SecretRef) {
+ *out = *in
+ in.Credentials.DeepCopyInto(&out.Credentials)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Device42SecretRef.
+func (in *Device42SecretRef) DeepCopy() *Device42SecretRef {
+ if in == nil {
+ return nil
+ }
+ out := new(Device42SecretRef)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *DopplerAuth) DeepCopyInto(out *DopplerAuth) {
*out = *in
@@ -1664,6 +1875,43 @@ func (in *IBMProvider) DeepCopy() *IBMProvider {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *InfisicalAuth) DeepCopyInto(out *InfisicalAuth) {
+ *out = *in
+ if in.UniversalAuthCredentials != nil {
+ in, out := &in.UniversalAuthCredentials, &out.UniversalAuthCredentials
+ *out = new(UniversalAuthCredentials)
+ (*in).DeepCopyInto(*out)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalAuth.
+func (in *InfisicalAuth) DeepCopy() *InfisicalAuth {
+ if in == nil {
+ return nil
+ }
+ out := new(InfisicalAuth)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *InfisicalProvider) DeepCopyInto(out *InfisicalProvider) {
+ *out = *in
+ in.Auth.DeepCopyInto(&out.Auth)
+ out.SecretsScope = in.SecretsScope
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalProvider.
+func (in *InfisicalProvider) DeepCopy() *InfisicalProvider {
+ if in == nil {
+ return nil
+ }
+ out := new(InfisicalProvider)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KeeperSecurityProvider) DeepCopyInto(out *KeeperSecurityProvider) {
*out = *in
@@ -1715,6 +1963,11 @@ func (in *KubernetesProvider) DeepCopyInto(out *KubernetesProvider) {
*out = *in
in.Server.DeepCopyInto(&out.Server)
in.Auth.DeepCopyInto(&out.Auth)
+ if in.AuthRef != nil {
+ in, out := &in.AuthRef, &out.AuthRef
+ *out = new(metav1.SecretKeySelector)
+ (*in).DeepCopyInto(*out)
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesProvider.
@@ -1752,6 +2005,21 @@ func (in *KubernetesServer) DeepCopy() *KubernetesServer {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *MachineIdentityScopeInWorkspace) DeepCopyInto(out *MachineIdentityScopeInWorkspace) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MachineIdentityScopeInWorkspace.
+func (in *MachineIdentityScopeInWorkspace) DeepCopy() *MachineIdentityScopeInWorkspace {
+ if in == nil {
+ return nil
+ }
+ out := new(MachineIdentityScopeInWorkspace)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *NoSecretError) DeepCopyInto(out *NoSecretError) {
*out = *in
@@ -1767,6 +2035,21 @@ func (in *NoSecretError) DeepCopy() *NoSecretError {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *NotModifiedError) DeepCopyInto(out *NotModifiedError) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new NotModifiedError.
+func (in *NotModifiedError) DeepCopy() *NotModifiedError {
+ if in == nil {
+ return nil
+ }
+ out := new(NotModifiedError)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *OnboardbaseAuthSecretRef) DeepCopyInto(out *OnboardbaseAuthSecretRef) {
*out = *in
@@ -2018,6 +2301,58 @@ func (in *PasswordDepotSecretRef) DeepCopy() *PasswordDepotSecretRef {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PreviderAuth) DeepCopyInto(out *PreviderAuth) {
+ *out = *in
+ if in.SecretRef != nil {
+ in, out := &in.SecretRef, &out.SecretRef
+ *out = new(PreviderAuthSecretRef)
+ (*in).DeepCopyInto(*out)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreviderAuth.
+func (in *PreviderAuth) DeepCopy() *PreviderAuth {
+ if in == nil {
+ return nil
+ }
+ out := new(PreviderAuth)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PreviderAuthSecretRef) DeepCopyInto(out *PreviderAuthSecretRef) {
+ *out = *in
+ in.AccessToken.DeepCopyInto(&out.AccessToken)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreviderAuthSecretRef.
+func (in *PreviderAuthSecretRef) DeepCopy() *PreviderAuthSecretRef {
+ if in == nil {
+ return nil
+ }
+ out := new(PreviderAuthSecretRef)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PreviderProvider) DeepCopyInto(out *PreviderProvider) {
+ *out = *in
+ in.Auth.DeepCopyInto(&out.Auth)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreviderProvider.
+func (in *PreviderProvider) DeepCopy() *PreviderProvider {
+ if in == nil {
+ return nil
+ }
+ out := new(PreviderProvider)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *PulumiProvider) DeepCopyInto(out *PulumiProvider) {
*out = *in
@@ -2103,6 +2438,51 @@ func (in *ScalewayProviderSecretRef) DeepCopy() *ScalewayProviderSecretRef {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *SecretServerProvider) DeepCopyInto(out *SecretServerProvider) {
+ *out = *in
+ if in.Username != nil {
+ in, out := &in.Username, &out.Username
+ *out = new(SecretServerProviderRef)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.Password != nil {
+ in, out := &in.Password, &out.Password
+ *out = new(SecretServerProviderRef)
+ (*in).DeepCopyInto(*out)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretServerProvider.
+func (in *SecretServerProvider) DeepCopy() *SecretServerProvider {
+ if in == nil {
+ return nil
+ }
+ out := new(SecretServerProvider)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *SecretServerProviderRef) DeepCopyInto(out *SecretServerProviderRef) {
+ *out = *in
+ if in.SecretRef != nil {
+ in, out := &in.SecretRef, &out.SecretRef
+ *out = new(metav1.SecretKeySelector)
+ (*in).DeepCopyInto(*out)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretServerProviderRef.
+func (in *SecretServerProviderRef) DeepCopy() *SecretServerProviderRef {
+ if in == nil {
+ return nil
+ }
+ out := new(SecretServerProviderRef)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SecretStore) DeepCopyInto(out *SecretStore) {
*out = *in
@@ -2180,6 +2560,11 @@ func (in *SecretStoreProvider) DeepCopyInto(out *SecretStoreProvider) {
*out = new(AkeylessProvider)
(*in).DeepCopyInto(*out)
}
+ if in.BitwardenSecretsManager != nil {
+ in, out := &in.BitwardenSecretsManager, &out.BitwardenSecretsManager
+ *out = new(BitwardenSecretsManagerProvider)
+ (*in).DeepCopyInto(*out)
+ }
if in.Vault != nil {
in, out := &in.Vault, &out.Vault
*out = new(VaultProvider)
@@ -2255,6 +2640,11 @@ func (in *SecretStoreProvider) DeepCopyInto(out *SecretStoreProvider) {
*out = new(DopplerProvider)
(*in).DeepCopyInto(*out)
}
+ if in.Previder != nil {
+ in, out := &in.Previder, &out.Previder
+ *out = new(PreviderProvider)
+ (*in).DeepCopyInto(*out)
+ }
if in.Onboardbase != nil {
in, out := &in.Onboardbase, &out.Onboardbase
*out = new(OnboardbaseProvider)
@@ -2275,6 +2665,11 @@ func (in *SecretStoreProvider) DeepCopyInto(out *SecretStoreProvider) {
*out = new(DelineaProvider)
(*in).DeepCopyInto(*out)
}
+ if in.SecretServer != nil {
+ in, out := &in.SecretServer, &out.SecretServer
+ *out = new(SecretServerProvider)
+ (*in).DeepCopyInto(*out)
+ }
if in.Chef != nil {
in, out := &in.Chef, &out.Chef
*out = new(ChefProvider)
@@ -2300,6 +2695,21 @@ func (in *SecretStoreProvider) DeepCopyInto(out *SecretStoreProvider) {
*out = new(PassboltProvider)
(*in).DeepCopyInto(*out)
}
+ if in.Device42 != nil {
+ in, out := &in.Device42, &out.Device42
+ *out = new(Device42Provider)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.Infisical != nil {
+ in, out := &in.Infisical, &out.Infisical
+ *out = new(InfisicalProvider)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.Beyondtrust != nil {
+ in, out := &in.Beyondtrust, &out.Beyondtrust
+ *out = new(BeyondtrustProvider)
+ (*in).DeepCopyInto(*out)
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretStoreProvider.
@@ -2611,6 +3021,23 @@ func (in *TokenAuth) DeepCopy() *TokenAuth {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *UniversalAuthCredentials) DeepCopyInto(out *UniversalAuthCredentials) {
+ *out = *in
+ in.ClientID.DeepCopyInto(&out.ClientID)
+ in.ClientSecret.DeepCopyInto(&out.ClientSecret)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UniversalAuthCredentials.
+func (in *UniversalAuthCredentials) DeepCopy() *UniversalAuthCredentials {
+ if in == nil {
+ return nil
+ }
+ out := new(UniversalAuthCredentials)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultAppRole) DeepCopyInto(out *VaultAppRole) {
*out = *in
@@ -2947,6 +3374,13 @@ func (in *VaultProvider) DeepCopyInto(out *VaultProvider) {
*out = new(CAProvider)
(*in).DeepCopyInto(*out)
}
+ if in.Headers != nil {
+ in, out := &in.Headers, &out.Headers
+ *out = make(map[string]string, len(*in))
+ for key, val := range *in {
+ (*out)[key] = val
+ }
+ }
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultProvider.
diff --git a/apis/generators/v1alpha1/generator.go b/apis/generators/v1alpha1/generator_interfaces.go
similarity index 89%
rename from apis/generators/v1alpha1/generator.go
rename to apis/generators/v1alpha1/generator_interfaces.go
index 57283adec0c..64af15bbeda 100644
--- a/apis/generators/v1alpha1/generator.go
+++ b/apis/generators/v1alpha1/generator_interfaces.go
@@ -25,6 +25,8 @@ import (
// +kubebuilder:object:generate:false
// +k8s:deepcopy-gen:interfaces=nil
// +k8s:deepcopy-gen=nil
+
+// Generator is the common interface for all generators that is actually used to generate whatever is needed.
type Generator interface {
Generate(
ctx context.Context,
diff --git a/apis/generators/v1alpha1/generator_schema.go b/apis/generators/v1alpha1/generator_schema.go
index 3eaa263c97b..ed0a13a0aa5 100644
--- a/apis/generators/v1alpha1/generator_schema.go
+++ b/apis/generators/v1alpha1/generator_schema.go
@@ -17,10 +17,6 @@ package v1alpha1
import (
"fmt"
"sync"
-
- apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/util/json"
)
var builder map[string]Generator
@@ -58,24 +54,3 @@ func GetGeneratorByName(kind string) (Generator, bool) {
buildlock.RUnlock()
return f, ok
}
-
-// GetGenerator returns a implementation from a generator
-// defined as json.
-func GetGenerator(obj *apiextensions.JSON) (Generator, error) {
- type unknownGenerator struct {
- metav1.TypeMeta `json:",inline"`
- metav1.ObjectMeta `json:"metadata,omitempty"`
- }
- var res unknownGenerator
- err := json.Unmarshal(obj.Raw, &res)
- if err != nil {
- return nil, err
- }
- buildlock.RLock()
- defer buildlock.RUnlock()
- gen, ok := builder[res.Kind]
- if !ok {
- return nil, fmt.Errorf("failed to find registered generator for: %s", string(obj.Raw))
- }
- return gen, nil
-}
diff --git a/apis/generators/v1alpha1/register.go b/apis/generators/v1alpha1/register.go
index f3896fe359d..a0dab72ab9c 100644
--- a/apis/generators/v1alpha1/register.go
+++ b/apis/generators/v1alpha1/register.go
@@ -44,6 +44,14 @@ var (
ECRAuthorizationTokenGroupVersionKind = SchemeGroupVersion.WithKind(ECRAuthorizationTokenKind)
)
+// STSSessionToken type metadata.
+var (
+ STSSessionTokenKind = reflect.TypeOf(STSSessionToken{}).Name()
+ STSSessionTokenGroupKind = schema.GroupKind{Group: Group, Kind: STSSessionTokenKind}.String()
+ STSSessionTokenKindAPIVersion = STSSessionTokenKind + "." + SchemeGroupVersion.String()
+ STSSessionTokenGroupVersionKind = SchemeGroupVersion.WithKind(STSSessionTokenKind)
+)
+
// GCRAccessToken type metadata.
var (
GCRAccessTokenKind = reflect.TypeOf(GCRAccessToken{}).Name()
@@ -100,13 +108,56 @@ var (
GithubAccessTokenGroupVersionKind = SchemeGroupVersion.WithKind(GithubAccessTokenKind)
)
+// QuayAccessToken type metadata.
+var (
+ QuayAccessTokenKind = reflect.TypeOf(QuayAccessToken{}).Name()
+ QuayAccessTokenGroupKind = schema.GroupKind{Group: Group, Kind: QuayAccessTokenKind}.String()
+ QuayAccessTokenKindAPIVersion = QuayAccessTokenKind + "." + SchemeGroupVersion.String()
+ QuayAccessTokenGroupVersionKind = SchemeGroupVersion.WithKind(QuayAccessTokenKind)
+)
+
+// Uuid type metadata.
+var (
+ UUIDKind = reflect.TypeOf(UUID{}).Name()
+ UUIDGroupKind = schema.GroupKind{Group: Group, Kind: UUIDKind}.String()
+ UUIDKindAPIVersion = UUIDKind + "." + SchemeGroupVersion.String()
+ UUIDGroupVersionKind = SchemeGroupVersion.WithKind(UUIDKind)
+)
+
+// ClusterGenerator type metadata.
+var (
+ ClusterGeneratorKind = reflect.TypeOf(ClusterGenerator{}).Name()
+ ClusterGeneratorGroupKind = schema.GroupKind{Group: Group, Kind: ClusterGeneratorKind}.String()
+ ClusterGeneratorKindAPIVersion = ClusterGeneratorKind + "." + SchemeGroupVersion.String()
+ ClusterGeneratorGroupVersionKind = SchemeGroupVersion.WithKind(ClusterGeneratorKind)
+)
+
func init() {
- SchemeBuilder.Register(&ECRAuthorizationToken{}, &ECRAuthorizationToken{})
- SchemeBuilder.Register(&GCRAccessToken{}, &GCRAccessTokenList{})
- SchemeBuilder.Register(&GithubAccessToken{}, &GithubAccessTokenList{})
+ /*
+ ===============================================================================
+ NOTE: when adding support for new kinds of generators:
+ 1. register the struct types in `SchemeBuilder` (right below this note)
+ 2. update the `kubebuilder:validation:Enum` annotation for GeneratorRef.Kind (apis/externalsecrets/v1beta1/externalsecret_types.go)
+ 3. add it to the imports of (pkg/generator/register/register.go)
+ 4. add it to the ClusterRole called "*-controller" (deploy/charts/external-secrets/templates/rbac.yaml)
+ 5. support it in ClusterGenerator:
+ - add a new GeneratorKind enum value (apis/generators/v1alpha1/types_cluster.go)
+ - update the `kubebuilder:validation:Enum` annotation for the GeneratorKind enum
+ - add a spec field to GeneratorSpec (apis/generators/v1alpha1/types_cluster.go)
+ - update the clusterGeneratorToVirtual() function (pkg/utils/resolvers/generator.go)
+ ===============================================================================
+ */
+
SchemeBuilder.Register(&ACRAccessToken{}, &ACRAccessTokenList{})
+ SchemeBuilder.Register(&ClusterGenerator{}, &ClusterGeneratorList{})
+ SchemeBuilder.Register(&ECRAuthorizationToken{}, &ECRAuthorizationTokenList{})
SchemeBuilder.Register(&Fake{}, &FakeList{})
- SchemeBuilder.Register(&VaultDynamicSecret{}, &VaultDynamicSecretList{})
+ SchemeBuilder.Register(&GCRAccessToken{}, &GCRAccessTokenList{})
+ SchemeBuilder.Register(&GithubAccessToken{}, &GithubAccessTokenList{})
+ SchemeBuilder.Register(&QuayAccessToken{}, &QuayAccessTokenList{})
SchemeBuilder.Register(&Password{}, &PasswordList{})
+ SchemeBuilder.Register(&STSSessionToken{}, &STSSessionTokenList{})
+ SchemeBuilder.Register(&UUID{}, &UUIDList{})
+ SchemeBuilder.Register(&VaultDynamicSecret{}, &VaultDynamicSecretList{})
SchemeBuilder.Register(&Webhook{}, &WebhookList{})
}
diff --git a/apis/generators/v1alpha1/generator_acr.go b/apis/generators/v1alpha1/types_acr.go
similarity index 96%
rename from apis/generators/v1alpha1/generator_acr.go
rename to apis/generators/v1alpha1/types_acr.go
index 6747d727ca8..2b940c660f8 100644
--- a/apis/generators/v1alpha1/generator_acr.go
+++ b/apis/generators/v1alpha1/types_acr.go
@@ -104,7 +104,8 @@ type AzureACRServicePrincipalAuthSecretRef struct {
// +kubebuilder:object:root=true
// +kubebuilder:storageversion
// +kubebuilder:subresource:status
-// +kubebuilder:resource:scope=Namespaced,categories={acraccesstoken},shortName=acraccesstoken
+// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
+// +kubebuilder:resource:scope=Namespaced,categories={external-secrets, external-secrets-generators}
type ACRAccessToken struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
diff --git a/apis/generators/v1alpha1/types_cluster.go b/apis/generators/v1alpha1/types_cluster.go
new file mode 100644
index 00000000000..4ee116fb66e
--- /dev/null
+++ b/apis/generators/v1alpha1/types_cluster.go
@@ -0,0 +1,83 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+type ClusterGeneratorSpec struct {
+ // Kind the kind of this generator.
+ Kind GeneratorKind `json:"kind"`
+
+ // Generator the spec for this generator, must match the kind.
+ Generator GeneratorSpec `json:"generator"`
+}
+
+// GeneratorKind represents a kind of generator.
+// +kubebuilder:validation:Enum=ACRAccessToken;ECRAuthorizationToken;Fake;GCRAccessToken;GithubAccessToken;QuayAccessToken;Password;STSSessionToken;UUID;VaultDynamicSecret;Webhook
+type GeneratorKind string
+
+const (
+ GeneratorKindACRAccessToken GeneratorKind = "ACRAccessToken"
+ GeneratorKindECRAuthorizationToken GeneratorKind = "ECRAuthorizationToken"
+ GeneratorKindFake GeneratorKind = "Fake"
+ GeneratorKindGCRAccessToken GeneratorKind = "GCRAccessToken"
+ GeneratorKindGithubAccessToken GeneratorKind = "GithubAccessToken"
+ GeneratorKindQuayAccessToken GeneratorKind = "QuayAccessToken"
+ GeneratorKindPassword GeneratorKind = "Password"
+ GeneratorKindSTSSessionToken GeneratorKind = "STSSessionToken"
+ GeneratorKindUUID GeneratorKind = "UUID"
+ GeneratorKindVaultDynamicSecret GeneratorKind = "VaultDynamicSecret"
+ GeneratorKindWebhook GeneratorKind = "Webhook"
+)
+
+// +kubebuilder:validation:MaxProperties=1
+// +kubebuilder:validation:MinProperties=1
+type GeneratorSpec struct {
+ ACRAccessTokenSpec *ACRAccessTokenSpec `json:"acrAccessTokenSpec,omitempty"`
+ ECRAuthorizationTokenSpec *ECRAuthorizationTokenSpec `json:"ecrAuthorizationTokenSpec,omitempty"`
+ FakeSpec *FakeSpec `json:"fakeSpec,omitempty"`
+ GCRAccessTokenSpec *GCRAccessTokenSpec `json:"gcrAccessTokenSpec,omitempty"`
+ GithubAccessTokenSpec *GithubAccessTokenSpec `json:"githubAccessTokenSpec,omitempty"`
+ QuayAccessTokenSpec *QuayAccessTokenSpec `json:"quayAccessTokenSpec,omitempty"`
+ PasswordSpec *PasswordSpec `json:"passwordSpec,omitempty"`
+ STSSessionTokenSpec *STSSessionTokenSpec `json:"stsSessionTokenSpec,omitempty"`
+ UUIDSpec *UUIDSpec `json:"uuidSpec,omitempty"`
+ VaultDynamicSecretSpec *VaultDynamicSecretSpec `json:"vaultDynamicSecretSpec,omitempty"`
+ WebhookSpec *WebhookSpec `json:"webhookSpec,omitempty"`
+}
+
+// ClusterGenerator represents a cluster-wide generator which can be referenced as part of `generatorRef` fields.
+// +kubebuilder:object:root=true
+// +kubebuilder:storageversion
+// +kubebuilder:subresource:status
+// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
+// +kubebuilder:resource:scope=Cluster,categories={external-secrets, external-secrets-generators}
+type ClusterGenerator struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ObjectMeta `json:"metadata,omitempty"`
+
+ Spec ClusterGeneratorSpec `json:"spec,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+
+// ClusterGeneratorList contains a list of ClusterGenerator resources.
+type ClusterGeneratorList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata,omitempty"`
+ Items []ClusterGenerator `json:"items"`
+}
diff --git a/apis/generators/v1alpha1/generator_ecr.go b/apis/generators/v1alpha1/types_ecr.go
similarity index 91%
rename from apis/generators/v1alpha1/generator_ecr.go
rename to apis/generators/v1alpha1/types_ecr.go
index e72edf9f40c..19e124b8683 100644
--- a/apis/generators/v1alpha1/generator_ecr.go
+++ b/apis/generators/v1alpha1/types_ecr.go
@@ -32,6 +32,11 @@ type ECRAuthorizationTokenSpec struct {
// desired AWS service.
// +optional
Role string `json:"role,omitempty"`
+
+ // Scope specifies the ECR service scope.
+ // Valid options are private and public.
+ // +optional
+ Scope string `json:"scope,omitempty"`
}
// AWSAuth tells the controller how to do authentication with aws.
@@ -74,7 +79,8 @@ type AWSJWTAuth struct {
// +kubebuilder:object:root=true
// +kubebuilder:storageversion
// +kubebuilder:subresource:status
-// +kubebuilder:resource:scope=Namespaced,categories={ecrauthorizationtoken},shortName=ecrauthorizationtoken
+// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
+// +kubebuilder:resource:scope=Namespaced,categories={external-secrets, external-secrets-generators}
type ECRAuthorizationToken struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
diff --git a/apis/generators/v1alpha1/generator_fake.go b/apis/generators/v1alpha1/types_fake.go
similarity index 90%
rename from apis/generators/v1alpha1/generator_fake.go
rename to apis/generators/v1alpha1/types_fake.go
index f5ce8e9b453..cebf116c83c 100644
--- a/apis/generators/v1alpha1/generator_fake.go
+++ b/apis/generators/v1alpha1/types_fake.go
@@ -35,7 +35,8 @@ type FakeSpec struct {
// +kubebuilder:object:root=true
// +kubebuilder:storageversion
// +kubebuilder:subresource:status
-// +kubebuilder:resource:scope=Namespaced,categories={fake},shortName=fake
+// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
+// +kubebuilder:resource:scope=Namespaced,categories={external-secrets, external-secrets-generators}
type Fake struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
diff --git a/apis/generators/v1alpha1/generator_gcr.go b/apis/generators/v1alpha1/types_gcr.go
similarity index 92%
rename from apis/generators/v1alpha1/generator_gcr.go
rename to apis/generators/v1alpha1/types_gcr.go
index 2302306942c..6c973fb9109 100644
--- a/apis/generators/v1alpha1/generator_gcr.go
+++ b/apis/generators/v1alpha1/types_gcr.go
@@ -52,7 +52,8 @@ type GCPWorkloadIdentity struct {
// +kubebuilder:object:root=true
// +kubebuilder:storageversion
// +kubebuilder:subresource:status
-// +kubebuilder:resource:scope=Namespaced,categories={gcraccesstoken},shortName=gcraccesstoken
+// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
+// +kubebuilder:resource:scope=Namespaced,categories={external-secrets, external-secrets-generators}
type GCRAccessToken struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
diff --git a/apis/generators/v1alpha1/generator_github.go b/apis/generators/v1alpha1/types_github.go
similarity index 74%
rename from apis/generators/v1alpha1/generator_github.go
rename to apis/generators/v1alpha1/types_github.go
index ed228791859..cbb0f22a62f 100644
--- a/apis/generators/v1alpha1/generator_github.go
+++ b/apis/generators/v1alpha1/types_github.go
@@ -25,12 +25,17 @@ type GithubAccessTokenSpec struct {
URL string `json:"url,omitempty"`
AppID string `json:"appID"`
InstallID string `json:"installID"`
+ // List of repositories the token will have access to. If omitted, defaults to all repositories the GitHub App
+ // is installed to.
+ Repositories []string `json:"repositories,omitempty"`
+ // Map of permissions the token will have. If omitted, defaults to all permissions the GitHub App has.
+ Permissions map[string]string `json:"permissions,omitempty"`
// Auth configures how ESO authenticates with a Github instance.
Auth GithubAuth `json:"auth"`
}
type GithubAuth struct {
- PrivatKey GithubSecretRef `json:"privatKey"`
+ PrivateKey GithubSecretRef `json:"privateKey"`
}
type GithubSecretRef struct {
@@ -41,7 +46,8 @@ type GithubSecretRef struct {
// +kubebuilder:object:root=true
// +kubebuilder:storageversion
// +kubebuilder:subresource:status
-// +kubebuilder:resource:scope=Namespaced,categories={githubaccesstoken},shortName=githubaccesstoken
+// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
+// +kubebuilder:resource:scope=Namespaced,categories={external-secrets, external-secrets-generators}
type GithubAccessToken struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
diff --git a/apis/generators/v1alpha1/generator_password.go b/apis/generators/v1alpha1/types_password.go
similarity index 92%
rename from apis/generators/v1alpha1/generator_password.go
rename to apis/generators/v1alpha1/types_password.go
index d33ba2c63d6..56e833c9cfe 100644
--- a/apis/generators/v1alpha1/generator_password.go
+++ b/apis/generators/v1alpha1/types_password.go
@@ -52,7 +52,8 @@ type PasswordSpec struct {
// +kubebuilder:object:root=true
// +kubebuilder:storageversion
// +kubebuilder:subresource:status
-// +kubebuilder:resource:scope=Namespaced,categories={password},shortName=password
+// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
+// +kubebuilder:resource:scope=Namespaced,categories={external-secrets, external-secrets-generators}
type Password struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
diff --git a/apis/generators/v1alpha1/types_quay.go b/apis/generators/v1alpha1/types_quay.go
new file mode 100644
index 00000000000..09b663112af
--- /dev/null
+++ b/apis/generators/v1alpha1/types_quay.go
@@ -0,0 +1,52 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+)
+
+type QuayAccessTokenSpec struct {
+ // URL configures the Quay instance URL. Defaults to quay.io.
+ URL string `json:"url,omitempty"`
+ // Name of the robot account you are federating with
+ RobotAccount string `json:"robotAccount"`
+ // Name of the service account you are federating with
+ ServiceAccountRef esmeta.ServiceAccountSelector `json:"serviceAccountRef"`
+}
+
+// QuayAccessToken generates Quay oauth token for pulling/pushing images
+// +kubebuilder:object:root=true
+// +kubebuilder:storageversion
+// +kubebuilder:subresource:status
+// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
+// +kubebuilder:resource:scope=Namespaced,categories={external-secrets, external-secrets-generators}
+type QuayAccessToken struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ObjectMeta `json:"metadata,omitempty"`
+
+ Spec QuayAccessTokenSpec `json:"spec,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+
+// QuayAccessTokenList contains a list of ExternalSecret resources.
+type QuayAccessTokenList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata,omitempty"`
+ Items []QuayAccessToken `json:"items"`
+}
diff --git a/apis/generators/v1alpha1/types_sts.go b/apis/generators/v1alpha1/types_sts.go
new file mode 100644
index 00000000000..3b8c4113b1f
--- /dev/null
+++ b/apis/generators/v1alpha1/types_sts.go
@@ -0,0 +1,80 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// RequestParameters contains parameters that can be passed to the STS service.
+type RequestParameters struct {
+ // SessionDuration The duration, in seconds, that the credentials should remain valid. Acceptable durations for
+ // IAM user sessions range from 900 seconds (15 minutes) to 129,600 seconds (36 hours), with 43,200 seconds
+ // (12 hours) as the default.
+ // +optional
+ SessionDuration *int64 `json:"sessionDuration,omitempty"`
+ // SerialNumber is the identification number of the MFA device that is associated with the IAM user who is making
+ // the GetSessionToken call.
+ // Possible values: hardware device (such as GAHT12345678) or an Amazon Resource Name (ARN) for a virtual device
+ // (such as arn:aws:iam::123456789012:mfa/user)
+ // +optional
+ SerialNumber *string `json:"serialNumber,omitempty"`
+ // TokenCode is the value provided by the MFA device, if MFA is required.
+ // +optional
+ TokenCode *string `json:"tokenCode,omitempty"`
+}
+
+type STSSessionTokenSpec struct {
+ // Region specifies the region to operate in.
+ Region string `json:"region"`
+
+ // Auth defines how to authenticate with AWS
+ // +optional
+ Auth AWSAuth `json:"auth,omitempty"`
+
+ // You can assume a role before making calls to the
+ // desired AWS service.
+ // +optional
+ Role string `json:"role,omitempty"`
+
+ // RequestParameters contains parameters that can be passed to the STS service.
+ // +optional
+ RequestParameters *RequestParameters `json:"requestParameters,omitempty"`
+}
+
+// STSSessionToken uses the GetSessionToken API to retrieve an authorization token.
+// The authorization token is valid for 12 hours.
+// The authorizationToken returned is a base64 encoded string that can be decoded.
+// For more information, see GetSessionToken (https://docs.aws.amazon.com/STS/latest/APIReference/API_GetSessionToken.html).
+// +kubebuilder:object:root=true
+// +kubebuilder:storageversion
+// +kubebuilder:subresource:status
+// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
+// +kubebuilder:resource:scope=Namespaced,categories={external-secrets, external-secrets-generators}
+type STSSessionToken struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ObjectMeta `json:"metadata,omitempty"`
+
+ Spec STSSessionTokenSpec `json:"spec,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+
+// STSSessionTokenList contains a list of STSSessionToken resources.
+type STSSessionTokenList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata,omitempty"`
+ Items []STSSessionToken `json:"items"`
+}
diff --git a/apis/generators/v1alpha1/types_uuid.go b/apis/generators/v1alpha1/types_uuid.go
new file mode 100644
index 00000000000..dfeff408394
--- /dev/null
+++ b/apis/generators/v1alpha1/types_uuid.go
@@ -0,0 +1,44 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1alpha1
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// UUIDSpec controls the behavior of the uuid generator.
+type UUIDSpec struct{}
+
+// UUID generates a version 1 UUID (e56657e3-764f-11ef-a397-65231a88c216).
+// +kubebuilder:object:root=true
+// +kubebuilder:storageversion
+// +kubebuilder:subresource:status
+// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
+// +kubebuilder:resource:scope=Namespaced,categories={external-secrets, external-secrets-generators}
+type UUID struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ObjectMeta `json:"metadata,omitempty"`
+
+ Spec UUIDSpec `json:"spec,omitempty"`
+}
+
+// +kubebuilder:object:root=true
+
+// UUIDList contains a list of ExternalSecret resources.
+type UUIDList struct {
+ metav1.TypeMeta `json:",inline"`
+ metav1.ListMeta `json:"metadata,omitempty"`
+ Items []UUID `json:"items"`
+}
diff --git a/apis/generators/v1alpha1/generator_vault.go b/apis/generators/v1alpha1/types_vault.go
similarity index 83%
rename from apis/generators/v1alpha1/generator_vault.go
rename to apis/generators/v1alpha1/types_vault.go
index 3f77c81a1b7..9266b5427ec 100644
--- a/apis/generators/v1alpha1/generator_vault.go
+++ b/apis/generators/v1alpha1/types_vault.go
@@ -41,11 +41,20 @@ type VaultDynamicSecretSpec struct {
// +kubebuilder:default=Data
ResultType VaultDynamicSecretResultType `json:"resultType,omitempty"`
+ // Used to configure http retries if failed
+ // +optional
+ RetrySettings *esv1beta1.SecretStoreRetrySettings `json:"retrySettings,omitempty"`
+
// Vault provider common spec
Provider *esv1beta1.VaultProvider `json:"provider"`
// Vault path to obtain the dynamic secret from
Path string `json:"path"`
+
+ // Do not fail if no secrets are found. Useful for requests where no data is expected.
+ // +optional
+ // +kubebuilder:default=false
+ AllowEmptyResponse bool `json:"allowEmptyResponse,omitempty"`
}
// +kubebuilder:validation:Enum=Data;Auth
@@ -59,7 +68,8 @@ const (
// +kubebuilder:object:root=true
// +kubebuilder:storageversion
// +kubebuilder:subresource:status
-// +kubebuilder:resource:scope=Namespaced,categories={vaultdynamicsecret},shortName=vaultdynamicsecret
+// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
+// +kubebuilder:resource:scope=Namespaced,categories={external-secrets, external-secrets-generators}
type VaultDynamicSecret struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
diff --git a/apis/generators/v1alpha1/generator_webhook.go b/apis/generators/v1alpha1/types_webhook.go
similarity index 78%
rename from apis/generators/v1alpha1/generator_webhook.go
rename to apis/generators/v1alpha1/types_webhook.go
index 5185704b931..c4fa8b4d02c 100644
--- a/apis/generators/v1alpha1/generator_webhook.go
+++ b/apis/generators/v1alpha1/types_webhook.go
@@ -73,14 +73,23 @@ type WebhookCAProvider struct {
Type WebhookCAProviderType `json:"type"`
// The name of the object located at the provider type.
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
Name string `json:"name"`
- // The key the value inside of the provider type to use, only used with "Secret" type
+ // The key where the CA certificate can be found in the Secret or ConfigMap.
// +kubebuilder:validation:Optional
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[-._a-zA-Z0-9]+$
Key string `json:"key,omitempty"`
// The namespace the Provider type is in.
// +optional
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=63
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
Namespace *string `json:"namespace,omitempty"`
}
@@ -100,8 +109,15 @@ type WebhookSecret struct {
type SecretKeySelector struct {
// The name of the Secret resource being referred to.
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
Name string `json:"name,omitempty"`
+
// The key where the token is found.
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[-._a-zA-Z0-9]+$
Key string `json:"key,omitempty"`
}
@@ -112,7 +128,8 @@ type SecretKeySelector struct {
// +kubebuilder:object:root=true
// +kubebuilder:storageversion
// +kubebuilder:subresource:status
-// +kubebuilder:resource:scope=Namespaced,categories={webhook},shortName=webhookl
+// +kubebuilder:metadata:labels="external-secrets.io/component=controller"
+// +kubebuilder:resource:scope=Namespaced,categories={external-secrets, external-secrets-generators}
type Webhook struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
diff --git a/apis/generators/v1alpha1/zz_generated.deepcopy.go b/apis/generators/v1alpha1/zz_generated.deepcopy.go
index b71166d4baf..c7809c0d82c 100644
--- a/apis/generators/v1alpha1/zz_generated.deepcopy.go
+++ b/apis/generators/v1alpha1/zz_generated.deepcopy.go
@@ -265,6 +265,80 @@ func (in *AzureACRWorkloadIdentityAuth) DeepCopy() *AzureACRWorkloadIdentityAuth
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterGenerator) DeepCopyInto(out *ClusterGenerator) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ in.Spec.DeepCopyInto(&out.Spec)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterGenerator.
+func (in *ClusterGenerator) DeepCopy() *ClusterGenerator {
+ if in == nil {
+ return nil
+ }
+ out := new(ClusterGenerator)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ClusterGenerator) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterGeneratorList) DeepCopyInto(out *ClusterGeneratorList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]ClusterGenerator, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterGeneratorList.
+func (in *ClusterGeneratorList) DeepCopy() *ClusterGeneratorList {
+ if in == nil {
+ return nil
+ }
+ out := new(ClusterGeneratorList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ClusterGeneratorList) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterGeneratorSpec) DeepCopyInto(out *ClusterGeneratorSpec) {
+ *out = *in
+ in.Generator.DeepCopyInto(&out.Generator)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterGeneratorSpec.
+func (in *ClusterGeneratorSpec) DeepCopy() *ClusterGeneratorSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(ClusterGeneratorSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ControllerClassResource) DeepCopyInto(out *ControllerClassResource) {
*out = *in
@@ -566,6 +640,76 @@ func (in *GCRAccessTokenSpec) DeepCopy() *GCRAccessTokenSpec {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *GeneratorSpec) DeepCopyInto(out *GeneratorSpec) {
+ *out = *in
+ if in.ACRAccessTokenSpec != nil {
+ in, out := &in.ACRAccessTokenSpec, &out.ACRAccessTokenSpec
+ *out = new(ACRAccessTokenSpec)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.ECRAuthorizationTokenSpec != nil {
+ in, out := &in.ECRAuthorizationTokenSpec, &out.ECRAuthorizationTokenSpec
+ *out = new(ECRAuthorizationTokenSpec)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.FakeSpec != nil {
+ in, out := &in.FakeSpec, &out.FakeSpec
+ *out = new(FakeSpec)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.GCRAccessTokenSpec != nil {
+ in, out := &in.GCRAccessTokenSpec, &out.GCRAccessTokenSpec
+ *out = new(GCRAccessTokenSpec)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.GithubAccessTokenSpec != nil {
+ in, out := &in.GithubAccessTokenSpec, &out.GithubAccessTokenSpec
+ *out = new(GithubAccessTokenSpec)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.QuayAccessTokenSpec != nil {
+ in, out := &in.QuayAccessTokenSpec, &out.QuayAccessTokenSpec
+ *out = new(QuayAccessTokenSpec)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.PasswordSpec != nil {
+ in, out := &in.PasswordSpec, &out.PasswordSpec
+ *out = new(PasswordSpec)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.STSSessionTokenSpec != nil {
+ in, out := &in.STSSessionTokenSpec, &out.STSSessionTokenSpec
+ *out = new(STSSessionTokenSpec)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.UUIDSpec != nil {
+ in, out := &in.UUIDSpec, &out.UUIDSpec
+ *out = new(UUIDSpec)
+ **out = **in
+ }
+ if in.VaultDynamicSecretSpec != nil {
+ in, out := &in.VaultDynamicSecretSpec, &out.VaultDynamicSecretSpec
+ *out = new(VaultDynamicSecretSpec)
+ (*in).DeepCopyInto(*out)
+ }
+ if in.WebhookSpec != nil {
+ in, out := &in.WebhookSpec, &out.WebhookSpec
+ *out = new(WebhookSpec)
+ (*in).DeepCopyInto(*out)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GeneratorSpec.
+func (in *GeneratorSpec) DeepCopy() *GeneratorSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(GeneratorSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GithubAccessToken) DeepCopyInto(out *GithubAccessToken) {
*out = *in
@@ -627,6 +771,18 @@ func (in *GithubAccessTokenList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GithubAccessTokenSpec) DeepCopyInto(out *GithubAccessTokenSpec) {
*out = *in
+ if in.Repositories != nil {
+ in, out := &in.Repositories, &out.Repositories
+ *out = make([]string, len(*in))
+ copy(*out, *in)
+ }
+ if in.Permissions != nil {
+ in, out := &in.Permissions, &out.Permissions
+ *out = make(map[string]string, len(*in))
+ for key, val := range *in {
+ (*out)[key] = val
+ }
+ }
in.Auth.DeepCopyInto(&out.Auth)
}
@@ -643,7 +799,7 @@ func (in *GithubAccessTokenSpec) DeepCopy() *GithubAccessTokenSpec {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *GithubAuth) DeepCopyInto(out *GithubAuth) {
*out = *in
- in.PrivatKey.DeepCopyInto(&out.PrivatKey)
+ in.PrivateKey.DeepCopyInto(&out.PrivateKey)
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GithubAuth.
@@ -760,6 +916,189 @@ func (in *PasswordSpec) DeepCopy() *PasswordSpec {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *QuayAccessToken) DeepCopyInto(out *QuayAccessToken) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ in.Spec.DeepCopyInto(&out.Spec)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QuayAccessToken.
+func (in *QuayAccessToken) DeepCopy() *QuayAccessToken {
+ if in == nil {
+ return nil
+ }
+ out := new(QuayAccessToken)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *QuayAccessToken) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *QuayAccessTokenList) DeepCopyInto(out *QuayAccessTokenList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]QuayAccessToken, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QuayAccessTokenList.
+func (in *QuayAccessTokenList) DeepCopy() *QuayAccessTokenList {
+ if in == nil {
+ return nil
+ }
+ out := new(QuayAccessTokenList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *QuayAccessTokenList) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *QuayAccessTokenSpec) DeepCopyInto(out *QuayAccessTokenSpec) {
+ *out = *in
+ in.ServiceAccountRef.DeepCopyInto(&out.ServiceAccountRef)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new QuayAccessTokenSpec.
+func (in *QuayAccessTokenSpec) DeepCopy() *QuayAccessTokenSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(QuayAccessTokenSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *RequestParameters) DeepCopyInto(out *RequestParameters) {
+ *out = *in
+ if in.SessionDuration != nil {
+ in, out := &in.SessionDuration, &out.SessionDuration
+ *out = new(int64)
+ **out = **in
+ }
+ if in.SerialNumber != nil {
+ in, out := &in.SerialNumber, &out.SerialNumber
+ *out = new(string)
+ **out = **in
+ }
+ if in.TokenCode != nil {
+ in, out := &in.TokenCode, &out.TokenCode
+ *out = new(string)
+ **out = **in
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RequestParameters.
+func (in *RequestParameters) DeepCopy() *RequestParameters {
+ if in == nil {
+ return nil
+ }
+ out := new(RequestParameters)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *STSSessionToken) DeepCopyInto(out *STSSessionToken) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ in.Spec.DeepCopyInto(&out.Spec)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new STSSessionToken.
+func (in *STSSessionToken) DeepCopy() *STSSessionToken {
+ if in == nil {
+ return nil
+ }
+ out := new(STSSessionToken)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *STSSessionToken) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *STSSessionTokenList) DeepCopyInto(out *STSSessionTokenList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]STSSessionToken, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new STSSessionTokenList.
+func (in *STSSessionTokenList) DeepCopy() *STSSessionTokenList {
+ if in == nil {
+ return nil
+ }
+ out := new(STSSessionTokenList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *STSSessionTokenList) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *STSSessionTokenSpec) DeepCopyInto(out *STSSessionTokenSpec) {
+ *out = *in
+ in.Auth.DeepCopyInto(&out.Auth)
+ if in.RequestParameters != nil {
+ in, out := &in.RequestParameters, &out.RequestParameters
+ *out = new(RequestParameters)
+ (*in).DeepCopyInto(*out)
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new STSSessionTokenSpec.
+func (in *STSSessionTokenSpec) DeepCopy() *STSSessionTokenSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(STSSessionTokenSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *SecretKeySelector) DeepCopyInto(out *SecretKeySelector) {
*out = *in
@@ -775,6 +1114,79 @@ func (in *SecretKeySelector) DeepCopy() *SecretKeySelector {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *UUID) DeepCopyInto(out *UUID) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+ out.Spec = in.Spec
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UUID.
+func (in *UUID) DeepCopy() *UUID {
+ if in == nil {
+ return nil
+ }
+ out := new(UUID)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *UUID) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *UUIDList) DeepCopyInto(out *UUIDList) {
+ *out = *in
+ out.TypeMeta = in.TypeMeta
+ in.ListMeta.DeepCopyInto(&out.ListMeta)
+ if in.Items != nil {
+ in, out := &in.Items, &out.Items
+ *out = make([]UUID, len(*in))
+ for i := range *in {
+ (*in)[i].DeepCopyInto(&(*out)[i])
+ }
+ }
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UUIDList.
+func (in *UUIDList) DeepCopy() *UUIDList {
+ if in == nil {
+ return nil
+ }
+ out := new(UUIDList)
+ in.DeepCopyInto(out)
+ return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *UUIDList) DeepCopyObject() runtime.Object {
+ if c := in.DeepCopy(); c != nil {
+ return c
+ }
+ return nil
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *UUIDSpec) DeepCopyInto(out *UUIDSpec) {
+ *out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new UUIDSpec.
+func (in *UUIDSpec) DeepCopy() *UUIDSpec {
+ if in == nil {
+ return nil
+ }
+ out := new(UUIDSpec)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *VaultDynamicSecret) DeepCopyInto(out *VaultDynamicSecret) {
*out = *in
@@ -841,6 +1253,11 @@ func (in *VaultDynamicSecretSpec) DeepCopyInto(out *VaultDynamicSecretSpec) {
*out = new(apiextensionsv1.JSON)
(*in).DeepCopyInto(*out)
}
+ if in.RetrySettings != nil {
+ in, out := &in.RetrySettings, &out.RetrySettings
+ *out = new(v1beta1.SecretStoreRetrySettings)
+ (*in).DeepCopyInto(*out)
+ }
if in.Provider != nil {
in, out := &in.Provider, &out.Provider
*out = new(v1beta1.VaultProvider)
diff --git a/apis/meta/v1/types.go b/apis/meta/v1/types.go
index 7ec5e35aafa..1134d0fd95b 100644
--- a/apis/meta/v1/types.go
+++ b/apis/meta/v1/types.go
@@ -14,29 +14,48 @@ limitations under the License.
package v1
-// A reference to a specific 'key' within a Secret resource,
+// A reference to a specific 'key' within a Secret resource.
// In some instances, `key` is a required field.
type SecretKeySelector struct {
// The name of the Secret resource being referred to.
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
Name string `json:"name,omitempty"`
- // Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- // to the namespace of the referent.
+
+ // The namespace of the Secret resource being referred to.
+ // Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
// +optional
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=63
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
Namespace *string `json:"namespace,omitempty"`
- // The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- // defaulted, in others it may be required.
+
+ // A key in the referenced Secret.
+ // Some instances of this field may be defaulted, in others it may be required.
// +optional
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[-._a-zA-Z0-9]+$
Key string `json:"key,omitempty"`
}
// A reference to a ServiceAccount resource.
type ServiceAccountSelector struct {
// The name of the ServiceAccount resource being referred to.
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=253
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
Name string `json:"name"`
- // Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- // to the namespace of the referent.
+
+ // Namespace of the resource being referred to.
+ // Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
// +optional
+ // +kubebuilder:validation:MinLength:=1
+ // +kubebuilder:validation:MaxLength:=63
+ // +kubebuilder:validation:Pattern:=^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
Namespace *string `json:"namespace,omitempty"`
+
// Audience specifies the `aud` claim for the service account token
// If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
// then this audiences will be appended to the list
diff --git a/assets/ESI_Logo.svg b/assets/ESI_Logo.svg
new file mode 100755
index 00000000000..94886090400
--- /dev/null
+++ b/assets/ESI_Logo.svg
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/cmd/certcontroller.go b/cmd/controller/certcontroller.go
similarity index 62%
rename from cmd/certcontroller.go
rename to cmd/controller/certcontroller.go
index c87bd5f9f99..26d30412293 100644
--- a/cmd/certcontroller.go
+++ b/cmd/controller/certcontroller.go
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-package cmd
+package controller
import (
"os"
@@ -22,14 +22,20 @@ import (
"github.com/spf13/cobra"
"go.uber.org/zap/zapcore"
+ admissionregistration "k8s.io/api/admissionregistration/v1"
v1 "k8s.io/api/core/v1"
+ apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ "k8s.io/apimachinery/pkg/labels"
ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook"
+ "github.com/external-secrets/external-secrets/pkg/constants"
+ ctrlcommon "github.com/external-secrets/external-secrets/pkg/controllers/common"
"github.com/external-secrets/external-secrets/pkg/controllers/crds"
"github.com/external-secrets/external-secrets/pkg/controllers/webhookconfig"
)
@@ -40,24 +46,31 @@ var certcontrollerCmd = &cobra.Command{
Long: `Controller to manage certificates for external secrets CRDs and ValidatingWebhookConfigs.
For more information visit https://external-secrets.io`,
Run: func(cmd *cobra.Command, args []string) {
- var lvl zapcore.Level
- var enc zapcore.TimeEncoder
- lvlErr := lvl.UnmarshalText([]byte(loglevel))
- if lvlErr != nil {
- setupLog.Error(lvlErr, "error unmarshalling loglevel")
- os.Exit(1)
- }
- encErr := enc.UnmarshalText([]byte(zapTimeEncoding))
- if encErr != nil {
- setupLog.Error(encErr, "error unmarshalling timeEncoding")
- os.Exit(1)
- }
- opts := zap.Options{
- Level: lvl,
- TimeEncoder: enc,
+ setupLogger()
+
+ // completely disable caching of Secrets and ConfigMaps to save memory
+ // see: https://github.com/external-secrets/external-secrets/issues/721
+ clientCacheDisableFor := make([]client.Object, 0)
+ clientCacheDisableFor = append(clientCacheDisableFor, &v1.Secret{}, &v1.ConfigMap{})
+
+ // in large clusters, the CRDs and ValidatingWebhookConfigurations can take up a lot of memory
+ // see: https://github.com/external-secrets/external-secrets/pull/3588
+ cacheByObject := make(map[client.Object]cache.ByObject)
+ if enablePartialCache {
+ // only cache ValidatingWebhookConfiguration with "external-secrets.io/component=webhook" label
+ cacheByObject[&admissionregistration.ValidatingWebhookConfiguration{}] = cache.ByObject{
+ Label: labels.SelectorFromSet(labels.Set{
+ constants.WellKnownLabelKey: constants.WellKnownLabelValueWebhook,
+ }),
+ }
+
+ // only cache CustomResourceDefinition with "external-secrets.io/component=controller" label
+ cacheByObject[&apiextensions.CustomResourceDefinition{}] = cache.ByObject{
+ Label: labels.SelectorFromSet(labels.Set{
+ constants.WellKnownLabelKey: constants.WellKnownLabelValueController,
+ }),
+ }
}
- logger := zap.New(zap.UseFlagOptions(&opts))
- ctrl.SetLogger(logger)
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
Scheme: scheme,
@@ -70,17 +83,12 @@ var certcontrollerCmd = &cobra.Command{
HealthProbeBindAddress: healthzAddr,
LeaderElection: enableLeaderElection,
LeaderElectionID: "crd-certs-controller",
+ Cache: cache.Options{
+ ByObject: cacheByObject,
+ },
Client: client.Options{
Cache: &client.CacheOptions{
- DisableFor: []client.Object{
- // the client creates a ListWatch for all resource kinds that
- // are requested with .Get().
- // We want to avoid to cache all secrets or configmaps in memory.
- // The ES controller uses v1.PartialObjectMetadata for the secrets
- // that he owns.
- // see #721
- &v1.Secret{},
- },
+ DisableFor: clientCacheDisableFor,
},
},
})
@@ -91,9 +99,17 @@ var certcontrollerCmd = &cobra.Command{
crdctrl := crds.New(mgr.GetClient(), mgr.GetScheme(), mgr.Elected(),
ctrl.Log.WithName("controllers").WithName("webhook-certs-updater"),
- crdRequeueInterval, serviceName, serviceNamespace, secretName, secretNamespace, crdNames)
+ crdRequeueInterval,
+ crds.Opts{
+ SvcName: serviceName,
+ SvcNamespace: serviceNamespace,
+ SecretName: secretName,
+ SecretNamespace: secretNamespace,
+ Resources: crdNames,
+ })
if err := crdctrl.SetupWithManager(mgr, controller.Options{
MaxConcurrentReconciles: concurrent,
+ RateLimiter: ctrlcommon.BuildRateLimiter(),
}); err != nil {
setupLog.Error(err, errCreateController, "controller", "CustomResourceDefinition")
os.Exit(1)
@@ -101,10 +117,16 @@ var certcontrollerCmd = &cobra.Command{
whc := webhookconfig.New(mgr.GetClient(), mgr.GetScheme(), mgr.Elected(),
ctrl.Log.WithName("controllers").WithName("webhook-certs-updater"),
- serviceName, serviceNamespace,
- secretName, secretNamespace, crdRequeueInterval)
+ webhookconfig.Opts{
+ SvcName: serviceName,
+ SvcNamespace: serviceNamespace,
+ SecretName: secretName,
+ SecretNamespace: secretNamespace,
+ RequeueInterval: crdRequeueInterval,
+ })
if err := whc.SetupWithManager(mgr, controller.Options{
MaxConcurrentReconciles: concurrent,
+ RateLimiter: ctrlcommon.BuildRateLimiter(),
}); err != nil {
setupLog.Error(err, errCreateController, "controller", "WebhookConfig")
os.Exit(1)
@@ -129,6 +151,27 @@ var certcontrollerCmd = &cobra.Command{
},
}
+func setupLogger() {
+ var lvl zapcore.Level
+ var enc zapcore.TimeEncoder
+ lvlErr := lvl.UnmarshalText([]byte(loglevel))
+ if lvlErr != nil {
+ setupLog.Error(lvlErr, "error unmarshalling loglevel")
+ os.Exit(1)
+ }
+ encErr := enc.UnmarshalText([]byte(zapTimeEncoding))
+ if encErr != nil {
+ setupLog.Error(encErr, "error unmarshalling timeEncoding")
+ os.Exit(1)
+ }
+ opts := zap.Options{
+ Level: lvl,
+ TimeEncoder: enc,
+ }
+ logger := zap.New(zap.UseFlagOptions(&opts))
+ ctrl.SetLogger(logger)
+}
+
func init() {
rootCmd.AddCommand(certcontrollerCmd)
@@ -139,6 +182,8 @@ func init() {
certcontrollerCmd.Flags().StringVar(&secretName, "secret-name", "external-secrets-webhook", "Secret to store certs for webhook")
certcontrollerCmd.Flags().StringVar(&secretNamespace, "secret-namespace", "default", "namespace of the secret to store certs")
certcontrollerCmd.Flags().StringSliceVar(&crdNames, "crd-names", []string{"externalsecrets.external-secrets.io", "clustersecretstores.external-secrets.io", "secretstores.external-secrets.io"}, "CRD names reconciled by the controller")
+ certcontrollerCmd.Flags().BoolVar(&enablePartialCache, "enable-partial-cache", false,
+ "Enable caching of only the relevant CRDs and Webhook configurations in the Informer to improve memory efficiency")
certcontrollerCmd.Flags().BoolVar(&enableLeaderElection, "enable-leader-election", false,
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
diff --git a/cmd/root.go b/cmd/controller/root.go
similarity index 77%
rename from cmd/root.go
rename to cmd/controller/root.go
index 5a12549f951..8fa6c8dd16a 100644
--- a/cmd/root.go
+++ b/cmd/controller/root.go
@@ -14,23 +14,22 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-package cmd
+package controller
import (
"os"
"time"
"github.com/spf13/cobra"
- "go.uber.org/zap/zapcore"
v1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"k8s.io/apimachinery/pkg/runtime"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/cache"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
- "sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook"
@@ -39,6 +38,7 @@ import (
genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
"github.com/external-secrets/external-secrets/pkg/controllers/clusterexternalsecret"
"github.com/external-secrets/external-secrets/pkg/controllers/clusterexternalsecret/cesmetrics"
+ ctrlcommon "github.com/external-secrets/external-secrets/pkg/controllers/common"
"github.com/external-secrets/external-secrets/pkg/controllers/externalsecret"
"github.com/external-secrets/external-secrets/pkg/controllers/externalsecret/esmetrics"
ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
@@ -64,6 +64,8 @@ var (
enableLeaderElection bool
enableSecretsCache bool
enableConfigMapsCache bool
+ enableManagedSecretsCache bool
+ enablePartialCache bool
concurrent int
port int
clientQPS float32
@@ -92,11 +94,14 @@ const (
)
func init() {
- _ = clientgoscheme.AddToScheme(scheme)
- _ = esv1beta1.AddToScheme(scheme)
- _ = esv1alpha1.AddToScheme(scheme)
- _ = genv1alpha1.AddToScheme(scheme)
- _ = apiextensionsv1.AddToScheme(scheme)
+ // kubernetes schemes
+ utilruntime.Must(clientgoscheme.AddToScheme(scheme))
+ utilruntime.Must(apiextensionsv1.AddToScheme(scheme))
+
+ // external-secrets schemes
+ utilruntime.Must(esv1beta1.AddToScheme(scheme))
+ utilruntime.Must(esv1alpha1.AddToScheme(scheme))
+ utilruntime.Must(genv1alpha1.AddToScheme(scheme))
}
var rootCmd = &cobra.Command{
@@ -104,42 +109,28 @@ var rootCmd = &cobra.Command{
Short: "operator that reconciles ExternalSecrets and SecretStores",
Long: `For more information visit https://external-secrets.io`,
Run: func(cmd *cobra.Command, args []string) {
- var lvl zapcore.Level
- var enc zapcore.TimeEncoder
- // the client creates a ListWatch for all resource kinds that
- // are requested with .Get().
- // We want to avoid to cache all secrets or configmaps in memory.
- // The ES controller uses v1.PartialObjectMetadata for the secrets
- // that he owns.
- // see #721
- cacheList := make([]client.Object, 0)
- if !enableSecretsCache {
- cacheList = append(cacheList, &v1.Secret{})
- }
- if !enableConfigMapsCache {
- cacheList = append(cacheList, &v1.ConfigMap{})
- }
- lvlErr := lvl.UnmarshalText([]byte(loglevel))
- if lvlErr != nil {
- setupLog.Error(lvlErr, "error unmarshalling loglevel")
- os.Exit(1)
- }
- encErr := enc.UnmarshalText([]byte(zapTimeEncoding))
- if encErr != nil {
- setupLog.Error(encErr, "error unmarshalling timeEncoding")
- os.Exit(1)
- }
- opts := zap.Options{
- Level: lvl,
- TimeEncoder: enc,
- }
- logger := zap.New(zap.UseFlagOptions(&opts))
- ctrl.SetLogger(logger)
+ setupLogger()
+
ctrlmetrics.SetUpLabelNames(enableExtendedMetricLabels)
esmetrics.SetUpMetrics()
config := ctrl.GetConfigOrDie()
config.QPS = clientQPS
config.Burst = clientBurst
+
+ // the client creates a ListWatch for resources that are requested with .Get() or .List()
+ // some users might want to completely disable caching of Secrets and ConfigMaps
+ // to decrease memory usage at the expense of high Kubernetes API usage
+ // see: https://github.com/external-secrets/external-secrets/issues/721
+ clientCacheDisableFor := make([]client.Object, 0)
+ if !enableSecretsCache {
+ // dont cache any secrets
+ clientCacheDisableFor = append(clientCacheDisableFor, &v1.Secret{})
+ }
+ if !enableConfigMapsCache {
+ // dont cache any configmaps
+ clientCacheDisableFor = append(clientCacheDisableFor, &v1.ConfigMap{})
+ }
+
ctrlOpts := ctrl.Options{
Scheme: scheme,
Metrics: server.Options{
@@ -150,7 +141,7 @@ var rootCmd = &cobra.Command{
}),
Client: client.Options{
Cache: &client.CacheOptions{
- DisableFor: cacheList,
+ DisableFor: clientCacheDisableFor,
},
},
LeaderElection: enableLeaderElection,
@@ -167,6 +158,19 @@ var rootCmd = &cobra.Command{
os.Exit(1)
}
+ // we create a special client for accessing secrets in the ExternalSecret reconcile loop.
+ // by default, it is the same as the normal client, but if `--enable-managed-secrets-caching`
+ // is set, we use a special client that only caches secrets managed by an ExternalSecret.
+ // if we are already caching all secrets, we don't need to use the special client.
+ secretClient := mgr.GetClient()
+ if enableManagedSecretsCache && !enableSecretsCache {
+ secretClient, err = ctrlcommon.BuildManagedSecretClient(mgr)
+ if err != nil {
+ setupLog.Error(err, "unable to create managed secret client")
+ os.Exit(1)
+ }
+ }
+
ssmetrics.SetUpMetrics()
if err = (&secretstore.StoreReconciler{
Client: mgr.GetClient(),
@@ -176,6 +180,7 @@ var rootCmd = &cobra.Command{
RequeueInterval: storeRequeueInterval,
}).SetupWithManager(mgr, controller.Options{
MaxConcurrentReconciles: concurrent,
+ RateLimiter: ctrlcommon.BuildRateLimiter(),
}); err != nil {
setupLog.Error(err, errCreateController, "controller", "SecretStore")
os.Exit(1)
@@ -188,13 +193,17 @@ var rootCmd = &cobra.Command{
Scheme: mgr.GetScheme(),
ControllerClass: controllerClass,
RequeueInterval: storeRequeueInterval,
- }).SetupWithManager(mgr); err != nil {
+ }).SetupWithManager(mgr, controller.Options{
+ MaxConcurrentReconciles: concurrent,
+ RateLimiter: ctrlcommon.BuildRateLimiter(),
+ }); err != nil {
setupLog.Error(err, errCreateController, "controller", "ClusterSecretStore")
os.Exit(1)
}
}
if err = (&externalsecret.Reconciler{
Client: mgr.GetClient(),
+ SecretClient: secretClient,
Log: ctrl.Log.WithName("controllers").WithName("ExternalSecret"),
Scheme: mgr.GetScheme(),
RestConfig: mgr.GetConfig(),
@@ -204,6 +213,7 @@ var rootCmd = &cobra.Command{
EnableFloodGate: enableFloodGate,
}).SetupWithManager(mgr, controller.Options{
MaxConcurrentReconciles: concurrent,
+ RateLimiter: ctrlcommon.BuildRateLimiter(),
}); err != nil {
setupLog.Error(err, errCreateController, "controller", "ExternalSecret")
os.Exit(1)
@@ -215,8 +225,12 @@ var rootCmd = &cobra.Command{
Log: ctrl.Log.WithName("controllers").WithName("PushSecret"),
Scheme: mgr.GetScheme(),
ControllerClass: controllerClass,
+ RestConfig: mgr.GetConfig(),
RequeueInterval: time.Hour,
- }).SetupWithManager(mgr); err != nil {
+ }).SetupWithManager(mgr, controller.Options{
+ MaxConcurrentReconciles: concurrent,
+ RateLimiter: ctrlcommon.BuildRateLimiter(),
+ }); err != nil {
setupLog.Error(err, errCreateController, "controller", "PushSecret")
os.Exit(1)
}
@@ -231,6 +245,7 @@ var rootCmd = &cobra.Command{
RequeueInterval: time.Hour,
}).SetupWithManager(mgr, controller.Options{
MaxConcurrentReconciles: concurrent,
+ RateLimiter: ctrlcommon.BuildRateLimiter(),
}); err != nil {
setupLog.Error(err, errCreateController, "controller", "ClusterExternalSecret")
os.Exit(1)
@@ -263,16 +278,17 @@ func init() {
"Enable leader election for controller manager. "+
"Enabling this will ensure there is only one active controller manager.")
rootCmd.Flags().IntVar(&concurrent, "concurrent", 1, "The number of concurrent reconciles.")
- rootCmd.Flags().Float32Var(&clientQPS, "client-qps", 0, "QPS configuration to be passed to rest.Client")
- rootCmd.Flags().IntVar(&clientBurst, "client-burst", 0, "Maximum Burst allowed to be passed to rest.Client")
+ rootCmd.Flags().Float32Var(&clientQPS, "client-qps", 50, "QPS configuration to be passed to rest.Client")
+ rootCmd.Flags().IntVar(&clientBurst, "client-burst", 100, "Maximum Burst allowed to be passed to rest.Client")
rootCmd.Flags().StringVar(&loglevel, "loglevel", "info", "loglevel to use, one of: debug, info, warn, error, dpanic, panic, fatal")
rootCmd.Flags().StringVar(&zapTimeEncoding, "zap-time-encoding", "epoch", "Zap time encoding (one of 'epoch', 'millis', 'nano', 'iso8601', 'rfc3339' or 'rfc3339nano')")
rootCmd.Flags().StringVar(&namespace, "namespace", "", "watch external secrets scoped in the provided namespace only. ClusterSecretStore can be used but only work if it doesn't reference resources from other namespaces")
rootCmd.Flags().BoolVar(&enableClusterStoreReconciler, "enable-cluster-store-reconciler", true, "Enable cluster store reconciler.")
rootCmd.Flags().BoolVar(&enableClusterExternalSecretReconciler, "enable-cluster-external-secret-reconciler", true, "Enable cluster external secret reconciler.")
rootCmd.Flags().BoolVar(&enablePushSecretReconciler, "enable-push-secret-reconciler", true, "Enable push secret reconciler.")
- rootCmd.Flags().BoolVar(&enableSecretsCache, "enable-secrets-caching", false, "Enable secrets caching for external-secrets pod.")
- rootCmd.Flags().BoolVar(&enableConfigMapsCache, "enable-configmaps-caching", false, "Enable secrets caching for external-secrets pod.")
+ rootCmd.Flags().BoolVar(&enableSecretsCache, "enable-secrets-caching", false, "Enable secrets caching for ALL secrets in the cluster (WARNING: can increase memory usage).")
+ rootCmd.Flags().BoolVar(&enableConfigMapsCache, "enable-configmaps-caching", false, "Enable configmaps caching for ALL configmaps in the cluster (WARNING: can increase memory usage).")
+ rootCmd.Flags().BoolVar(&enableManagedSecretsCache, "enable-managed-secrets-caching", true, "Enable secrets caching for secrets managed by an ExternalSecret")
rootCmd.Flags().DurationVar(&storeRequeueInterval, "store-requeue-interval", time.Minute*5, "Default Time duration between reconciling (Cluster)SecretStores")
rootCmd.Flags().BoolVar(&enableFloodGate, "enable-flood-gate", true, "Enable flood gate. External secret will be reconciled only if the ClusterStore or Store have an healthy or unknown state.")
rootCmd.Flags().BoolVar(&enableExtendedMetricLabels, "enable-extended-metric-labels", false, "Enable recommended kubernetes annotations as labels in metrics.")
diff --git a/cmd/webhook.go b/cmd/controller/webhook.go
similarity index 90%
rename from cmd/webhook.go
rename to cmd/controller/webhook.go
index 9f78baa3cfe..be1067dc5af 100644
--- a/cmd/webhook.go
+++ b/cmd/controller/webhook.go
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
-package cmd
+package controller
import (
"context"
@@ -28,10 +28,9 @@ import (
"time"
"github.com/spf13/cobra"
- "go.uber.org/zap/zapcore"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
ctrl "sigs.k8s.io/controller-runtime"
- "sigs.k8s.io/controller-runtime/pkg/log/zap"
"sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook"
@@ -45,9 +44,12 @@ const (
)
func init() {
- _ = clientgoscheme.AddToScheme(scheme)
- _ = esv1beta1.AddToScheme(scheme)
- _ = esv1alpha1.AddToScheme(scheme)
+ // kubernetes schemes
+ utilruntime.Must(clientgoscheme.AddToScheme(scheme))
+
+ // external-secrets schemes
+ utilruntime.Must(esv1beta1.AddToScheme(scheme))
+ utilruntime.Must(esv1alpha1.AddToScheme(scheme))
}
var webhookCmd = &cobra.Command{
@@ -56,31 +58,14 @@ var webhookCmd = &cobra.Command{
Long: `Webhook implementation for ExternalSecrets and SecretStores.
For more information visit https://external-secrets.io`,
Run: func(cmd *cobra.Command, args []string) {
- var lvl zapcore.Level
- var enc zapcore.TimeEncoder
+ setupLogger()
+
c := crds.CertInfo{
CertDir: certDir,
CertName: "tls.crt",
KeyName: "tls.key",
CAName: "ca.crt",
}
- lvlErr := lvl.UnmarshalText([]byte(loglevel))
- if lvlErr != nil {
- setupLog.Error(lvlErr, "error unmarshalling loglevel")
- os.Exit(1)
- }
- encErr := enc.UnmarshalText([]byte(zapTimeEncoding))
- if encErr != nil {
- setupLog.Error(encErr, "error unmarshalling timeEncoding")
- os.Exit(1)
- }
- opts := zap.Options{
- Level: lvl,
- TimeEncoder: enc,
- }
- logger := zap.New(zap.UseFlagOptions(&opts))
- ctrl.SetLogger(logger)
-
err := waitForCerts(c, time.Minute*2)
if err != nil {
setupLog.Error(err, "unable to validate certificates")
@@ -211,10 +196,10 @@ func waitForCerts(c crds.CertInfo, timeout time.Duration) error {
if err == nil {
return nil
}
- if err != nil {
- setupLog.Error(err, "invalid certs. retrying...")
- <-time.After(time.Second * 10)
- }
+
+ setupLog.Error(err, "invalid certs. retrying...")
+ <-time.After(time.Second * 10)
+
if ctx.Err() != nil {
return ctx.Err()
}
diff --git a/cmd/esoctl/.goreleaser.yaml b/cmd/esoctl/.goreleaser.yaml
new file mode 100644
index 00000000000..78f074739aa
--- /dev/null
+++ b/cmd/esoctl/.goreleaser.yaml
@@ -0,0 +1,33 @@
+version: 2
+builds:
+ - id: default
+ binary: esoctl
+ flags:
+ - -tags
+ - netgo release
+ - -trimpath
+ env:
+ - CGO_ENABLED=0
+ goos:
+ - linux
+ - darwin
+ - windows
+ goarch:
+ - amd64
+
+archives:
+ - id: default
+ builds:
+ - default
+ name_template: "esoctl_{{ .Os }}_{{ .Arch }}{{ if .Arm }}v{{ .Arm }}{{ end }}"
+ format: tar.gz
+
+signs:
+ - artifacts: checksum
+ args: ["--batch", "-u", "{{ .Env.GPG_FINGERPRINT }}", "--output", "${signature}", "--detach-sign", "${artifact}"]
+
+sboms:
+ - artifacts: archive
+
+checksum:
+ name_template: "esoctl_checksums.txt"
diff --git a/cmd/esoctl/Makefile b/cmd/esoctl/Makefile
new file mode 100644
index 00000000000..a70500daa0b
--- /dev/null
+++ b/cmd/esoctl/Makefile
@@ -0,0 +1,35 @@
+# set the shell to bash always
+SHELL := /bin/bash
+
+# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
+ifeq (,$(shell go env GOBIN))
+GOBIN=$(shell go env GOPATH)/bin
+else
+GOBIN=$(shell go env GOBIN)
+endif
+
+# check if there are any existing `git tag` values
+ifeq ($(shell git tag),)
+# no tags found - default to initial tag `v0.0.0`
+export VERSION := $(shell echo "v0.0.0-$$(git rev-list HEAD --count)-g$$(git describe --dirty --always)" | sed 's/-/./2' | sed 's/-/./2')
+else
+# use tags
+export VERSION := $(shell git describe --dirty --always --tags --exclude 'helm*' | sed 's/-/./2' | sed 's/-/./2')
+endif
+
+## Location to install dependencies to
+LOCALBIN ?= $(shell pwd)/bin
+$(LOCALBIN):
+ mkdir -p $(LOCALBIN)
+
+.PHONY: build
+build: ## Build binary for the specified arch
+ go build -o '$(LOCALBIN)/esoctl' -trimpath -ldflags="-s -w -X 'main.version=$(VERSION)'" .
+
+.PHONY: binaries
+binaries: ## Build release binaries for all major OSs.
+ @rm -fr dist
+ @mkdir -p dist
+ GOOS=linux GOARCH=amd64 go build -o dist/esoctl-linux-amd64 -trimpath -ldflags="-s -w -X 'main.version=$(VERSION)'" .
+ GOOS=darwin GOARCH=amd64 go build -o dist/esoctl-darwin-amd64 -trimpath -ldflags="-s -w -X 'main.version=$(VERSION)'" .
+ GOOS=windows GOARCH=amd64 go build -o dist/esoctl-windows-amd64.exe -trimpath -ldflags="-s -w -X 'main.version=$(VERSION)'" .
diff --git a/cmd/esoctl/README.md b/cmd/esoctl/README.md
new file mode 100644
index 00000000000..070e1278544
--- /dev/null
+++ b/cmd/esoctl/README.md
@@ -0,0 +1,14 @@
+# esoctl
+
+This tool contains external-secrets-operator related activities and helpers.
+
+## Templates
+
+`cmd/esoctl` -> `esoctl template`
+
+The purpose is to give users the ability to rapidly test and iterate on templates in a PushSecret/ExternalSecret.
+
+For a more in-dept description read [Using esoctl Tool](../../docs/guides/using-esoctl-tool.md).
+
+This project doesn't have its own go mod files to allow it to grow together with ESO instead of waiting for new ESO
+releases to import it.
diff --git a/cmd/esoctl/main.go b/cmd/esoctl/main.go
new file mode 100644
index 00000000000..26b21b59275
--- /dev/null
+++ b/cmd/esoctl/main.go
@@ -0,0 +1,29 @@
+/*
+Copyright © 2025 ESO Maintainer team
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package main
+
+import (
+ "fmt"
+ "os"
+)
+
+func main() {
+ if err := rootCmd.Execute(); err != nil {
+ _, _ = fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+}
diff --git a/cmd/esoctl/root.go b/cmd/esoctl/root.go
new file mode 100644
index 00000000000..8d123f71d5e
--- /dev/null
+++ b/cmd/esoctl/root.go
@@ -0,0 +1,28 @@
+/*
+Copyright © 2025 ESO Maintainer team
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package main
+
+import "github.com/spf13/cobra"
+
+var rootCmd = &cobra.Command{
+ Use: "esoctl",
+ Short: "operations for external-secrets-operator",
+ Long: `For more information visit https://external-secrets.io`,
+ Run: func(cmd *cobra.Command, args []string) {
+ _ = cmd.Usage()
+ },
+}
diff --git a/cmd/esoctl/template.go b/cmd/esoctl/template.go
new file mode 100644
index 00000000000..a23ff4d0e63
--- /dev/null
+++ b/cmd/esoctl/template.go
@@ -0,0 +1,230 @@
+/*
+Copyright © 2025 ESO Maintainer team
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package main
+
+import (
+ "context"
+ "fmt"
+ "os"
+
+ "github.com/spf13/cobra"
+ corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
+ "sigs.k8s.io/yaml"
+
+ "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ "github.com/external-secrets/external-secrets/pkg/controllers/templating"
+ "github.com/external-secrets/external-secrets/pkg/template"
+)
+
+// version is filled during build time.
+var version string
+
+var (
+ templateFile string
+ secretDataFile string
+ outputFile string
+ templateFromConfigMapFile string
+ templateFromSecretFile string
+ showVersion bool
+)
+
+func init() {
+ rootCmd.AddCommand(templateCmd)
+ templateCmd.Flags().StringVar(&templateFile, "source-templated-object", "", "Link to a file containing the object that contains the template")
+ templateCmd.Flags().StringVar(&secretDataFile, "source-secret-data-file", "", "Link to a file containing secret data in form of map[string][]byte")
+ templateCmd.Flags().StringVar(&templateFromConfigMapFile, "template-from-config-map", "", "Link to a file containing config map data for TemplateFrom.ConfigMap")
+ templateCmd.Flags().StringVar(&templateFromSecretFile, "template-from-secret", "", "Link to a file containing config map data for TemplateFrom.Secret")
+ templateCmd.Flags().StringVar(&outputFile, "output", "", "If set, the output will be written to this file")
+ templateCmd.Flags().BoolVar(&showVersion, "version", false, "If set, only print the version and exit")
+}
+
+var templateCmd = &cobra.Command{
+ Use: "template",
+ Short: "given an input and a template provides an output",
+ Long: `Given an input that mimics a secret's data section and a template it produces an output of the render template.`,
+ RunE: templateRun,
+}
+
+func templateRun(_ *cobra.Command, _ []string) error {
+ if version == "" {
+ version = "0.0.0-dev"
+ }
+
+ if showVersion {
+ fmt.Printf("version: %s\n", version)
+
+ os.Exit(0)
+ }
+
+ ctx := context.Background()
+ obj := &unstructured.Unstructured{}
+ content, err := os.ReadFile(templateFile)
+ if err != nil {
+ return fmt.Errorf("could not read template file: %w", err)
+ }
+
+ if err := yaml.Unmarshal(content, obj); err != nil {
+ return fmt.Errorf("could not unmarshal template: %w", err)
+ }
+
+ tmpl, err := fetchTemplateFromSourceObject(obj)
+ if err != nil {
+ return err
+ }
+
+ data := map[string][]byte{}
+ sourceDataContent, err := os.ReadFile(secretDataFile)
+ if err != nil {
+ return fmt.Errorf("could not read source secret file: %w", err)
+ }
+
+ if err := yaml.Unmarshal(sourceDataContent, &data); err != nil {
+ return fmt.Errorf("could not unmarshal secret: %w", err)
+ }
+
+ execute, err := template.EngineForVersion(tmpl.EngineVersion)
+ if err != nil {
+ return err
+ }
+
+ targetSecret := &corev1.Secret{}
+ p := &templating.Parser{
+ TargetSecret: targetSecret,
+ DataMap: data,
+ Exec: execute,
+ }
+
+ if err := setupFromConfigAndFromSecret(p); err != nil {
+ return fmt.Errorf("could not setup from secret: %w", err)
+ }
+
+ if err := executeTemplate(p, ctx, tmpl); err != nil {
+ return fmt.Errorf("could not render template: %w", err)
+ }
+
+ out := os.Stdout
+ if outputFile != "" {
+ f, err := os.Create(outputFile)
+ if err != nil {
+ return fmt.Errorf("could not create output file: %w", err)
+ }
+ defer func() {
+ _ = f.Close()
+ }()
+
+ out = f
+ }
+
+ // display the resulting secret
+ content, err = yaml.Marshal(targetSecret)
+ if err != nil {
+ return fmt.Errorf("could not marshal secret: %w", err)
+ }
+
+ _, err = fmt.Fprintln(out, string(content))
+
+ return err
+}
+
+func fetchTemplateFromSourceObject(obj *unstructured.Unstructured) (*esv1beta1.ExternalSecretTemplate, error) {
+ var tmpl *esv1beta1.ExternalSecretTemplate
+ switch obj.GetKind() {
+ case "ExternalSecret":
+ es := &esv1beta1.ExternalSecret{}
+ if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, es); err != nil {
+ return nil, err
+ }
+
+ tmpl = es.Spec.Target.Template
+ case "PushSecret":
+ ps := &v1alpha1.PushSecret{}
+ if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, ps); err != nil {
+ return nil, err
+ }
+
+ tmpl = ps.Spec.Template
+ default:
+ return nil, fmt.Errorf("unsupported template kind %s", obj.GetKind())
+ }
+
+ return tmpl, nil
+}
+
+func executeTemplate(p *templating.Parser, ctx context.Context, tmpl *esv1beta1.ExternalSecretTemplate) error {
+ // apply templates defined in template.templateFrom
+ err := p.MergeTemplateFrom(ctx, "default", tmpl)
+ if err != nil {
+ return fmt.Errorf("could not merge template: %w", err)
+ }
+
+ // apply data templates
+ // NOTE: explicitly defined template.data templates take precedence over templateFrom
+ err = p.MergeMap(tmpl.Data, esv1beta1.TemplateTargetData)
+ if err != nil {
+ return fmt.Errorf("could not merge data: %w", err)
+ }
+
+ // apply templates for labels
+ // NOTE: this only works for v2 templates
+ err = p.MergeMap(tmpl.Metadata.Labels, esv1beta1.TemplateTargetLabels)
+ if err != nil {
+ return fmt.Errorf("could not merge labels: %w", err)
+ }
+
+ // apply template for annotations
+ // NOTE: this only works for v2 templates
+ err = p.MergeMap(tmpl.Metadata.Annotations, esv1beta1.TemplateTargetAnnotations)
+ if err != nil {
+ return fmt.Errorf("could not merge annotations: %w", err)
+ }
+
+ return err
+}
+
+func setupFromConfigAndFromSecret(p *templating.Parser) error {
+ if templateFromConfigMapFile != "" {
+ var configMap corev1.ConfigMap
+ configMapContent, err := os.ReadFile(templateFromConfigMapFile)
+ if err != nil {
+ return err
+ }
+
+ if err := yaml.Unmarshal(configMapContent, &configMap); err != nil {
+ return fmt.Errorf("could not unmarshal configmap: %w", err)
+ }
+
+ p.TemplateFromConfigMap = &configMap
+ }
+
+ if templateFromSecretFile != "" {
+ var secret corev1.Secret
+ secretContent, err := os.ReadFile(templateFromSecretFile)
+ if err != nil {
+ return err
+ }
+
+ if err := yaml.Unmarshal(secretContent, &secret); err != nil {
+ return fmt.Errorf("could not unmarshal secret: %w", err)
+ }
+
+ p.TemplateFromSecret = &secret
+ }
+ return nil
+}
diff --git a/config/crds/bases/external-secrets.io_clusterexternalsecrets.yaml b/config/crds/bases/external-secrets.io_clusterexternalsecrets.yaml
index 9445a9feb06..b8b11f9ede1 100644
--- a/config/crds/bases/external-secrets.io_clusterexternalsecrets.yaml
+++ b/config/crds/bases/external-secrets.io_clusterexternalsecrets.yaml
@@ -2,13 +2,15 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: clusterexternalsecrets.external-secrets.io
spec:
group: external-secrets.io
names:
categories:
- - externalsecrets
+ - external-secrets
kind: ClusterExternalSecret
listKind: ClusterExternalSecretList
plural: clusterexternalsecrets
@@ -66,8 +68,12 @@ spec:
type: object
type: object
externalSecretName:
- description: The name of the external secrets to be created defaults
- to the name of the ClusterExternalSecret
+ description: |-
+ The name of the external secrets to be created.
+ Defaults to the name of the ClusterExternalSecret
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
externalSecretSpec:
description: The spec for the ExternalSecrets to be created
@@ -125,21 +131,23 @@ spec:
- key
type: object
secretKey:
- description: |-
- SecretKey defines the key in which the controller stores
- the value. This is the key in the Kind=Secret
+ description: The key in the Kubernetes Secret to store the
+ value.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
sourceRef:
description: |-
SourceRef allows you to override the source
- from which the value will pulled from.
+ from which the value will be pulled.
maxProperties: 1
+ minProperties: 1
properties:
generatorRef:
description: |-
GeneratorRef points to a generator custom resource.
-
Deprecated: The generatorRef is not implemented in .data[].
this will be removed with v1.
properties:
@@ -149,11 +157,26 @@ spec:
resource
type: string
kind:
- description: Specify the Kind of the resource, e.g.
- Password, ACRAccessToken etc.
+ description: Specify the Kind of the generator resource
+ enum:
+ - ACRAccessToken
+ - ClusterGenerator
+ - ECRAuthorizationToken
+ - Fake
+ - GCRAccessToken
+ - GithubAccessToken
+ - QuayAccessToken
+ - Password
+ - STSSessionToken
+ - UUID
+ - VaultDynamicSecret
+ - Webhook
type: string
name:
description: Specify the name of the generator resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- kind
@@ -167,12 +190,16 @@ spec:
description: |-
Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
Defaults to `SecretStore`
+ enum:
+ - SecretStore
+ - ClusterSecretStore
type: string
name:
description: Name of the SecretStore resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
- required:
- - name
type: object
type: object
required:
@@ -314,6 +341,7 @@ spec:
When sourceRef points to a generator Extract or Find is not supported.
The generator returns a static map of values
maxProperties: 1
+ minProperties: 1
properties:
generatorRef:
description: GeneratorRef points to a generator custom
@@ -325,11 +353,26 @@ spec:
resource
type: string
kind:
- description: Specify the Kind of the resource, e.g.
- Password, ACRAccessToken etc.
+ description: Specify the Kind of the generator resource
+ enum:
+ - ACRAccessToken
+ - ClusterGenerator
+ - ECRAuthorizationToken
+ - Fake
+ - GCRAccessToken
+ - GithubAccessToken
+ - QuayAccessToken
+ - Password
+ - STSSessionToken
+ - UUID
+ - VaultDynamicSecret
+ - Webhook
type: string
name:
description: Specify the name of the generator resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- kind
@@ -343,12 +386,16 @@ spec:
description: |-
Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
Defaults to `SecretStore`
+ enum:
+ - SecretStore
+ - ClusterSecretStore
type: string
name:
description: Name of the SecretStore resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
- required:
- - name
type: object
type: object
type: object
@@ -356,8 +403,10 @@ spec:
refreshInterval:
default: 1h
description: |-
- RefreshInterval is the amount of time before the values are read again from the SecretStore provider
+ RefreshInterval is the amount of time before the values are read again from the SecretStore provider,
+ specified as Golang Duration strings.
Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h"
+ Example values: "1h", "2h30m", "5d", "10s"
May be set to zero to fetch and create it once. Defaults to 1h.
type: string
secretStoreRef:
@@ -368,12 +417,16 @@ spec:
description: |-
Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
Defaults to `SecretStore`
+ enum:
+ - SecretStore
+ - ClusterSecretStore
type: string
name:
description: Name of the SecretStore resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
- required:
- - name
type: object
target:
default:
@@ -386,8 +439,8 @@ spec:
creationPolicy:
default: Owner
description: |-
- CreationPolicy defines rules on how to create the resulting Secret
- Defaults to 'Owner'
+ CreationPolicy defines rules on how to create the resulting Secret.
+ Defaults to "Owner"
enum:
- Owner
- Orphan
@@ -397,8 +450,8 @@ spec:
deletionPolicy:
default: Retain
description: |-
- DeletionPolicy defines rules on how to delete the resulting Secret
- Defaults to 'Retain'
+ DeletionPolicy defines rules on how to delete the resulting Secret.
+ Defaults to "Retain"
enum:
- Delete
- Merge
@@ -410,9 +463,11 @@ spec:
type: boolean
name:
description: |-
- Name defines the name of the Secret resource to be managed
- This field is immutable
+ The name of the Secret resource to be managed.
Defaults to the .metadata.name of the ExternalSecret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
template:
description: Template defines a blueprint for the created
@@ -457,9 +512,15 @@ spec:
configMap:
properties:
items:
+ description: A list of keys in the ConfigMap/Secret
+ to use as templates for Secret data
items:
properties:
key:
+ description: A key in the ConfigMap/Secret
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
templateAs:
default: Values
@@ -472,6 +533,11 @@ spec:
type: object
type: array
name:
+ description: The name of the ConfigMap/Secret
+ resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- items
@@ -482,9 +548,15 @@ spec:
secret:
properties:
items:
+ description: A list of keys in the ConfigMap/Secret
+ to use as templates for Secret data
items:
properties:
key:
+ description: A key in the ConfigMap/Secret
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
templateAs:
default: Values
@@ -497,6 +569,11 @@ spec:
type: object
type: array
name:
+ description: The name of the ConfigMap/Secret
+ resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- items
@@ -547,11 +624,13 @@ spec:
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
@@ -597,11 +676,13 @@ spec:
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
@@ -617,6 +698,9 @@ spec:
description: Choose namespaces by name. This field is ORed with anything
that NamespaceSelectors ends up choosing.
items:
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: array
refreshTime:
diff --git a/config/crds/bases/external-secrets.io_clustersecretstores.yaml b/config/crds/bases/external-secrets.io_clustersecretstores.yaml
index 9ec502620e9..e153faa572c 100644
--- a/config/crds/bases/external-secrets.io_clustersecretstores.yaml
+++ b/config/crds/bases/external-secrets.io_clustersecretstores.yaml
@@ -2,13 +2,15 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: clustersecretstores.external-secrets.io
spec:
group: external-secrets.io
names:
categories:
- - externalsecrets
+ - external-secrets
kind: ClusterSecretStore
listKind: ClusterSecretStoreList
plural: clustersecretstores
@@ -95,17 +97,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -126,11 +137,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -149,57 +166,84 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessType:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessTypeParam:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -216,15 +260,24 @@ spec:
Akeyless Gateway certificate.
properties:
key:
- description: The key the value inside of the provider
- type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found
+ in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider
type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -274,17 +327,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessKeySecretSecretRef:
@@ -292,17 +354,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -345,11 +416,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -365,17 +442,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretAccessKeySecretRef:
@@ -383,17 +469,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -430,17 +525,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientSecret:
@@ -449,17 +553,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -495,11 +608,17 @@ spec:
name:
description: The name of the ServiceAccount resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -553,17 +672,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -589,11 +717,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -623,17 +757,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -666,17 +809,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -705,42 +857,60 @@ spec:
properties:
clientCert:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientKey:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -762,11 +932,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -777,22 +953,31 @@ spec:
properties:
bearerToken:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -800,6 +985,9 @@ spec:
remoteNamespace:
default: default
description: Remote namespace to fetch the secrets from
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
server:
description: configures the Kubernetes server Address.
@@ -812,15 +1000,24 @@ spec:
description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider'
properties:
key:
- description: The key the value inside of the provider
- type to use, only used with "Secret" type
+ description: The key where the CA certificate can
+ be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the
provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -860,17 +1057,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
privatekey:
@@ -879,17 +1085,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -948,11 +1163,17 @@ spec:
name:
description: The name of the ServiceAccount resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -980,17 +1201,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1041,17 +1271,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -1071,17 +1310,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -1091,17 +1339,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1146,11 +1403,17 @@ spec:
name:
description: The name of the ServiceAccount
resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -1176,17 +1439,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -1217,17 +1489,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -1248,11 +1529,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -1280,17 +1567,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -1308,17 +1604,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1335,15 +1640,24 @@ spec:
Vault server certificate.
properties:
key:
- description: The key the value inside of the provider
- type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found
+ in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider
type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -1420,15 +1734,24 @@ spec:
webhook server certificate.
properties:
key:
- description: The key the value inside of the provider
- type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found
+ in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider
type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -1470,17 +1793,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -1514,17 +1846,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1534,22 +1875,31 @@ spec:
properties:
certSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1644,6 +1994,11 @@ spec:
ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in
for a ClusterSecretStore instance.
properties:
+ namespaceRegexes:
+ description: Choose namespaces by using regex matching
+ items:
+ type: string
+ type: array
namespaceSelector:
description: Choose namespace using a labelSelector
properties:
@@ -1673,11 +2028,13 @@ spec:
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
@@ -1691,6 +2048,9 @@ spec:
namespaces:
description: Choose namespaces by name
items:
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: array
type: object
@@ -1739,17 +2099,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -1770,11 +2139,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -1793,57 +2168,84 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessType:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessTypeParam:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1862,15 +2264,24 @@ spec:
key:
description: The key where the CA certificate can be found
in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider
type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -1920,17 +2331,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessKeySecretSecretRef:
@@ -1938,17 +2358,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -1998,11 +2427,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -2018,17 +2453,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretAccessKeySecretRef:
@@ -2036,17 +2480,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
sessionTokenSecretRef:
@@ -2057,17 +2510,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2075,6 +2537,9 @@ spec:
externalID:
description: AWS External ID set on assumed IAM roles
type: string
+ prefix:
+ description: Prefix adds a prefix to all retrieved values.
+ type: string
region:
description: AWS Region to be used for the provider
type: string
@@ -2142,23 +2607,60 @@ spec:
with Azure. Required for ServicePrincipal auth type. Optional
for WorkloadIdentity.
properties:
+ clientCertificate:
+ description: The Azure ClientCertificate of the service
+ principle used for authentication.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
clientId:
description: The Azure clientId of the service principle
or managed identity used for authentication.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientSecret:
@@ -2167,17 +2669,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
tenantId:
@@ -2186,17 +2697,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2245,11 +2765,17 @@ spec:
name:
description: The name of the ServiceAccount resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -2266,6 +2792,333 @@ spec:
required:
- vaultUrl
type: object
+ beyondtrust:
+ description: Beyondtrust configures this store to sync secrets
+ using Password Safe provider.
+ properties:
+ auth:
+ description: Auth configures how the operator authenticates
+ with Beyondtrust.
+ properties:
+ apiKey:
+ description: APIKey If not provided then ClientID/ClientSecret
+ become required.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret
+ that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set
+ a value without using a secret.
+ type: string
+ type: object
+ certificate:
+ description: Certificate (cert.pem) for use when authenticating
+ with an OAuth client Id using a Client Certificate.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret
+ that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set
+ a value without using a secret.
+ type: string
+ type: object
+ certificateKey:
+ description: Certificate private key (key.pem). For use
+ when authenticating with an OAuth client Id
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret
+ that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set
+ a value without using a secret.
+ type: string
+ type: object
+ clientId:
+ description: ClientID is the API OAuth Client ID.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret
+ that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set
+ a value without using a secret.
+ type: string
+ type: object
+ clientSecret:
+ description: ClientSecret is the API OAuth Client Secret.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret
+ that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set
+ a value without using a secret.
+ type: string
+ type: object
+ type: object
+ server:
+ description: Auth configures how API server works.
+ properties:
+ apiUrl:
+ type: string
+ clientTimeOutSeconds:
+ description: Timeout specifies a time limit for requests
+ made by this Client. The timeout includes connection
+ time, any redirects, and reading the response body.
+ Defaults to 45 seconds.
+ type: integer
+ retrievalType:
+ description: The secret retrieval type. SECRET = Secrets
+ Safe (credential, text, file). MANAGED_ACCOUNT = Password
+ Safe account associated with a system.
+ type: string
+ separator:
+ description: A character that separates the folder names.
+ type: string
+ verifyCA:
+ type: boolean
+ required:
+ - apiUrl
+ - verifyCA
+ type: object
+ required:
+ - auth
+ - server
+ type: object
+ bitwardensecretsmanager:
+ description: BitwardenSecretsManager configures this store to
+ sync secrets using BitwardenSecretsManager provider
+ properties:
+ apiURL:
+ type: string
+ auth:
+ description: |-
+ Auth configures how secret-manager authenticates with a bitwarden machine account instance.
+ Make sure that the token being used has permissions on the given secret.
+ properties:
+ secretRef:
+ description: BitwardenSecretsManagerSecretRef contains
+ the credential ref to the bitwarden instance.
+ properties:
+ credentials:
+ description: AccessToken used for the bitwarden instance.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - credentials
+ type: object
+ required:
+ - secretRef
+ type: object
+ bitwardenServerSDKURL:
+ type: string
+ caBundle:
+ description: |-
+ Base64 encoded certificate for the bitwarden server sdk. The sdk MUST run with HTTPS to make sure no MITM attack
+ can be performed.
+ type: string
+ caProvider:
+ description: 'see: https://external-secrets.io/latest/spec/#external-secrets.io/v1alpha1.CAProvider'
+ properties:
+ key:
+ description: The key where the CA certificate can be found
+ in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the object located at the provider
+ type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace the Provider type is in.
+ Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type:
+ description: The type of provider to use such as "Secret",
+ or "ConfigMap".
+ enum:
+ - Secret
+ - ConfigMap
+ type: string
+ required:
+ - name
+ - type
+ type: object
+ identityURL:
+ type: string
+ organizationID:
+ description: OrganizationID determines which organization
+ this secret store manages.
+ type: string
+ projectID:
+ description: ProjectID determines which project this secret
+ store manages.
+ type: string
+ required:
+ - auth
+ - organizationID
+ - projectID
+ type: object
chef:
description: Chef configures this store to sync secrets with chef
server
@@ -2284,17 +3137,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -2328,42 +3190,60 @@ spec:
type: string
apiKeyRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
userRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -2387,17 +3267,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -2416,11 +3305,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -2444,15 +3339,24 @@ spec:
key:
description: The key where the CA certificate can be found
in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider
type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -2485,17 +3389,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -2512,17 +3425,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -2548,6 +3470,54 @@ spec:
- clientSecret
- tenant
type: object
+ device42:
+ description: Device42 configures this store to sync secrets using
+ the Device42 provider
+ properties:
+ auth:
+ description: Auth configures how secret-manager authenticates
+ with a Device42 instance.
+ properties:
+ secretRef:
+ properties:
+ credentials:
+ description: Username / Password is used for authentication.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ required:
+ - secretRef
+ type: object
+ host:
+ description: URL configures the Device42 instance URL.
+ type: string
+ required:
+ - auth
+ - host
+ type: object
doppler:
description: Doppler configures this store to sync secrets using
the Doppler provider
@@ -2566,17 +3536,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -2656,17 +3635,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2689,17 +3677,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2725,11 +3722,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -2740,6 +3743,10 @@ spec:
- serviceAccountRef
type: object
type: object
+ location:
+ description: Location optionally defines a location for a
+ secret
+ type: string
projectID:
description: ProjectID project where secret is located
type: string
@@ -2759,17 +3766,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2836,17 +3852,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2858,28 +3883,133 @@ spec:
required:
- auth
type: object
+ infisical:
+ description: Infisical configures this store to sync secrets using
+ the Infisical provider
+ properties:
+ auth:
+ description: Auth configures how the Operator authenticates
+ with the Infisical API
+ properties:
+ universalAuthCredentials:
+ properties:
+ clientId:
+ description: |-
+ A reference to a specific 'key' within a Secret resource.
+ In some instances, `key` is a required field.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ clientSecret:
+ description: |-
+ A reference to a specific 'key' within a Secret resource.
+ In some instances, `key` is a required field.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - clientId
+ - clientSecret
+ type: object
+ type: object
+ hostAPI:
+ default: https://app.infisical.com/api
+ type: string
+ secretsScope:
+ properties:
+ environmentSlug:
+ type: string
+ projectSlug:
+ type: string
+ recursive:
+ default: false
+ type: boolean
+ secretsPath:
+ default: /
+ type: string
+ required:
+ - environmentSlug
+ - projectSlug
+ type: object
+ required:
+ - auth
+ - secretsScope
+ type: object
keepersecurity:
description: KeeperSecurity configures this store to sync secrets
using the KeeperSecurity provider
properties:
authRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred
to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
folderID:
@@ -2903,42 +4033,60 @@ spec:
properties:
clientCert:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientKey:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2957,11 +4105,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -2971,29 +4125,69 @@ spec:
properties:
bearerToken:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
type: object
+ authRef:
+ description: A reference to a secret that contains the auth
+ information.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred
+ to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
remoteNamespace:
default: default
description: Remote namespace to fetch the secrets from
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
server:
description: configures the Kubernetes server Address.
@@ -3008,15 +4202,24 @@ spec:
key:
description: The key where the CA certificate can
be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the
provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -3034,8 +4237,6 @@ spec:
description: configures the Kubernetes server Address.
type: string
type: object
- required:
- - auth
type: object
onboardbase:
description: Onboardbase configures this store to sync secrets
@@ -3057,17 +4258,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
passcodeRef:
@@ -3076,17 +4286,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3127,17 +4346,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3179,17 +4407,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
privatekey:
@@ -3198,17 +4435,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3267,11 +4513,17 @@ spec:
name:
description: The name of the ServiceAccount resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -3292,42 +4544,60 @@ spec:
properties:
passwordSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
privateKeySecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3356,17 +4626,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -3384,6 +4663,53 @@ spec:
- database
- host
type: object
+ previder:
+ description: Previder configures this store to sync secrets using
+ the Previder provider
+ properties:
+ auth:
+ description: PreviderAuth contains a secretRef for credentials.
+ properties:
+ secretRef:
+ description: PreviderAuthSecretRef holds secret references
+ for Previder Vault credentials.
+ properties:
+ accessToken:
+ description: The AccessToken is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - accessToken
+ type: object
+ type: object
+ baseUri:
+ type: string
+ required:
+ - auth
+ type: object
pulumi:
description: Pulumi configures this store to sync secrets using
the Pulumi provider
@@ -3398,22 +4724,31 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
apiUrl:
- default: https://api.pulumi.com
+ default: https://api.pulumi.com/api/esc
description: APIURL is the URL of the Pulumi API.
type: string
environment:
@@ -3428,10 +4763,15 @@ spec:
Organization are a space to collaborate on shared projects and stacks.
To create a new organization, visit https://app.pulumi.com/ and click "New Organization".
type: string
+ project:
+ description: Project is the name of the Pulumi ESC project
+ the environment belongs to.
+ type: string
required:
- accessToken
- environment
- organization
+ - project
type: object
scaleway:
description: Scaleway
@@ -3445,17 +4785,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -3483,17 +4832,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -3507,6 +4865,93 @@ spec:
- region
- secretKey
type: object
+ secretserver:
+ description: |-
+ SecretServer configures this store to sync secrets using SecretServer provider
+ https://docs.delinea.com/online-help/secret-server/start.htm
+ properties:
+ password:
+ description: Password is the secret server account password.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that
+ will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a
+ value without using a secret.
+ type: string
+ type: object
+ serverURL:
+ description: |-
+ ServerURL
+ URL to your secret server installation
+ type: string
+ username:
+ description: Username is the secret server account username.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that
+ will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a
+ value without using a secret.
+ type: string
+ type: object
+ required:
+ - password
+ - serverURL
+ - username
+ type: object
senhasegura:
description: Senhasegura configures this store to sync secrets
using senhasegura provider
@@ -3518,22 +4963,31 @@ spec:
type: string
clientSecretSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3590,17 +5044,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -3612,17 +5075,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3641,17 +5113,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -3661,17 +5142,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -3700,11 +5190,17 @@ spec:
name:
description: The name of the ServiceAccount
resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -3729,17 +5225,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretAccessKeySecretRef:
@@ -3747,17 +5252,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
sessionTokenSecretRef:
@@ -3768,17 +5282,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -3840,11 +5363,17 @@ spec:
name:
description: The name of the ServiceAccount
resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -3870,17 +5399,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3911,17 +5449,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -3942,11 +5489,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -3974,17 +5527,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -4010,17 +5572,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
userPass:
@@ -4041,17 +5612,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -4079,15 +5659,24 @@ spec:
key:
description: The key where the CA certificate can be found
in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider
type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -4107,6 +5696,11 @@ spec:
the option is enabled serverside.
https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header
type: boolean
+ headers:
+ additionalProperties:
+ type: string
+ description: Headers to be added in Vault request
+ type: object
namespace:
description: |-
Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows
@@ -4147,17 +5741,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
keySecretRef:
@@ -4168,17 +5771,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -4215,15 +5827,24 @@ spec:
webhook server certificate.
properties:
key:
- description: The key the value inside of the provider
- type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found
+ in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider
type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -4265,17 +5886,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -4309,17 +5939,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -4329,22 +5968,31 @@ spec:
properties:
certSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -4367,17 +6015,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -4387,22 +6044,31 @@ spec:
properties:
certSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
diff --git a/config/crds/bases/external-secrets.io_externalsecrets.yaml b/config/crds/bases/external-secrets.io_externalsecrets.yaml
index 3bb615d5088..34d4b598b52 100644
--- a/config/crds/bases/external-secrets.io_externalsecrets.yaml
+++ b/config/crds/bases/external-secrets.io_externalsecrets.yaml
@@ -2,13 +2,15 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: externalsecrets.external-secrets.io
spec:
group: external-secrets.io
names:
categories:
- - externalsecrets
+ - external-secrets
kind: ExternalSecret
listKind: ExternalSecretList
plural: externalsecrets
@@ -18,6 +20,9 @@ spec:
scope: Namespaced
versions:
- additionalPrinterColumns:
+ - jsonPath: .spec.secretStoreRef.kind
+ name: Store
+ type: string
- jsonPath: .spec.secretStoreRef.name
name: Store
type: string
@@ -86,6 +91,10 @@ spec:
- key
type: object
secretKey:
+ description: The key in the Kubernetes Secret to store the value.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
required:
- remoteRef
@@ -136,12 +145,16 @@ spec:
description: |-
Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
Defaults to `SecretStore`
+ enum:
+ - SecretStore
+ - ClusterSecretStore
type: string
name:
description: Name of the SecretStore resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
- required:
- - name
type: object
target:
description: |-
@@ -151,8 +164,8 @@ spec:
creationPolicy:
default: Owner
description: |-
- CreationPolicy defines rules on how to create the resulting Secret
- Defaults to 'Owner'
+ CreationPolicy defines rules on how to create the resulting Secret.
+ Defaults to "Owner"
enum:
- Owner
- Merge
@@ -163,9 +176,11 @@ spec:
type: boolean
name:
description: |-
- Name defines the name of the Secret resource to be managed
- This field is immutable
+ The name of the Secret resource to be managed.
Defaults to the .metadata.name of the ExternalSecret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
template:
description: Template defines a blueprint for the created Secret
@@ -206,15 +221,25 @@ spec:
configMap:
properties:
items:
+ description: A list of keys in the ConfigMap/Secret
+ to use as templates for Secret data
items:
properties:
key:
+ description: A key in the ConfigMap/Secret
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
required:
- key
type: object
type: array
name:
+ description: The name of the ConfigMap/Secret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- items
@@ -223,15 +248,25 @@ spec:
secret:
properties:
items:
+ description: A list of keys in the ConfigMap/Secret
+ to use as templates for Secret data
items:
properties:
key:
+ description: A key in the ConfigMap/Secret
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
required:
- key
type: object
type: array
name:
+ description: The name of the ConfigMap/Secret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- items
@@ -254,10 +289,13 @@ spec:
reference to the secret
properties:
name:
+ default: ""
description: |-
Name of the referent.
+ This field is effectively required, but due to backwards compatibility is
+ allowed to be empty. Instances of this type with an empty value here are
+ almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion, kind, uid?
type: string
type: object
x-kubernetes-map-type: atomic
@@ -298,6 +336,9 @@ spec:
subresources:
status: {}
- additionalPrinterColumns:
+ - jsonPath: .spec.secretStoreRef.kind
+ name: StoreType
+ type: string
- jsonPath: .spec.secretStoreRef.name
name: Store
type: string
@@ -387,21 +428,22 @@ spec:
- key
type: object
secretKey:
- description: |-
- SecretKey defines the key in which the controller stores
- the value. This is the key in the Kind=Secret
+ description: The key in the Kubernetes Secret to store the value.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
sourceRef:
description: |-
SourceRef allows you to override the source
- from which the value will pulled from.
+ from which the value will be pulled.
maxProperties: 1
+ minProperties: 1
properties:
generatorRef:
description: |-
GeneratorRef points to a generator custom resource.
-
Deprecated: The generatorRef is not implemented in .data[].
this will be removed with v1.
properties:
@@ -411,11 +453,26 @@ spec:
resource
type: string
kind:
- description: Specify the Kind of the resource, e.g.
- Password, ACRAccessToken etc.
+ description: Specify the Kind of the generator resource
+ enum:
+ - ACRAccessToken
+ - ClusterGenerator
+ - ECRAuthorizationToken
+ - Fake
+ - GCRAccessToken
+ - GithubAccessToken
+ - QuayAccessToken
+ - Password
+ - STSSessionToken
+ - UUID
+ - VaultDynamicSecret
+ - Webhook
type: string
name:
description: Specify the name of the generator resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- kind
@@ -429,12 +486,16 @@ spec:
description: |-
Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
Defaults to `SecretStore`
+ enum:
+ - SecretStore
+ - ClusterSecretStore
type: string
name:
description: Name of the SecretStore resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
- required:
- - name
type: object
type: object
required:
@@ -576,6 +637,7 @@ spec:
When sourceRef points to a generator Extract or Find is not supported.
The generator returns a static map of values
maxProperties: 1
+ minProperties: 1
properties:
generatorRef:
description: GeneratorRef points to a generator custom resource.
@@ -586,11 +648,26 @@ spec:
resource
type: string
kind:
- description: Specify the Kind of the resource, e.g.
- Password, ACRAccessToken etc.
+ description: Specify the Kind of the generator resource
+ enum:
+ - ACRAccessToken
+ - ClusterGenerator
+ - ECRAuthorizationToken
+ - Fake
+ - GCRAccessToken
+ - GithubAccessToken
+ - QuayAccessToken
+ - Password
+ - STSSessionToken
+ - UUID
+ - VaultDynamicSecret
+ - Webhook
type: string
name:
description: Specify the name of the generator resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- kind
@@ -604,12 +681,16 @@ spec:
description: |-
Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
Defaults to `SecretStore`
+ enum:
+ - SecretStore
+ - ClusterSecretStore
type: string
name:
description: Name of the SecretStore resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
- required:
- - name
type: object
type: object
type: object
@@ -617,8 +698,10 @@ spec:
refreshInterval:
default: 1h
description: |-
- RefreshInterval is the amount of time before the values are read again from the SecretStore provider
+ RefreshInterval is the amount of time before the values are read again from the SecretStore provider,
+ specified as Golang Duration strings.
Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h"
+ Example values: "1h", "2h30m", "5d", "10s"
May be set to zero to fetch and create it once. Defaults to 1h.
type: string
secretStoreRef:
@@ -629,12 +712,16 @@ spec:
description: |-
Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
Defaults to `SecretStore`
+ enum:
+ - SecretStore
+ - ClusterSecretStore
type: string
name:
description: Name of the SecretStore resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
- required:
- - name
type: object
target:
default:
@@ -647,8 +734,8 @@ spec:
creationPolicy:
default: Owner
description: |-
- CreationPolicy defines rules on how to create the resulting Secret
- Defaults to 'Owner'
+ CreationPolicy defines rules on how to create the resulting Secret.
+ Defaults to "Owner"
enum:
- Owner
- Orphan
@@ -658,8 +745,8 @@ spec:
deletionPolicy:
default: Retain
description: |-
- DeletionPolicy defines rules on how to delete the resulting Secret
- Defaults to 'Retain'
+ DeletionPolicy defines rules on how to delete the resulting Secret.
+ Defaults to "Retain"
enum:
- Delete
- Merge
@@ -670,9 +757,11 @@ spec:
type: boolean
name:
description: |-
- Name defines the name of the Secret resource to be managed
- This field is immutable
+ The name of the Secret resource to be managed.
Defaults to the .metadata.name of the ExternalSecret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
template:
description: Template defines a blueprint for the created Secret
@@ -717,9 +806,15 @@ spec:
configMap:
properties:
items:
+ description: A list of keys in the ConfigMap/Secret
+ to use as templates for Secret data
items:
properties:
key:
+ description: A key in the ConfigMap/Secret
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
templateAs:
default: Values
@@ -732,6 +827,10 @@ spec:
type: object
type: array
name:
+ description: The name of the ConfigMap/Secret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- items
@@ -742,9 +841,15 @@ spec:
secret:
properties:
items:
+ description: A list of keys in the ConfigMap/Secret
+ to use as templates for Secret data
items:
properties:
key:
+ description: A key in the ConfigMap/Secret
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
templateAs:
default: Values
@@ -757,6 +862,10 @@ spec:
type: object
type: array
name:
+ description: The name of the ConfigMap/Secret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- items
@@ -783,10 +892,13 @@ spec:
reference to the secret
properties:
name:
+ default: ""
description: |-
Name of the referent.
+ This field is effectively required, but due to backwards compatibility is
+ allowed to be empty. Instances of this type with an empty value here are
+ almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion, kind, uid?
type: string
type: object
x-kubernetes-map-type: atomic
diff --git a/config/crds/bases/external-secrets.io_pushsecrets.yaml b/config/crds/bases/external-secrets.io_pushsecrets.yaml
index 24016035ac0..459fccc92f9 100644
--- a/config/crds/bases/external-secrets.io_pushsecrets.yaml
+++ b/config/crds/bases/external-secrets.io_pushsecrets.yaml
@@ -2,13 +2,15 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: pushsecrets.external-secrets.io
spec:
group: external-secrets.io
names:
categories:
- - pushsecrets
+ - external-secrets
kind: PushSecret
listKind: PushSecretList
plural: pushsecrets
@@ -90,8 +92,7 @@ spec:
type: array
deletionPolicy:
default: None
- description: 'Deletion Policy to handle Secrets in the provider. Possible
- Values: "Delete/None". Defaults to "None".'
+ description: Deletion Policy to handle Secrets in the provider.
enum:
- Delete
- None
@@ -105,9 +106,11 @@ spec:
properties:
kind:
default: SecretStore
- description: |-
- Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
- Defaults to `SecretStore`
+ description: Kind of the SecretStore resource (SecretStore or
+ ClusterSecretStore)
+ enum:
+ - SecretStore
+ - ClusterSecretStore
type: string
labelSelector:
description: Optionally, sync to secret stores with label selector
@@ -138,11 +141,13 @@ spec:
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
@@ -156,24 +161,64 @@ spec:
name:
description: Optionally, sync to the SecretStore of the given
name
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
type: object
type: array
selector:
description: The Secret Selector (k8s source) for the Push Secret
+ maxProperties: 1
+ minProperties: 1
properties:
+ generatorRef:
+ description: Point to a generator to create a Secret.
+ properties:
+ apiVersion:
+ default: generators.external-secrets.io/v1alpha1
+ description: Specify the apiVersion of the generator resource
+ type: string
+ kind:
+ description: Specify the Kind of the generator resource
+ enum:
+ - ACRAccessToken
+ - ClusterGenerator
+ - ECRAuthorizationToken
+ - Fake
+ - GCRAccessToken
+ - GithubAccessToken
+ - QuayAccessToken
+ - Password
+ - STSSessionToken
+ - UUID
+ - VaultDynamicSecret
+ - Webhook
+ type: string
+ name:
+ description: Specify the name of the generator resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ required:
+ - kind
+ - name
+ type: object
secret:
description: Select a Secret to Push.
properties:
name:
- description: Name of the Secret. The Secret must exist in
- the same namespace as the PushSecret manifest.
+ description: |-
+ Name of the Secret.
+ The Secret must exist in the same namespace as the PushSecret manifest.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- name
type: object
- required:
- - secret
type: object
template:
description: Template defines a blueprint for the created Secret resource.
@@ -217,9 +262,15 @@ spec:
configMap:
properties:
items:
+ description: A list of keys in the ConfigMap/Secret
+ to use as templates for Secret data
items:
properties:
key:
+ description: A key in the ConfigMap/Secret
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
templateAs:
default: Values
@@ -232,6 +283,10 @@ spec:
type: object
type: array
name:
+ description: The name of the ConfigMap/Secret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- items
@@ -242,9 +297,15 @@ spec:
secret:
properties:
items:
+ description: A list of keys in the ConfigMap/Secret
+ to use as templates for Secret data
items:
properties:
key:
+ description: A key in the ConfigMap/Secret
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
templateAs:
default: Values
@@ -257,6 +318,10 @@ spec:
type: object
type: array
name:
+ description: The name of the ConfigMap/Secret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- items
@@ -276,8 +341,7 @@ spec:
type: object
updatePolicy:
default: Replace
- description: 'UpdatePolicy to handle Secrets in the provider. Possible
- Values: "Replace/IfNotExists". Defaults to "Replace".'
+ description: UpdatePolicy to handle Secrets in the provider.
enum:
- Replace
- IfNotExists
diff --git a/config/crds/bases/external-secrets.io_secretstores.yaml b/config/crds/bases/external-secrets.io_secretstores.yaml
index 6cc50bc0105..7ae261923dc 100644
--- a/config/crds/bases/external-secrets.io_secretstores.yaml
+++ b/config/crds/bases/external-secrets.io_secretstores.yaml
@@ -2,13 +2,15 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: secretstores.external-secrets.io
spec:
group: external-secrets.io
names:
categories:
- - externalsecrets
+ - external-secrets
kind: SecretStore
listKind: SecretStoreList
plural: secretstores
@@ -95,17 +97,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -126,11 +137,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -149,57 +166,84 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessType:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessTypeParam:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -216,15 +260,24 @@ spec:
Akeyless Gateway certificate.
properties:
key:
- description: The key the value inside of the provider
- type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found
+ in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider
type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -274,17 +327,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessKeySecretSecretRef:
@@ -292,17 +354,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -345,11 +416,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -365,17 +442,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretAccessKeySecretRef:
@@ -383,17 +469,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -430,17 +525,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientSecret:
@@ -449,17 +553,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -495,11 +608,17 @@ spec:
name:
description: The name of the ServiceAccount resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -553,17 +672,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -589,11 +717,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -623,17 +757,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -666,17 +809,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -705,42 +857,60 @@ spec:
properties:
clientCert:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientKey:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -762,11 +932,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -777,22 +953,31 @@ spec:
properties:
bearerToken:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -800,6 +985,9 @@ spec:
remoteNamespace:
default: default
description: Remote namespace to fetch the secrets from
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
server:
description: configures the Kubernetes server Address.
@@ -812,15 +1000,24 @@ spec:
description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider'
properties:
key:
- description: The key the value inside of the provider
- type to use, only used with "Secret" type
+ description: The key where the CA certificate can
+ be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the
provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -860,17 +1057,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
privatekey:
@@ -879,17 +1085,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -948,11 +1163,17 @@ spec:
name:
description: The name of the ServiceAccount resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -980,17 +1201,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1041,17 +1271,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -1071,17 +1310,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -1091,17 +1339,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1146,11 +1403,17 @@ spec:
name:
description: The name of the ServiceAccount
resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -1176,17 +1439,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -1217,17 +1489,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -1248,11 +1529,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -1280,17 +1567,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -1308,17 +1604,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1335,15 +1640,24 @@ spec:
Vault server certificate.
properties:
key:
- description: The key the value inside of the provider
- type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found
+ in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider
type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -1420,15 +1734,24 @@ spec:
webhook server certificate.
properties:
key:
- description: The key the value inside of the provider
- type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found
+ in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider
type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -1470,17 +1793,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -1514,17 +1846,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1534,22 +1875,31 @@ spec:
properties:
certSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1644,6 +1994,11 @@ spec:
ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in
for a ClusterSecretStore instance.
properties:
+ namespaceRegexes:
+ description: Choose namespaces by using regex matching
+ items:
+ type: string
+ type: array
namespaceSelector:
description: Choose namespace using a labelSelector
properties:
@@ -1673,11 +2028,13 @@ spec:
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
@@ -1691,6 +2048,9 @@ spec:
namespaces:
description: Choose namespaces by name
items:
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: array
type: object
@@ -1739,17 +2099,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -1770,11 +2139,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -1793,57 +2168,84 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessType:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessTypeParam:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1862,15 +2264,24 @@ spec:
key:
description: The key where the CA certificate can be found
in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider
type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -1920,17 +2331,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessKeySecretSecretRef:
@@ -1938,17 +2358,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -1998,11 +2427,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -2018,17 +2453,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretAccessKeySecretRef:
@@ -2036,17 +2480,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
sessionTokenSecretRef:
@@ -2057,17 +2510,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2075,6 +2537,9 @@ spec:
externalID:
description: AWS External ID set on assumed IAM roles
type: string
+ prefix:
+ description: Prefix adds a prefix to all retrieved values.
+ type: string
region:
description: AWS Region to be used for the provider
type: string
@@ -2142,23 +2607,60 @@ spec:
with Azure. Required for ServicePrincipal auth type. Optional
for WorkloadIdentity.
properties:
+ clientCertificate:
+ description: The Azure ClientCertificate of the service
+ principle used for authentication.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
clientId:
description: The Azure clientId of the service principle
or managed identity used for authentication.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientSecret:
@@ -2167,17 +2669,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
tenantId:
@@ -2186,17 +2697,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2245,11 +2765,17 @@ spec:
name:
description: The name of the ServiceAccount resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -2266,6 +2792,333 @@ spec:
required:
- vaultUrl
type: object
+ beyondtrust:
+ description: Beyondtrust configures this store to sync secrets
+ using Password Safe provider.
+ properties:
+ auth:
+ description: Auth configures how the operator authenticates
+ with Beyondtrust.
+ properties:
+ apiKey:
+ description: APIKey If not provided then ClientID/ClientSecret
+ become required.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret
+ that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set
+ a value without using a secret.
+ type: string
+ type: object
+ certificate:
+ description: Certificate (cert.pem) for use when authenticating
+ with an OAuth client Id using a Client Certificate.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret
+ that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set
+ a value without using a secret.
+ type: string
+ type: object
+ certificateKey:
+ description: Certificate private key (key.pem). For use
+ when authenticating with an OAuth client Id
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret
+ that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set
+ a value without using a secret.
+ type: string
+ type: object
+ clientId:
+ description: ClientID is the API OAuth Client ID.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret
+ that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set
+ a value without using a secret.
+ type: string
+ type: object
+ clientSecret:
+ description: ClientSecret is the API OAuth Client Secret.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret
+ that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set
+ a value without using a secret.
+ type: string
+ type: object
+ type: object
+ server:
+ description: Auth configures how API server works.
+ properties:
+ apiUrl:
+ type: string
+ clientTimeOutSeconds:
+ description: Timeout specifies a time limit for requests
+ made by this Client. The timeout includes connection
+ time, any redirects, and reading the response body.
+ Defaults to 45 seconds.
+ type: integer
+ retrievalType:
+ description: The secret retrieval type. SECRET = Secrets
+ Safe (credential, text, file). MANAGED_ACCOUNT = Password
+ Safe account associated with a system.
+ type: string
+ separator:
+ description: A character that separates the folder names.
+ type: string
+ verifyCA:
+ type: boolean
+ required:
+ - apiUrl
+ - verifyCA
+ type: object
+ required:
+ - auth
+ - server
+ type: object
+ bitwardensecretsmanager:
+ description: BitwardenSecretsManager configures this store to
+ sync secrets using BitwardenSecretsManager provider
+ properties:
+ apiURL:
+ type: string
+ auth:
+ description: |-
+ Auth configures how secret-manager authenticates with a bitwarden machine account instance.
+ Make sure that the token being used has permissions on the given secret.
+ properties:
+ secretRef:
+ description: BitwardenSecretsManagerSecretRef contains
+ the credential ref to the bitwarden instance.
+ properties:
+ credentials:
+ description: AccessToken used for the bitwarden instance.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - credentials
+ type: object
+ required:
+ - secretRef
+ type: object
+ bitwardenServerSDKURL:
+ type: string
+ caBundle:
+ description: |-
+ Base64 encoded certificate for the bitwarden server sdk. The sdk MUST run with HTTPS to make sure no MITM attack
+ can be performed.
+ type: string
+ caProvider:
+ description: 'see: https://external-secrets.io/latest/spec/#external-secrets.io/v1alpha1.CAProvider'
+ properties:
+ key:
+ description: The key where the CA certificate can be found
+ in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the object located at the provider
+ type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace the Provider type is in.
+ Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type:
+ description: The type of provider to use such as "Secret",
+ or "ConfigMap".
+ enum:
+ - Secret
+ - ConfigMap
+ type: string
+ required:
+ - name
+ - type
+ type: object
+ identityURL:
+ type: string
+ organizationID:
+ description: OrganizationID determines which organization
+ this secret store manages.
+ type: string
+ projectID:
+ description: ProjectID determines which project this secret
+ store manages.
+ type: string
+ required:
+ - auth
+ - organizationID
+ - projectID
+ type: object
chef:
description: Chef configures this store to sync secrets with chef
server
@@ -2284,17 +3137,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -2328,42 +3190,60 @@ spec:
type: string
apiKeyRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
userRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -2387,17 +3267,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -2416,11 +3305,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -2444,15 +3339,24 @@ spec:
key:
description: The key where the CA certificate can be found
in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider
type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -2485,17 +3389,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -2512,17 +3425,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -2548,6 +3470,54 @@ spec:
- clientSecret
- tenant
type: object
+ device42:
+ description: Device42 configures this store to sync secrets using
+ the Device42 provider
+ properties:
+ auth:
+ description: Auth configures how secret-manager authenticates
+ with a Device42 instance.
+ properties:
+ secretRef:
+ properties:
+ credentials:
+ description: Username / Password is used for authentication.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ required:
+ - secretRef
+ type: object
+ host:
+ description: URL configures the Device42 instance URL.
+ type: string
+ required:
+ - auth
+ - host
+ type: object
doppler:
description: Doppler configures this store to sync secrets using
the Doppler provider
@@ -2566,17 +3536,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -2656,17 +3635,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2689,17 +3677,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2725,11 +3722,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -2740,6 +3743,10 @@ spec:
- serviceAccountRef
type: object
type: object
+ location:
+ description: Location optionally defines a location for a
+ secret
+ type: string
projectID:
description: ProjectID project where secret is located
type: string
@@ -2759,17 +3766,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2836,17 +3852,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2858,28 +3883,133 @@ spec:
required:
- auth
type: object
+ infisical:
+ description: Infisical configures this store to sync secrets using
+ the Infisical provider
+ properties:
+ auth:
+ description: Auth configures how the Operator authenticates
+ with the Infisical API
+ properties:
+ universalAuthCredentials:
+ properties:
+ clientId:
+ description: |-
+ A reference to a specific 'key' within a Secret resource.
+ In some instances, `key` is a required field.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ clientSecret:
+ description: |-
+ A reference to a specific 'key' within a Secret resource.
+ In some instances, `key` is a required field.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - clientId
+ - clientSecret
+ type: object
+ type: object
+ hostAPI:
+ default: https://app.infisical.com/api
+ type: string
+ secretsScope:
+ properties:
+ environmentSlug:
+ type: string
+ projectSlug:
+ type: string
+ recursive:
+ default: false
+ type: boolean
+ secretsPath:
+ default: /
+ type: string
+ required:
+ - environmentSlug
+ - projectSlug
+ type: object
+ required:
+ - auth
+ - secretsScope
+ type: object
keepersecurity:
description: KeeperSecurity configures this store to sync secrets
using the KeeperSecurity provider
properties:
authRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred
to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
folderID:
@@ -2903,42 +4033,60 @@ spec:
properties:
clientCert:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientKey:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2957,11 +4105,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -2971,29 +4125,69 @@ spec:
properties:
bearerToken:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
type: object
+ authRef:
+ description: A reference to a secret that contains the auth
+ information.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred
+ to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
remoteNamespace:
default: default
description: Remote namespace to fetch the secrets from
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
server:
description: configures the Kubernetes server Address.
@@ -3008,15 +4202,24 @@ spec:
key:
description: The key where the CA certificate can
be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the
provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -3034,8 +4237,6 @@ spec:
description: configures the Kubernetes server Address.
type: string
type: object
- required:
- - auth
type: object
onboardbase:
description: Onboardbase configures this store to sync secrets
@@ -3057,17 +4258,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
passcodeRef:
@@ -3076,17 +4286,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3127,17 +4346,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3179,17 +4407,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
privatekey:
@@ -3198,17 +4435,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3267,11 +4513,17 @@ spec:
name:
description: The name of the ServiceAccount resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -3292,42 +4544,60 @@ spec:
properties:
passwordSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
privateKeySecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3356,17 +4626,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -3384,6 +4663,53 @@ spec:
- database
- host
type: object
+ previder:
+ description: Previder configures this store to sync secrets using
+ the Previder provider
+ properties:
+ auth:
+ description: PreviderAuth contains a secretRef for credentials.
+ properties:
+ secretRef:
+ description: PreviderAuthSecretRef holds secret references
+ for Previder Vault credentials.
+ properties:
+ accessToken:
+ description: The AccessToken is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - accessToken
+ type: object
+ type: object
+ baseUri:
+ type: string
+ required:
+ - auth
+ type: object
pulumi:
description: Pulumi configures this store to sync secrets using
the Pulumi provider
@@ -3398,22 +4724,31 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
apiUrl:
- default: https://api.pulumi.com
+ default: https://api.pulumi.com/api/esc
description: APIURL is the URL of the Pulumi API.
type: string
environment:
@@ -3428,10 +4763,15 @@ spec:
Organization are a space to collaborate on shared projects and stacks.
To create a new organization, visit https://app.pulumi.com/ and click "New Organization".
type: string
+ project:
+ description: Project is the name of the Pulumi ESC project
+ the environment belongs to.
+ type: string
required:
- accessToken
- environment
- organization
+ - project
type: object
scaleway:
description: Scaleway
@@ -3445,17 +4785,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -3483,17 +4832,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -3507,6 +4865,93 @@ spec:
- region
- secretKey
type: object
+ secretserver:
+ description: |-
+ SecretServer configures this store to sync secrets using SecretServer provider
+ https://docs.delinea.com/online-help/secret-server/start.htm
+ properties:
+ password:
+ description: Password is the secret server account password.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that
+ will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a
+ value without using a secret.
+ type: string
+ type: object
+ serverURL:
+ description: |-
+ ServerURL
+ URL to your secret server installation
+ type: string
+ username:
+ description: Username is the secret server account username.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that
+ will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a
+ value without using a secret.
+ type: string
+ type: object
+ required:
+ - password
+ - serverURL
+ - username
+ type: object
senhasegura:
description: Senhasegura configures this store to sync secrets
using senhasegura provider
@@ -3518,22 +4963,31 @@ spec:
type: string
clientSecretSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3590,17 +5044,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -3612,17 +5075,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3641,17 +5113,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -3661,17 +5142,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -3700,11 +5190,17 @@ spec:
name:
description: The name of the ServiceAccount
resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -3729,17 +5225,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretAccessKeySecretRef:
@@ -3747,17 +5252,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
sessionTokenSecretRef:
@@ -3768,17 +5282,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -3840,11 +5363,17 @@ spec:
name:
description: The name of the ServiceAccount
resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -3870,17 +5399,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3911,17 +5449,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -3942,11 +5489,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -3974,17 +5527,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -4010,17 +5572,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
userPass:
@@ -4041,17 +5612,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -4079,15 +5659,24 @@ spec:
key:
description: The key where the CA certificate can be found
in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider
type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -4107,6 +5696,11 @@ spec:
the option is enabled serverside.
https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header
type: boolean
+ headers:
+ additionalProperties:
+ type: string
+ description: Headers to be added in Vault request
+ type: object
namespace:
description: |-
Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows
@@ -4147,17 +5741,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
keySecretRef:
@@ -4168,17 +5771,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -4215,15 +5827,24 @@ spec:
webhook server certificate.
properties:
key:
- description: The key the value inside of the provider
- type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found
+ in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider
type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -4265,17 +5886,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -4309,17 +5939,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -4329,22 +5968,31 @@ spec:
properties:
certSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -4367,17 +6015,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -4387,22 +6044,31 @@ spec:
properties:
certSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
diff --git a/config/crds/bases/generators.external-secrets.io_acraccesstokens.yaml b/config/crds/bases/generators.external-secrets.io_acraccesstokens.yaml
index f9aa2a25ba6..583a3b37e22 100644
--- a/config/crds/bases/generators.external-secrets.io_acraccesstokens.yaml
+++ b/config/crds/bases/generators.external-secrets.io_acraccesstokens.yaml
@@ -2,18 +2,19 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: acraccesstokens.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- - acraccesstoken
+ - external-secrets
+ - external-secrets-generators
kind: ACRAccessToken
listKind: ACRAccessTokenList
plural: acraccesstokens
- shortNames:
- - acraccesstoken
singular: acraccesstoken
scope: Namespaced
versions:
@@ -28,7 +29,6 @@ spec:
This can be scoped down to the repository level using .spec.scope.
In case scope is defined it will return an ACR Access Token.
-
See docs: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md
properties:
apiVersion:
@@ -80,17 +80,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientSecret:
@@ -99,17 +108,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -136,11 +154,17 @@ spec:
name:
description: The name of the ServiceAccount resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -171,12 +195,10 @@ spec:
if not provided it will return a refresh token that has full scope.
Note: you need to pin it down to the repository level, there is no wildcard available.
-
examples:
repository:my-repository:pull,push
repository:my-repository:pull
-
see docs for details: https://docs.docker.com/registry/spec/auth/scope/
type: string
tenantId:
diff --git a/config/crds/bases/generators.external-secrets.io_clustergenerators.yaml b/config/crds/bases/generators.external-secrets.io_clustergenerators.yaml
new file mode 100644
index 00000000000..07547bb8cc8
--- /dev/null
+++ b/config/crds/bases/generators.external-secrets.io_clustergenerators.yaml
@@ -0,0 +1,1757 @@
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
+ name: clustergenerators.generators.external-secrets.io
+spec:
+ group: generators.external-secrets.io
+ names:
+ categories:
+ - external-secrets
+ - external-secrets-generators
+ kind: ClusterGenerator
+ listKind: ClusterGeneratorList
+ plural: clustergenerators
+ singular: clustergenerator
+ scope: Cluster
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: ClusterGenerator represents a cluster-wide generator which can
+ be referenced as part of `generatorRef` fields.
+ properties:
+ apiVersion:
+ description: |-
+ APIVersion defines the versioned schema of this representation of an object.
+ Servers should convert recognized schemas to the latest internal value, and
+ may reject unrecognized values.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+ type: string
+ kind:
+ description: |-
+ Kind is a string value representing the REST resource this object represents.
+ Servers may infer this from the endpoint the client submits requests to.
+ Cannot be updated.
+ In CamelCase.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+ type: string
+ metadata:
+ type: object
+ spec:
+ properties:
+ generator:
+ description: Generator the spec for this generator, must match the
+ kind.
+ maxProperties: 1
+ minProperties: 1
+ properties:
+ acrAccessTokenSpec:
+ description: |-
+ ACRAccessTokenSpec defines how to generate the access token
+ e.g. how to authenticate and which registry to use.
+ see: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#overview
+ properties:
+ auth:
+ properties:
+ managedIdentity:
+ description: ManagedIdentity uses Azure Managed Identity
+ to authenticate with Azure.
+ properties:
+ identityId:
+ description: If multiple Managed Identity is assigned
+ to the pod, you can select the one to be used
+ type: string
+ type: object
+ servicePrincipal:
+ description: ServicePrincipal uses Azure Service Principal
+ credentials to authenticate with Azure.
+ properties:
+ secretRef:
+ description: |-
+ Configuration used to authenticate with Azure using static
+ credentials stored in a Kind=Secret.
+ properties:
+ clientId:
+ description: The Azure clientId of the service
+ principle used for authentication.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource
+ being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ clientSecret:
+ description: The Azure ClientSecret of the service
+ principle used for authentication.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource
+ being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ required:
+ - secretRef
+ type: object
+ workloadIdentity:
+ description: WorkloadIdentity uses Azure Workload Identity
+ to authenticate with Azure.
+ properties:
+ serviceAccountRef:
+ description: |-
+ ServiceAccountRef specified the service account
+ that should be used when authenticating with WorkloadIdentity.
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount resource
+ being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ type: object
+ type: object
+ environmentType:
+ default: PublicCloud
+ description: |-
+ EnvironmentType specifies the Azure cloud environment endpoints to use for
+ connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint.
+ The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152
+ PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud
+ enum:
+ - PublicCloud
+ - USGovernmentCloud
+ - ChinaCloud
+ - GermanCloud
+ type: string
+ registry:
+ description: |-
+ the domain name of the ACR registry
+ e.g. foobarexample.azurecr.io
+ type: string
+ scope:
+ description: |-
+ Define the scope for the access token, e.g. pull/push access for a repository.
+ if not provided it will return a refresh token that has full scope.
+ Note: you need to pin it down to the repository level, there is no wildcard available.
+
+ examples:
+ repository:my-repository:pull,push
+ repository:my-repository:pull
+
+ see docs for details: https://docs.docker.com/registry/spec/auth/scope/
+ type: string
+ tenantId:
+ description: TenantID configures the Azure Tenant to send
+ requests to. Required for ServicePrincipal auth type.
+ type: string
+ required:
+ - auth
+ - registry
+ type: object
+ ecrAuthorizationTokenSpec:
+ properties:
+ auth:
+ description: Auth defines how to authenticate with AWS
+ properties:
+ jwt:
+ description: Authenticate against AWS using service account
+ tokens.
+ properties:
+ serviceAccountRef:
+ description: A reference to a ServiceAccount resource.
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount resource
+ being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ type: object
+ secretRef:
+ description: |-
+ AWSAuthSecretRef holds secret references for AWS credentials
+ both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.
+ properties:
+ accessKeyIDSecretRef:
+ description: The AccessKeyID is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ secretAccessKeySecretRef:
+ description: The SecretAccessKey is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ sessionTokenSecretRef:
+ description: |-
+ The SessionToken used for authentication
+ This must be defined if AccessKeyID and SecretAccessKey are temporary credentials
+ see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ type: object
+ region:
+ description: Region specifies the region to operate in.
+ type: string
+ role:
+ description: |-
+ You can assume a role before making calls to the
+ desired AWS service.
+ type: string
+ scope:
+ description: |-
+ Scope specifies the ECR service scope.
+ Valid options are private and public.
+ type: string
+ required:
+ - region
+ type: object
+ fakeSpec:
+ description: FakeSpec contains the static data.
+ properties:
+ controller:
+ description: |-
+ Used to select the correct ESO controller (think: ingress.ingressClassName)
+ The ESO controller is instantiated with a specific controller name and filters VDS based on this property
+ type: string
+ data:
+ additionalProperties:
+ type: string
+ description: |-
+ Data defines the static data returned
+ by this generator.
+ type: object
+ type: object
+ gcrAccessTokenSpec:
+ properties:
+ auth:
+ description: Auth defines the means for authenticating with
+ GCP
+ properties:
+ secretRef:
+ properties:
+ secretAccessKeySecretRef:
+ description: The SecretAccessKey is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ workloadIdentity:
+ properties:
+ clusterLocation:
+ type: string
+ clusterName:
+ type: string
+ clusterProjectID:
+ type: string
+ serviceAccountRef:
+ description: A reference to a ServiceAccount resource.
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount resource
+ being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ required:
+ - clusterLocation
+ - clusterName
+ - serviceAccountRef
+ type: object
+ type: object
+ projectID:
+ description: ProjectID defines which project to use to authenticate
+ with
+ type: string
+ required:
+ - auth
+ - projectID
+ type: object
+ githubAccessTokenSpec:
+ properties:
+ appID:
+ type: string
+ auth:
+ description: Auth configures how ESO authenticates with a
+ Github instance.
+ properties:
+ privateKey:
+ properties:
+ secretRef:
+ description: |-
+ A reference to a specific 'key' within a Secret resource.
+ In some instances, `key` is a required field.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - secretRef
+ type: object
+ required:
+ - privateKey
+ type: object
+ installID:
+ type: string
+ permissions:
+ additionalProperties:
+ type: string
+ description: Map of permissions the token will have. If omitted,
+ defaults to all permissions the GitHub App has.
+ type: object
+ repositories:
+ description: |-
+ List of repositories the token will have access to. If omitted, defaults to all repositories the GitHub App
+ is installed to.
+ items:
+ type: string
+ type: array
+ url:
+ description: URL configures the Github instance URL. Defaults
+ to https://github.com/.
+ type: string
+ required:
+ - appID
+ - auth
+ - installID
+ type: object
+ passwordSpec:
+ description: PasswordSpec controls the behavior of the password
+ generator.
+ properties:
+ allowRepeat:
+ default: false
+ description: set AllowRepeat to true to allow repeating characters.
+ type: boolean
+ digits:
+ description: |-
+ Digits specifies the number of digits in the generated
+ password. If omitted it defaults to 25% of the length of the password
+ type: integer
+ length:
+ default: 24
+ description: |-
+ Length of the password to be generated.
+ Defaults to 24
+ type: integer
+ noUpper:
+ default: false
+ description: Set NoUpper to disable uppercase characters
+ type: boolean
+ symbolCharacters:
+ description: |-
+ SymbolCharacters specifies the special characters that should be used
+ in the generated password.
+ type: string
+ symbols:
+ description: |-
+ Symbols specifies the number of symbol characters in the generated
+ password. If omitted it defaults to 25% of the length of the password
+ type: integer
+ required:
+ - allowRepeat
+ - length
+ - noUpper
+ type: object
+ quayAccessTokenSpec:
+ properties:
+ robotAccount:
+ description: Name of the robot account you are federating
+ with
+ type: string
+ serviceAccountRef:
+ description: Name of the service account you are federating
+ with
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ url:
+ description: URL configures the Quay instance URL. Defaults
+ to quay.io.
+ type: string
+ required:
+ - robotAccount
+ - serviceAccountRef
+ type: object
+ stsSessionTokenSpec:
+ properties:
+ auth:
+ description: Auth defines how to authenticate with AWS
+ properties:
+ jwt:
+ description: Authenticate against AWS using service account
+ tokens.
+ properties:
+ serviceAccountRef:
+ description: A reference to a ServiceAccount resource.
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount resource
+ being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ type: object
+ secretRef:
+ description: |-
+ AWSAuthSecretRef holds secret references for AWS credentials
+ both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.
+ properties:
+ accessKeyIDSecretRef:
+ description: The AccessKeyID is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ secretAccessKeySecretRef:
+ description: The SecretAccessKey is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ sessionTokenSecretRef:
+ description: |-
+ The SessionToken used for authentication
+ This must be defined if AccessKeyID and SecretAccessKey are temporary credentials
+ see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ type: object
+ region:
+ description: Region specifies the region to operate in.
+ type: string
+ requestParameters:
+ description: RequestParameters contains parameters that can
+ be passed to the STS service.
+ properties:
+ serialNumber:
+ description: |-
+ SerialNumber is the identification number of the MFA device that is associated with the IAM user who is making
+ the GetSessionToken call.
+ Possible values: hardware device (such as GAHT12345678) or an Amazon Resource Name (ARN) for a virtual device
+ (such as arn:aws:iam::123456789012:mfa/user)
+ type: string
+ sessionDuration:
+ description: |-
+ SessionDuration The duration, in seconds, that the credentials should remain valid. Acceptable durations for
+ IAM user sessions range from 900 seconds (15 minutes) to 129,600 seconds (36 hours), with 43,200 seconds
+ (12 hours) as the default.
+ format: int64
+ type: integer
+ tokenCode:
+ description: TokenCode is the value provided by the MFA
+ device, if MFA is required.
+ type: string
+ type: object
+ role:
+ description: |-
+ You can assume a role before making calls to the
+ desired AWS service.
+ type: string
+ required:
+ - region
+ type: object
+ uuidSpec:
+ description: UUIDSpec controls the behavior of the uuid generator.
+ type: object
+ vaultDynamicSecretSpec:
+ properties:
+ allowEmptyResponse:
+ default: false
+ description: Do not fail if no secrets are found. Useful for
+ requests where no data is expected.
+ type: boolean
+ controller:
+ description: |-
+ Used to select the correct ESO controller (think: ingress.ingressClassName)
+ The ESO controller is instantiated with a specific controller name and filters VDS based on this property
+ type: string
+ method:
+ description: Vault API method to use (GET/POST/other)
+ type: string
+ parameters:
+ description: Parameters to pass to Vault write (for non-GET
+ methods)
+ x-kubernetes-preserve-unknown-fields: true
+ path:
+ description: Vault path to obtain the dynamic secret from
+ type: string
+ provider:
+ description: Vault provider common spec
+ properties:
+ auth:
+ description: Auth configures how secret-manager authenticates
+ with the Vault server.
+ properties:
+ appRole:
+ description: |-
+ AppRole authenticates with Vault using the App Role auth mechanism,
+ with the role and secret stored in a Kubernetes Secret resource.
+ properties:
+ path:
+ default: approle
+ description: |-
+ Path where the App Role authentication backend is mounted
+ in Vault, e.g: "approle"
+ type: string
+ roleId:
+ description: |-
+ RoleID configured in the App Role authentication backend when setting
+ up the authentication backend in Vault.
+ type: string
+ roleRef:
+ description: |-
+ Reference to a key in a Secret that contains the App Role ID used
+ to authenticate with Vault.
+ The `key` field must be specified and denotes which entry within the Secret
+ resource is used as the app role id.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource
+ being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ secretRef:
+ description: |-
+ Reference to a key in a Secret that contains the App Role secret used
+ to authenticate with Vault.
+ The `key` field must be specified and denotes which entry within the Secret
+ resource is used as the app role secret.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource
+ being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - path
+ - secretRef
+ type: object
+ cert:
+ description: |-
+ Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate
+ Cert authentication method
+ properties:
+ clientCert:
+ description: |-
+ ClientCert is a certificate to authenticate using the Cert Vault
+ authentication method
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource
+ being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ secretRef:
+ description: |-
+ SecretRef to a key in a Secret resource containing client private key to
+ authenticate with Vault using the Cert authentication method
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource
+ being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ iam:
+ description: |-
+ Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials
+ AWS IAM authentication method
+ properties:
+ externalID:
+ description: AWS External ID set on assumed IAM
+ roles
+ type: string
+ jwt:
+ description: Specify a service account with IRSA
+ enabled
+ properties:
+ serviceAccountRef:
+ description: A reference to a ServiceAccount
+ resource.
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount
+ resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ type: object
+ path:
+ description: 'Path where the AWS auth method is
+ enabled in Vault, e.g: "aws"'
+ type: string
+ region:
+ description: AWS region
+ type: string
+ role:
+ description: This is the AWS role to be assumed
+ before talking to vault
+ type: string
+ secretRef:
+ description: Specify credentials in a Secret object
+ properties:
+ accessKeyIDSecretRef:
+ description: The AccessKeyID is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource
+ being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ secretAccessKeySecretRef:
+ description: The SecretAccessKey is used for
+ authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource
+ being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ sessionTokenSecretRef:
+ description: |-
+ The SessionToken used for authentication
+ This must be defined if AccessKeyID and SecretAccessKey are temporary credentials
+ see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource
+ being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ vaultAwsIamServerID:
+ description: 'X-Vault-AWS-IAM-Server-ID is an
+ additional header used by Vault IAM auth method
+ to mitigate against different types of replay
+ attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws'
+ type: string
+ vaultRole:
+ description: Vault Role. In vault, a role describes
+ an identity with a set of permissions, groups,
+ or policies you want to attach a user of the
+ secrets engine
+ type: string
+ required:
+ - vaultRole
+ type: object
+ jwt:
+ description: |-
+ Jwt authenticates with Vault by passing role and JWT token using the
+ JWT/OIDC authentication method
+ properties:
+ kubernetesServiceAccountToken:
+ description: |-
+ Optional ServiceAccountToken specifies the Kubernetes service account for which to request
+ a token for with the `TokenRequest` API.
+ properties:
+ audiences:
+ description: |-
+ Optional audiences field that will be used to request a temporary Kubernetes service
+ account token for the service account referenced by `serviceAccountRef`.
+ Defaults to a single audience `vault` it not specified.
+ Deprecated: use serviceAccountRef.Audiences instead
+ items:
+ type: string
+ type: array
+ expirationSeconds:
+ description: |-
+ Optional expiration time in seconds that will be used to request a temporary
+ Kubernetes service account token for the service account referenced by
+ `serviceAccountRef`.
+ Deprecated: this will be removed in the future.
+ Defaults to 10 minutes.
+ format: int64
+ type: integer
+ serviceAccountRef:
+ description: Service account field containing
+ the name of a kubernetes ServiceAccount.
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount
+ resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ required:
+ - serviceAccountRef
+ type: object
+ path:
+ default: jwt
+ description: |-
+ Path where the JWT authentication backend is mounted
+ in Vault, e.g: "jwt"
+ type: string
+ role:
+ description: |-
+ Role is a JWT role to authenticate using the JWT/OIDC Vault
+ authentication method
+ type: string
+ secretRef:
+ description: |-
+ Optional SecretRef that refers to a key in a Secret resource containing JWT token to
+ authenticate with Vault using the JWT/OIDC authentication method.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource
+ being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - path
+ type: object
+ kubernetes:
+ description: |-
+ Kubernetes authenticates with Vault by passing the ServiceAccount
+ token stored in the named Secret resource to the Vault server.
+ properties:
+ mountPath:
+ default: kubernetes
+ description: |-
+ Path where the Kubernetes authentication backend is mounted in Vault, e.g:
+ "kubernetes"
+ type: string
+ role:
+ description: |-
+ A required field containing the Vault Role to assume. A Role binds a
+ Kubernetes ServiceAccount with a set of Vault policies.
+ type: string
+ secretRef:
+ description: |-
+ Optional secret field containing a Kubernetes ServiceAccount JWT used
+ for authenticating with Vault. If a name is specified without a key,
+ `token` is the default. If one is not specified, the one bound to
+ the controller will be used.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource
+ being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ serviceAccountRef:
+ description: |-
+ Optional service account field containing the name of a kubernetes ServiceAccount.
+ If the service account is specified, the service account secret token JWT will be used
+ for authenticating with Vault. If the service account selector is not supplied,
+ the secretRef will be used instead.
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount
+ resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ required:
+ - mountPath
+ - role
+ type: object
+ ldap:
+ description: |-
+ Ldap authenticates with Vault by passing username/password pair using
+ the LDAP authentication method
+ properties:
+ path:
+ default: ldap
+ description: |-
+ Path where the LDAP authentication backend is mounted
+ in Vault, e.g: "ldap"
+ type: string
+ secretRef:
+ description: |-
+ SecretRef to a key in a Secret resource containing password for the LDAP
+ user used to authenticate with Vault using the LDAP authentication
+ method
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource
+ being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ username:
+ description: |-
+ Username is a LDAP user name used to authenticate using the LDAP Vault
+ authentication method
+ type: string
+ required:
+ - path
+ - username
+ type: object
+ namespace:
+ description: |-
+ Name of the vault namespace to authenticate to. This can be different than the namespace your secret is in.
+ Namespaces is a set of features within Vault Enterprise that allows
+ Vault environments to support Secure Multi-tenancy. e.g: "ns1".
+ More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces
+ This will default to Vault.Namespace field if set, or empty otherwise
+ type: string
+ tokenSecretRef:
+ description: TokenSecretRef authenticates with Vault
+ by presenting a token.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ userPass:
+ description: UserPass authenticates with Vault by
+ passing username/password pair
+ properties:
+ path:
+ default: user
+ description: |-
+ Path where the UserPassword authentication backend is mounted
+ in Vault, e.g: "user"
+ type: string
+ secretRef:
+ description: |-
+ SecretRef to a key in a Secret resource containing password for the
+ user used to authenticate with Vault using the UserPass authentication
+ method
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource
+ being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ username:
+ description: |-
+ Username is a user name used to authenticate using the UserPass Vault
+ authentication method
+ type: string
+ required:
+ - path
+ - username
+ type: object
+ type: object
+ caBundle:
+ description: |-
+ PEM encoded CA bundle used to validate Vault server certificate. Only used
+ if the Server URL is using HTTPS protocol. This parameter is ignored for
+ plain HTTP protocol connection. If not set the system root certificates
+ are used to validate the TLS connection.
+ format: byte
+ type: string
+ caProvider:
+ description: The provider for the CA bundle to use to
+ validate Vault server certificate.
+ properties:
+ key:
+ description: The key where the CA certificate can
+ be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the object located at the
+ provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace the Provider type is in.
+ Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type:
+ description: The type of provider to use such as "Secret",
+ or "ConfigMap".
+ enum:
+ - Secret
+ - ConfigMap
+ type: string
+ required:
+ - name
+ - type
+ type: object
+ forwardInconsistent:
+ description: |-
+ ForwardInconsistent tells Vault to forward read-after-write requests to the Vault
+ leader instead of simply retrying within a loop. This can increase performance if
+ the option is enabled serverside.
+ https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header
+ type: boolean
+ headers:
+ additionalProperties:
+ type: string
+ description: Headers to be added in Vault request
+ type: object
+ namespace:
+ description: |-
+ Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows
+ Vault environments to support Secure Multi-tenancy. e.g: "ns1".
+ More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces
+ type: string
+ path:
+ description: |-
+ Path is the mount path of the Vault KV backend endpoint, e.g:
+ "secret". The v2 KV secret engine version specific "/data" path suffix
+ for fetching secrets from Vault is optional and will be appended
+ if not present in specified path.
+ type: string
+ readYourWrites:
+ description: |-
+ ReadYourWrites ensures isolated read-after-write semantics by
+ providing discovered cluster replication states in each request.
+ More information about eventual consistency in Vault can be found here
+ https://www.vaultproject.io/docs/enterprise/consistency
+ type: boolean
+ server:
+ description: 'Server is the connection address for the
+ Vault server, e.g: "https://vault.example.com:8200".'
+ type: string
+ tls:
+ description: |-
+ The configuration used for client side related TLS communication, when the Vault server
+ requires mutual authentication. Only used if the Server URL is using HTTPS protocol.
+ This parameter is ignored for plain HTTP protocol connection.
+ It's worth noting this configuration is different from the "TLS certificates auth method",
+ which is available under the `auth.cert` section.
+ properties:
+ certSecretRef:
+ description: |-
+ CertSecretRef is a certificate added to the transport layer
+ when communicating with the Vault server.
+ If no key for the Secret is specified, external-secret will default to 'tls.crt'.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ keySecretRef:
+ description: |-
+ KeySecretRef to a key in a Secret resource containing client private key
+ added to the transport layer when communicating with the Vault server.
+ If no key for the Secret is specified, external-secret will default to 'tls.key'.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ version:
+ default: v2
+ description: |-
+ Version is the Vault KV secret engine version. This can be either "v1" or
+ "v2". Version defaults to "v2".
+ enum:
+ - v1
+ - v2
+ type: string
+ required:
+ - auth
+ - server
+ type: object
+ resultType:
+ default: Data
+ description: |-
+ Result type defines which data is returned from the generator.
+ By default it is the "data" section of the Vault API response.
+ When using e.g. /auth/token/create the "data" section is empty but
+ the "auth" section contains the generated token.
+ Please refer to the vault docs regarding the result data structure.
+ enum:
+ - Data
+ - Auth
+ type: string
+ retrySettings:
+ description: Used to configure http retries if failed
+ properties:
+ maxRetries:
+ format: int32
+ type: integer
+ retryInterval:
+ type: string
+ type: object
+ required:
+ - path
+ - provider
+ type: object
+ webhookSpec:
+ description: WebhookSpec controls the behavior of the external
+ generator. Any body parameters should be passed to the server
+ through the parameters field.
+ properties:
+ body:
+ description: Body
+ type: string
+ caBundle:
+ description: |-
+ PEM encoded CA bundle used to validate webhook server certificate. Only used
+ if the Server URL is using HTTPS protocol. This parameter is ignored for
+ plain HTTP protocol connection. If not set the system root certificates
+ are used to validate the TLS connection.
+ format: byte
+ type: string
+ caProvider:
+ description: The provider for the CA bundle to use to validate
+ webhook server certificate.
+ properties:
+ key:
+ description: The key where the CA certificate can be found
+ in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the object located at the provider
+ type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type:
+ description: The type of provider to use such as "Secret",
+ or "ConfigMap".
+ enum:
+ - Secret
+ - ConfigMap
+ type: string
+ required:
+ - name
+ - type
+ type: object
+ headers:
+ additionalProperties:
+ type: string
+ description: Headers
+ type: object
+ method:
+ description: Webhook Method
+ type: string
+ result:
+ description: Result formatting
+ properties:
+ jsonPath:
+ description: Json path of return value
+ type: string
+ type: object
+ secrets:
+ description: |-
+ Secrets to fill in templates
+ These secrets will be passed to the templating function as key value pairs under the given name
+ items:
+ properties:
+ name:
+ description: Name of this secret in templates
+ type: string
+ secretRef:
+ description: Secret ref to fill in credentials
+ properties:
+ key:
+ description: The key where the token is found.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ type: object
+ required:
+ - name
+ - secretRef
+ type: object
+ type: array
+ timeout:
+ description: Timeout
+ type: string
+ url:
+ description: Webhook url to call
+ type: string
+ required:
+ - result
+ - url
+ type: object
+ type: object
+ kind:
+ description: Kind the kind of this generator.
+ enum:
+ - ACRAccessToken
+ - ECRAuthorizationToken
+ - Fake
+ - GCRAccessToken
+ - GithubAccessToken
+ - QuayAccessToken
+ - Password
+ - STSSessionToken
+ - UUID
+ - VaultDynamicSecret
+ - Webhook
+ type: string
+ required:
+ - generator
+ - kind
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
diff --git a/config/crds/bases/generators.external-secrets.io_ecrauthorizationtokens.yaml b/config/crds/bases/generators.external-secrets.io_ecrauthorizationtokens.yaml
index 8869c950e58..5c87dacdfff 100644
--- a/config/crds/bases/generators.external-secrets.io_ecrauthorizationtokens.yaml
+++ b/config/crds/bases/generators.external-secrets.io_ecrauthorizationtokens.yaml
@@ -2,18 +2,19 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: ecrauthorizationtokens.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- - ecrauthorizationtoken
+ - external-secrets
+ - external-secrets-generators
kind: ECRAuthorizationToken
listKind: ECRAuthorizationTokenList
plural: ecrauthorizationtokens
- shortNames:
- - ecrauthorizationtoken
singular: ecrauthorizationtoken
scope: Namespaced
versions:
@@ -67,11 +68,17 @@ spec:
name:
description: The name of the ServiceAccount resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -87,17 +94,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred
to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretAccessKeySecretRef:
@@ -105,17 +121,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred
to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
sessionTokenSecretRef:
@@ -126,17 +151,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred
to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -149,6 +183,11 @@ spec:
You can assume a role before making calls to the
desired AWS service.
type: string
+ scope:
+ description: |-
+ Scope specifies the ECR service scope.
+ Valid options are private and public.
+ type: string
required:
- region
type: object
diff --git a/config/crds/bases/generators.external-secrets.io_fakes.yaml b/config/crds/bases/generators.external-secrets.io_fakes.yaml
index 272d8aec678..9aed7f1321b 100644
--- a/config/crds/bases/generators.external-secrets.io_fakes.yaml
+++ b/config/crds/bases/generators.external-secrets.io_fakes.yaml
@@ -2,18 +2,19 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: fakes.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- - fake
+ - external-secrets
+ - external-secrets-generators
kind: Fake
listKind: FakeList
plural: fakes
- shortNames:
- - fake
singular: fake
scope: Namespaced
versions:
diff --git a/config/crds/bases/generators.external-secrets.io_gcraccesstokens.yaml b/config/crds/bases/generators.external-secrets.io_gcraccesstokens.yaml
index 09265e13354..45ad54735fa 100644
--- a/config/crds/bases/generators.external-secrets.io_gcraccesstokens.yaml
+++ b/config/crds/bases/generators.external-secrets.io_gcraccesstokens.yaml
@@ -2,18 +2,19 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: gcraccesstokens.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- - gcraccesstoken
+ - external-secrets
+ - external-secrets-generators
kind: GCRAccessToken
listKind: GCRAccessTokenList
plural: gcraccesstokens
- shortNames:
- - gcraccesstoken
singular: gcraccesstoken
scope: Namespaced
versions:
@@ -53,17 +54,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred
to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -89,11 +99,17 @@ spec:
name:
description: The name of the ServiceAccount resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
diff --git a/config/crds/bases/generators.external-secrets.io_githubaccesstokens.yaml b/config/crds/bases/generators.external-secrets.io_githubaccesstokens.yaml
index 36c6867daa2..7cd460356f8 100644
--- a/config/crds/bases/generators.external-secrets.io_githubaccesstokens.yaml
+++ b/config/crds/bases/generators.external-secrets.io_githubaccesstokens.yaml
@@ -2,18 +2,19 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: githubaccesstokens.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- - githubaccesstoken
+ - external-secrets
+ - external-secrets-generators
kind: GithubAccessToken
listKind: GithubAccessTokenList
plural: githubaccesstokens
- shortNames:
- - githubaccesstoken
singular: githubaccesstoken
scope: Namespaced
versions:
@@ -46,36 +47,58 @@ spec:
auth:
description: Auth configures how ESO authenticates with a Github instance.
properties:
- privatKey:
+ privateKey:
properties:
secretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred
to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
- secretRef
type: object
required:
- - privatKey
+ - privateKey
type: object
installID:
type: string
+ permissions:
+ additionalProperties:
+ type: string
+ description: Map of permissions the token will have. If omitted, defaults
+ to all permissions the GitHub App has.
+ type: object
+ repositories:
+ description: |-
+ List of repositories the token will have access to. If omitted, defaults to all repositories the GitHub App
+ is installed to.
+ items:
+ type: string
+ type: array
url:
description: URL configures the Github instance URL. Defaults to https://github.com/.
type: string
diff --git a/config/crds/bases/generators.external-secrets.io_passwords.yaml b/config/crds/bases/generators.external-secrets.io_passwords.yaml
index 869120ed74f..7667e65a57f 100644
--- a/config/crds/bases/generators.external-secrets.io_passwords.yaml
+++ b/config/crds/bases/generators.external-secrets.io_passwords.yaml
@@ -2,18 +2,19 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: passwords.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- - password
+ - external-secrets
+ - external-secrets-generators
kind: Password
listKind: PasswordList
plural: passwords
- shortNames:
- - password
singular: password
scope: Namespaced
versions:
diff --git a/config/crds/bases/generators.external-secrets.io_quayaccesstokens.yaml b/config/crds/bases/generators.external-secrets.io_quayaccesstokens.yaml
new file mode 100644
index 00000000000..c4a869fa09d
--- /dev/null
+++ b/config/crds/bases/generators.external-secrets.io_quayaccesstokens.yaml
@@ -0,0 +1,89 @@
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
+ name: quayaccesstokens.generators.external-secrets.io
+spec:
+ group: generators.external-secrets.io
+ names:
+ categories:
+ - external-secrets
+ - external-secrets-generators
+ kind: QuayAccessToken
+ listKind: QuayAccessTokenList
+ plural: quayaccesstokens
+ singular: quayaccesstoken
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: QuayAccessToken generates Quay oauth token for pulling/pushing
+ images
+ properties:
+ apiVersion:
+ description: |-
+ APIVersion defines the versioned schema of this representation of an object.
+ Servers should convert recognized schemas to the latest internal value, and
+ may reject unrecognized values.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+ type: string
+ kind:
+ description: |-
+ Kind is a string value representing the REST resource this object represents.
+ Servers may infer this from the endpoint the client submits requests to.
+ Cannot be updated.
+ In CamelCase.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+ type: string
+ metadata:
+ type: object
+ spec:
+ properties:
+ robotAccount:
+ description: Name of the robot account you are federating with
+ type: string
+ serviceAccountRef:
+ description: Name of the service account you are federating with
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount resource being referred
+ to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ url:
+ description: URL configures the Quay instance URL. Defaults to quay.io.
+ type: string
+ required:
+ - robotAccount
+ - serviceAccountRef
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
diff --git a/config/crds/bases/generators.external-secrets.io_stssessiontokens.yaml b/config/crds/bases/generators.external-secrets.io_stssessiontokens.yaml
new file mode 100644
index 00000000000..f695b828f72
--- /dev/null
+++ b/config/crds/bases/generators.external-secrets.io_stssessiontokens.yaml
@@ -0,0 +1,214 @@
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
+ name: stssessiontokens.generators.external-secrets.io
+spec:
+ group: generators.external-secrets.io
+ names:
+ categories:
+ - external-secrets
+ - external-secrets-generators
+ kind: STSSessionToken
+ listKind: STSSessionTokenList
+ plural: stssessiontokens
+ singular: stssessiontoken
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: |-
+ STSSessionToken uses the GetSessionToken API to retrieve an authorization token.
+ The authorization token is valid for 12 hours.
+ The authorizationToken returned is a base64 encoded string that can be decoded.
+ For more information, see GetSessionToken (https://docs.aws.amazon.com/STS/latest/APIReference/API_GetSessionToken.html).
+ properties:
+ apiVersion:
+ description: |-
+ APIVersion defines the versioned schema of this representation of an object.
+ Servers should convert recognized schemas to the latest internal value, and
+ may reject unrecognized values.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+ type: string
+ kind:
+ description: |-
+ Kind is a string value representing the REST resource this object represents.
+ Servers may infer this from the endpoint the client submits requests to.
+ Cannot be updated.
+ In CamelCase.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+ type: string
+ metadata:
+ type: object
+ spec:
+ properties:
+ auth:
+ description: Auth defines how to authenticate with AWS
+ properties:
+ jwt:
+ description: Authenticate against AWS using service account tokens.
+ properties:
+ serviceAccountRef:
+ description: A reference to a ServiceAccount resource.
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount resource being
+ referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ type: object
+ secretRef:
+ description: |-
+ AWSAuthSecretRef holds secret references for AWS credentials
+ both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.
+ properties:
+ accessKeyIDSecretRef:
+ description: The AccessKeyID is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred
+ to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ secretAccessKeySecretRef:
+ description: The SecretAccessKey is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred
+ to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ sessionTokenSecretRef:
+ description: |-
+ The SessionToken used for authentication
+ This must be defined if AccessKeyID and SecretAccessKey are temporary credentials
+ see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred
+ to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ type: object
+ region:
+ description: Region specifies the region to operate in.
+ type: string
+ requestParameters:
+ description: RequestParameters contains parameters that can be passed
+ to the STS service.
+ properties:
+ serialNumber:
+ description: |-
+ SerialNumber is the identification number of the MFA device that is associated with the IAM user who is making
+ the GetSessionToken call.
+ Possible values: hardware device (such as GAHT12345678) or an Amazon Resource Name (ARN) for a virtual device
+ (such as arn:aws:iam::123456789012:mfa/user)
+ type: string
+ sessionDuration:
+ description: |-
+ SessionDuration The duration, in seconds, that the credentials should remain valid. Acceptable durations for
+ IAM user sessions range from 900 seconds (15 minutes) to 129,600 seconds (36 hours), with 43,200 seconds
+ (12 hours) as the default.
+ format: int64
+ type: integer
+ tokenCode:
+ description: TokenCode is the value provided by the MFA device,
+ if MFA is required.
+ type: string
+ type: object
+ role:
+ description: |-
+ You can assume a role before making calls to the
+ desired AWS service.
+ type: string
+ required:
+ - region
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
diff --git a/config/crds/bases/generators.external-secrets.io_uuids.yaml b/config/crds/bases/generators.external-secrets.io_uuids.yaml
new file mode 100644
index 00000000000..be05ca8e5b1
--- /dev/null
+++ b/config/crds/bases/generators.external-secrets.io_uuids.yaml
@@ -0,0 +1,50 @@
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
+ name: uuids.generators.external-secrets.io
+spec:
+ group: generators.external-secrets.io
+ names:
+ categories:
+ - external-secrets
+ - external-secrets-generators
+ kind: UUID
+ listKind: UUIDList
+ plural: uuids
+ singular: uuid
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: UUID generates a version 1 UUID (e56657e3-764f-11ef-a397-65231a88c216).
+ properties:
+ apiVersion:
+ description: |-
+ APIVersion defines the versioned schema of this representation of an object.
+ Servers should convert recognized schemas to the latest internal value, and
+ may reject unrecognized values.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+ type: string
+ kind:
+ description: |-
+ Kind is a string value representing the REST resource this object represents.
+ Servers may infer this from the endpoint the client submits requests to.
+ Cannot be updated.
+ In CamelCase.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: UUIDSpec controls the behavior of the uuid generator.
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
diff --git a/config/crds/bases/generators.external-secrets.io_vaultdynamicsecrets.yaml b/config/crds/bases/generators.external-secrets.io_vaultdynamicsecrets.yaml
index 6fccb81eb9a..e98964694b7 100644
--- a/config/crds/bases/generators.external-secrets.io_vaultdynamicsecrets.yaml
+++ b/config/crds/bases/generators.external-secrets.io_vaultdynamicsecrets.yaml
@@ -2,18 +2,19 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: vaultdynamicsecrets.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- - vaultdynamicsecret
+ - external-secrets
+ - external-secrets-generators
kind: VaultDynamicSecret
listKind: VaultDynamicSecretList
plural: vaultdynamicsecrets
- shortNames:
- - vaultdynamicsecret
singular: vaultdynamicsecret
scope: Namespaced
versions:
@@ -40,6 +41,11 @@ spec:
type: object
spec:
properties:
+ allowEmptyResponse:
+ default: false
+ description: Do not fail if no secrets are found. Useful for requests
+ where no data is expected.
+ type: boolean
controller:
description: |-
Used to select the correct ESO controller (think: ingress.ingressClassName)
@@ -86,17 +92,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -108,17 +123,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -137,17 +161,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -157,17 +190,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -196,11 +238,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -225,17 +273,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretAccessKeySecretRef:
@@ -243,17 +300,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
sessionTokenSecretRef:
@@ -264,17 +330,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -335,11 +410,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -365,17 +446,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -406,17 +496,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -437,11 +536,17 @@ spec:
name:
description: The name of the ServiceAccount resource
being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -469,17 +574,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -505,17 +619,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred
to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
userPass:
@@ -536,17 +659,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being
referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -574,15 +706,24 @@ spec:
key:
description: The key where the CA certificate can be found
in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider
type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret",
@@ -602,6 +743,11 @@ spec:
the option is enabled serverside.
https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header
type: boolean
+ headers:
+ additionalProperties:
+ type: string
+ description: Headers to be added in Vault request
+ type: object
namespace:
description: |-
Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows
@@ -642,17 +788,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred
to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
keySecretRef:
@@ -663,17 +818,26 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred
to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -702,6 +866,15 @@ spec:
- Data
- Auth
type: string
+ retrySettings:
+ description: Used to configure http retries if failed
+ properties:
+ maxRetries:
+ format: int32
+ type: integer
+ retryInterval:
+ type: string
+ type: object
required:
- path
- provider
diff --git a/config/crds/bases/generators.external-secrets.io_webhooks.yaml b/config/crds/bases/generators.external-secrets.io_webhooks.yaml
index 9f3f532130a..bf3657e8886 100644
--- a/config/crds/bases/generators.external-secrets.io_webhooks.yaml
+++ b/config/crds/bases/generators.external-secrets.io_webhooks.yaml
@@ -2,18 +2,19 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: webhooks.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- - webhook
+ - external-secrets
+ - external-secrets-generators
kind: Webhook
listKind: WebhookList
plural: webhooks
- shortNames:
- - webhookl
singular: webhook
scope: Namespaced
versions:
@@ -64,14 +65,23 @@ spec:
server certificate.
properties:
key:
- description: The key the value inside of the provider type to
- use, only used with "Secret" type
+ description: The key where the CA certificate can be found in
+ the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or
@@ -113,10 +123,16 @@ spec:
properties:
key:
description: The key where the token is found.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred
to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
type: object
required:
diff --git a/config/crds/bases/kustomization.yaml b/config/crds/bases/kustomization.yaml
index 0f86d9104d0..0c7b2f3574b 100644
--- a/config/crds/bases/kustomization.yaml
+++ b/config/crds/bases/kustomization.yaml
@@ -8,7 +8,14 @@ resources:
- external-secrets.io_pushsecrets.yaml
- external-secrets.io_secretstores.yaml
- generators.external-secrets.io_acraccesstokens.yaml
+ - generators.external-secrets.io_clustergenerators.yaml
- generators.external-secrets.io_ecrauthorizationtokens.yaml
- generators.external-secrets.io_fakes.yaml
- generators.external-secrets.io_gcraccesstokens.yaml
+ - generators.external-secrets.io_githubaccesstokens.yaml
- generators.external-secrets.io_passwords.yaml
+ - generators.external-secrets.io_quayaccesstokens.yaml
+ - generators.external-secrets.io_stssessiontokens.yaml
+ - generators.external-secrets.io_uuids.yaml
+ - generators.external-secrets.io_vaultdynamicsecrets.yaml
+ - generators.external-secrets.io_webhooks.yaml
diff --git a/deploy/charts/external-secrets/.helmignore b/deploy/charts/external-secrets/.helmignore
index 855edc3fbfb..8d99189ae34 100644
--- a/deploy/charts/external-secrets/.helmignore
+++ b/deploy/charts/external-secrets/.helmignore
@@ -24,3 +24,8 @@
# CRD README.md
templates/crds/README.md
+
+ci/
+tests/
+README.md.gotmpl
+.helmignore
diff --git a/deploy/charts/external-secrets/Chart.lock b/deploy/charts/external-secrets/Chart.lock
new file mode 100644
index 00000000000..f9abae8c125
--- /dev/null
+++ b/deploy/charts/external-secrets/Chart.lock
@@ -0,0 +1,6 @@
+dependencies:
+- name: bitwarden-sdk-server
+ repository: oci://ghcr.io/external-secrets/charts
+ version: v0.3.1
+digest: sha256:2d01e9083fc32c18dca4f9614625e0172e338a663138c2670e5b911645b6b8ee
+generated: "2024-09-20T12:57:07.63511+02:00"
diff --git a/deploy/charts/external-secrets/Chart.yaml b/deploy/charts/external-secrets/Chart.yaml
index 71c1260efd8..bab12d0fdf9 100644
--- a/deploy/charts/external-secrets/Chart.yaml
+++ b/deploy/charts/external-secrets/Chart.yaml
@@ -2,8 +2,8 @@ apiVersion: v2
name: external-secrets
description: External secret management for Kubernetes
type: application
-version: "0.9.16"
-appVersion: "v0.9.16"
+version: "0.13.0"
+appVersion: "v0.13.0"
kubeVersion: ">= 1.19.0-0"
keywords:
- kubernetes-external-secrets
@@ -13,3 +13,8 @@ icon: https://raw.githubusercontent.com/external-secrets/external-secrets/main/a
maintainers:
- name: mcavoyk
email: kellinmcavoy@gmail.com
+dependencies:
+ - name: bitwarden-sdk-server
+ version: v0.3.1
+ repository: oci://ghcr.io/external-secrets/charts
+ condition: bitwarden-sdk-server.enabled
diff --git a/deploy/charts/external-secrets/README.md b/deploy/charts/external-secrets/README.md
index 4ff90e395fb..2631e979625 100644
--- a/deploy/charts/external-secrets/README.md
+++ b/deploy/charts/external-secrets/README.md
@@ -4,7 +4,7 @@
[//]: # (README.md generated by gotmpl. DO NOT EDIT.)
-![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![Version: 0.9.16](https://img.shields.io/badge/Version-0.9.16-informational?style=flat-square)
+![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![Version: 0.13.0](https://img.shields.io/badge/Version-0.13.0-informational?style=flat-square)
External secret management for Kubernetes
@@ -35,6 +35,7 @@ The command removes all the Kubernetes components associated with the chart and
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| affinity | object | `{}` | |
+| bitwarden-sdk-server.enabled | bool | `false` | |
| certController.affinity | object | `{}` | |
| certController.create | bool | `true` | Specifies whether a certificate controller deployment be created. |
| certController.deploymentAnnotations | object | `{}` | Annotations to add to Deployment |
@@ -46,9 +47,10 @@ The command removes all the Kubernetes components associated with the chart and
| certController.hostNetwork | bool | `false` | Run the certController on the host network |
| certController.image.flavour | string | `""` | |
| certController.image.pullPolicy | string | `"IfNotPresent"` | |
-| certController.image.repository | string | `"ghcr.io/external-secrets/external-secrets"` | |
+| certController.image.repository | string | `"oci.external-secrets.io/external-secrets/external-secrets"` | |
| certController.image.tag | string | `""` | |
| certController.imagePullSecrets | list | `[]` | |
+| certController.log | object | `{"level":"info","timeEncoding":"epoch"}` | Specifices Log Params to the Webhook |
| certController.metrics.listen.port | int | `8080` | |
| certController.metrics.service.annotations | object | `{}` | Additional service annotations |
| certController.metrics.service.enabled | bool | `false` | Enable if you use another monitoring tool than Prometheus to scrape the metrics |
@@ -85,13 +87,15 @@ The command removes all the Kubernetes components associated with the chart and
| concurrent | int | `1` | Specifies the number of concurrent ExternalSecret Reconciles external-secret executes at a time. |
| controllerClass | string | `""` | If set external secrets will filter matching Secret Stores with the appropriate controller values. |
| crds.annotations | object | `{}` | |
-| crds.conversion.enabled | bool | `true` | |
+| crds.conversion.enabled | bool | `true` | If webhook is set to false this also needs to be set to false otherwise the kubeapi will be hammered because the conversion is looking for a webhook endpoint. |
| crds.createClusterExternalSecret | bool | `true` | If true, create CRDs for Cluster External Secret. |
+| crds.createClusterGenerator | bool | `true` | If true, create CRDs for Cluster Generator. |
| crds.createClusterSecretStore | bool | `true` | If true, create CRDs for Cluster Secret Store. |
| crds.createPushSecret | bool | `true` | If true, create CRDs for Push Secret. |
| createOperator | bool | `true` | Specifies whether an external secret operator deployment be created. |
| deploymentAnnotations | object | `{}` | Annotations to add to Deployment |
| dnsConfig | object | `{}` | Specifies `dnsOptions` to deployment |
+| dnsPolicy | string | `"ClusterFirst"` | Specifies `dnsPolicy` to deployment |
| extendedMetricLabels | bool | `false` | If true external secrets will use recommended kubernetes annotations as prometheus metric labels. |
| extraArgs | object | `{}` | |
| extraContainers | list | `[]` | |
@@ -106,13 +110,14 @@ The command removes all the Kubernetes components associated with the chart and
| global.tolerations | list | `[]` | |
| global.topologySpreadConstraints | list | `[]` | |
| hostNetwork | bool | `false` | Run the controller on the host network |
-| image.flavour | string | `""` | The flavour of tag you want to use There are different image flavours available, like distroless and ubi. Please see GitHub release notes for image tags for these flavors. By default the distroless image is used. |
+| image.flavour | string | `""` | The flavour of tag you want to use There are different image flavours available, like distroless and ubi. Please see GitHub release notes for image tags for these flavors. By default, the distroless image is used. |
| image.pullPolicy | string | `"IfNotPresent"` | |
-| image.repository | string | `"ghcr.io/external-secrets/external-secrets"` | |
+| image.repository | string | `"oci.external-secrets.io/external-secrets/external-secrets"` | |
| image.tag | string | `""` | The image tag to use. The default is the chart appVersion. |
| imagePullSecrets | list | `[]` | |
| installCRDs | bool | `true` | If set, install and upgrade CRDs through helm chart. |
| leaderElect | bool | `false` | If true, external-secrets will perform leader election between instances to ensure no more than one instance of external-secrets operates at a time. |
+| log | object | `{"level":"info","timeEncoding":"epoch"}` | Specifices Log Params to the Webhook |
| metrics.listen.port | int | `8080` | |
| metrics.service.annotations | object | `{}` | Additional service annotations |
| metrics.service.enabled | bool | `false` | Enable if you use another monitoring tool than Prometheus to scrape the metrics |
@@ -181,9 +186,10 @@ The command removes all the Kubernetes components associated with the chart and
| webhook.hostNetwork | bool | `false` | Specifies if webhook pod should use hostNetwork or not. |
| webhook.image.flavour | string | `""` | The flavour of tag you want to use |
| webhook.image.pullPolicy | string | `"IfNotPresent"` | |
-| webhook.image.repository | string | `"ghcr.io/external-secrets/external-secrets"` | |
+| webhook.image.repository | string | `"oci.external-secrets.io/external-secrets/external-secrets"` | |
| webhook.image.tag | string | `""` | The image tag to use. The default is the chart appVersion. |
| webhook.imagePullSecrets | list | `[]` | |
+| webhook.log | object | `{"level":"info","timeEncoding":"epoch"}` | Specifices Log Params to the Webhook |
| webhook.lookaheadInterval | string | `""` | Specifices the lookaheadInterval for certificate validity |
| webhook.metrics.listen.port | int | `8080` | |
| webhook.metrics.service.annotations | object | `{}` | Additional service annotations |
diff --git a/deploy/charts/external-secrets/ci/main-values.yaml b/deploy/charts/external-secrets/ci/main-values.yaml
index 75eb234e392..61b16e836af 100644
--- a/deploy/charts/external-secrets/ci/main-values.yaml
+++ b/deploy/charts/external-secrets/ci/main-values.yaml
@@ -1,2 +1,10 @@
image:
tag: main
+
+webhook:
+ image:
+ tag: main
+
+certController:
+ image:
+ tag: main
diff --git a/deploy/charts/external-secrets/templates/_helpers.tpl b/deploy/charts/external-secrets/templates/_helpers.tpl
index 2475b1145b7..d5eea075939 100644
--- a/deploy/charts/external-secrets/templates/_helpers.tpl
+++ b/deploy/charts/external-secrets/templates/_helpers.tpl
@@ -155,8 +155,6 @@ Determine the image to use, including if using a flavour.
{{- end }}
{{- end }}
-<<<<<<< HEAD
-
{{/*
Renders a complete tree, even values that contains template.
*/}}
@@ -167,8 +165,8 @@ Renders a complete tree, even values that contains template.
{{- tpl (.value | toYaml) .context }}
{{- end }}
{{- end -}}
-=======
-{{/*
+
+{{/*
Return true if the OpenShift is the detected platform
Usage:
{{- include "external-secrets.isOpenShift" . -}}
@@ -198,4 +196,3 @@ Render the securityContext based on the provided securityContext
{{- end -}}
{{- omit $adaptedContext "enabled" | toYaml -}}
{{- end -}}
->>>>>>> 2218c78b (Methods for managing securityContext and OpenShift support)
diff --git a/deploy/charts/external-secrets/templates/cert-controller-deployment.yaml b/deploy/charts/external-secrets/templates/cert-controller-deployment.yaml
index 000b442d600..a843f045a0b 100644
--- a/deploy/charts/external-secrets/templates/cert-controller-deployment.yaml
+++ b/deploy/charts/external-secrets/templates/cert-controller-deployment.yaml
@@ -60,10 +60,15 @@ spec:
- --secret-namespace={{ template "external-secrets.namespace" . }}
- --metrics-addr=:{{ .Values.certController.metrics.listen.port }}
- --healthz-addr={{ .Values.certController.readinessProbe.address }}:{{ .Values.certController.readinessProbe.port }}
- {{ if not .Values.crds.createClusterSecretStore -}}
+ - --loglevel={{ .Values.certController.log.level }}
+ - --zap-time-encoding={{ .Values.certController.log.timeEncoding }}
+ {{- if not .Values.crds.createClusterSecretStore }}
- --crd-names=externalsecrets.external-secrets.io
- --crd-names=secretstores.external-secrets.io
- {{- end -}}
+ {{- end }}
+ {{- if .Values.installCRDs }}
+ - --enable-partial-cache=true
+ {{- end }}
{{- range $key, $value := .Values.certController.extraArgs }}
{{- if $value }}
- --{{ $key }}={{ $value }}
diff --git a/deploy/charts/external-secrets/templates/cert-controller-rbac.yaml b/deploy/charts/external-secrets/templates/cert-controller-rbac.yaml
index 43c2306a67d..84a0c110bd0 100644
--- a/deploy/charts/external-secrets/templates/cert-controller-rbac.yaml
+++ b/deploy/charts/external-secrets/templates/cert-controller-rbac.yaml
@@ -21,9 +21,17 @@ rules:
resources:
- "validatingwebhookconfigurations"
verbs:
- - "get"
- "list"
- "watch"
+ - "get"
+ - apiGroups:
+ - "admissionregistration.k8s.io"
+ resources:
+ - "validatingwebhookconfigurations"
+ resourceNames:
+ - "secretstore-validate"
+ - "externalsecret-validate"
+ verbs:
- "update"
- "patch"
- apiGroups:
diff --git a/deploy/charts/external-secrets/templates/deployment.yaml b/deploy/charts/external-secrets/templates/deployment.yaml
index 5c71d96db0c..42c0d967aec 100644
--- a/deploy/charts/external-secrets/templates/deployment.yaml
+++ b/deploy/charts/external-secrets/templates/deployment.yaml
@@ -91,6 +91,8 @@ spec:
{{- end }}
{{- end }}
- --metrics-addr=:{{ .Values.metrics.listen.port }}
+ - --loglevel={{ .Values.log.level }}
+ - --zap-time-encoding={{ .Values.log.timeEncoding }}
ports:
- containerPort: {{ .Values.metrics.listen.port }}
protocol: TCP
@@ -108,8 +110,9 @@ spec:
{{- toYaml .Values.extraVolumeMounts | nindent 12 }}
{{- end }}
{{- if .Values.extraContainers }}
- {{ toYaml .Values.extraContainers | nindent 8}}
+ {{ toYaml .Values.extraContainers | nindent 8 }}
{{- end }}
+ dnsPolicy: {{ .Values.dnsPolicy }}
{{- if .Values.dnsConfig }}
dnsConfig:
{{- toYaml .Values.dnsConfig | nindent 8 }}
diff --git a/deploy/charts/external-secrets/templates/rbac.yaml b/deploy/charts/external-secrets/templates/rbac.yaml
index 21557995a4b..69bde32c257 100644
--- a/deploy/charts/external-secrets/templates/rbac.yaml
+++ b/deploy/charts/external-secrets/templates/rbac.yaml
@@ -44,17 +44,22 @@ rules:
- "pushsecrets/status"
- "pushsecrets/finalizers"
verbs:
+ - "get"
- "update"
- "patch"
- apiGroups:
- "generators.external-secrets.io"
resources:
- "acraccesstokens"
+ - "clustergenerators"
- "ecrauthorizationtokens"
- "fakes"
- "gcraccesstokens"
- "githubaccesstokens"
+ - "quayaccesstokens"
- "passwords"
+ - "stssessiontokens"
+ - "uuids"
- "vaultdynamicsecrets"
- "webhooks"
verbs:
@@ -144,10 +149,12 @@ rules:
- "generators.external-secrets.io"
resources:
- "acraccesstokens"
+ - "clustergenerators"
- "ecrauthorizationtokens"
- "fakes"
- "gcraccesstokens"
- "githubaccesstokens"
+ - "quayaccesstokens"
- "passwords"
- "vaultdynamicsecrets"
- "webhooks"
@@ -189,10 +196,12 @@ rules:
- "generators.external-secrets.io"
resources:
- "acraccesstokens"
+ - "clustergenerators"
- "ecrauthorizationtokens"
- "fakes"
- "gcraccesstokens"
- "githubaccesstokens"
+ - "quayaccesstokens"
- "passwords"
- "vaultdynamicsecrets"
- "webhooks"
diff --git a/deploy/charts/external-secrets/templates/validatingwebhook.yaml b/deploy/charts/external-secrets/templates/validatingwebhook.yaml
index 63b39763f97..0c3183ee185 100644
--- a/deploy/charts/external-secrets/templates/validatingwebhook.yaml
+++ b/deploy/charts/external-secrets/templates/validatingwebhook.yaml
@@ -4,10 +4,8 @@ kind: ValidatingWebhookConfiguration
metadata:
name: secretstore-validate
labels:
+ {{- include "external-secrets-webhook.labels" . | nindent 4 }}
external-secrets.io/component: webhook
- {{- with .Values.commonLabels }}
- {{ toYaml . | nindent 4 }}
- {{- end }}
{{- if and .Values.webhook.certManager.enabled .Values.webhook.certManager.addInjectorAnnotations }}
annotations:
cert-manager.io/inject-ca-from: {{ template "external-secrets.namespace" . }}/{{ include "external-secrets.fullname" . }}-webhook
@@ -50,10 +48,8 @@ kind: ValidatingWebhookConfiguration
metadata:
name: externalsecret-validate
labels:
+ {{- include "external-secrets-webhook.labels" . | nindent 4 }}
external-secrets.io/component: webhook
- {{- with .Values.commonLabels }}
- {{ toYaml . | nindent 4 }}
- {{- end }}
{{- if and .Values.webhook.certManager.enabled .Values.webhook.certManager.addInjectorAnnotations }}
annotations:
cert-manager.io/inject-ca-from: {{ template "external-secrets.namespace" . }}/{{ include "external-secrets.fullname" . }}-webhook
diff --git a/deploy/charts/external-secrets/templates/webhook-deployment.yaml b/deploy/charts/external-secrets/templates/webhook-deployment.yaml
index 24692a3203c..7419a426b24 100644
--- a/deploy/charts/external-secrets/templates/webhook-deployment.yaml
+++ b/deploy/charts/external-secrets/templates/webhook-deployment.yaml
@@ -59,6 +59,8 @@ spec:
- --check-interval={{ .Values.webhook.certCheckInterval }}
- --metrics-addr=:{{ .Values.webhook.metrics.listen.port }}
- --healthz-addr={{ .Values.webhook.readinessProbe.address }}:{{ .Values.webhook.readinessProbe.port }}
+ - --loglevel={{ .Values.webhook.log.level }}
+ - --zap-time-encoding={{ .Values.webhook.log.timeEncoding }}
{{- if .Values.webhook.lookaheadInterval }}
- --lookahead-interval={{ .Values.webhook.lookaheadInterval }}
{{- end }}
diff --git a/deploy/charts/external-secrets/tests/__snapshot__/cert_controller_test.yaml.snap b/deploy/charts/external-secrets/tests/__snapshot__/cert_controller_test.yaml.snap
index 07d0b75bcfb..7a08ea421e5 100644
--- a/deploy/charts/external-secrets/tests/__snapshot__/cert_controller_test.yaml.snap
+++ b/deploy/charts/external-secrets/tests/__snapshot__/cert_controller_test.yaml.snap
@@ -7,8 +7,8 @@ should match snapshot of default values:
app.kubernetes.io/instance: RELEASE-NAME
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: external-secrets-cert-controller
- app.kubernetes.io/version: v0.9.16
- helm.sh/chart: external-secrets-0.9.16
+ app.kubernetes.io/version: v0.13.0
+ helm.sh/chart: external-secrets-0.13.0
name: RELEASE-NAME-external-secrets-cert-controller
namespace: NAMESPACE
spec:
@@ -24,8 +24,8 @@ should match snapshot of default values:
app.kubernetes.io/instance: RELEASE-NAME
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: external-secrets-cert-controller
- app.kubernetes.io/version: v0.9.16
- helm.sh/chart: external-secrets-0.9.16
+ app.kubernetes.io/version: v0.13.0
+ helm.sh/chart: external-secrets-0.13.0
spec:
automountServiceAccountToken: true
containers:
@@ -38,7 +38,10 @@ should match snapshot of default values:
- --secret-namespace=NAMESPACE
- --metrics-addr=:8080
- --healthz-addr=:8081
- image: ghcr.io/external-secrets/external-secrets:v0.9.16
+ - --loglevel=info
+ - --zap-time-encoding=epoch
+ - --enable-partial-cache=true
+ image: oci.external-secrets.io/external-secrets/external-secrets:v0.13.0
imagePullPolicy: IfNotPresent
name: cert-controller
ports:
diff --git a/deploy/charts/external-secrets/tests/__snapshot__/controller_test.yaml.snap b/deploy/charts/external-secrets/tests/__snapshot__/controller_test.yaml.snap
index d8fa3ba2560..696f66b50cb 100644
--- a/deploy/charts/external-secrets/tests/__snapshot__/controller_test.yaml.snap
+++ b/deploy/charts/external-secrets/tests/__snapshot__/controller_test.yaml.snap
@@ -7,8 +7,8 @@ should match snapshot of default values:
app.kubernetes.io/instance: RELEASE-NAME
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: external-secrets
- app.kubernetes.io/version: v0.9.16
- helm.sh/chart: external-secrets-0.9.16
+ app.kubernetes.io/version: v0.13.0
+ helm.sh/chart: external-secrets-0.13.0
name: RELEASE-NAME-external-secrets
namespace: NAMESPACE
spec:
@@ -24,15 +24,17 @@ should match snapshot of default values:
app.kubernetes.io/instance: RELEASE-NAME
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: external-secrets
- app.kubernetes.io/version: v0.9.16
- helm.sh/chart: external-secrets-0.9.16
+ app.kubernetes.io/version: v0.13.0
+ helm.sh/chart: external-secrets-0.13.0
spec:
automountServiceAccountToken: true
containers:
- args:
- --concurrent=1
- --metrics-addr=:8080
- image: ghcr.io/external-secrets/external-secrets:v0.9.16
+ - --loglevel=info
+ - --zap-time-encoding=epoch
+ image: oci.external-secrets.io/external-secrets/external-secrets:v0.13.0
imagePullPolicy: IfNotPresent
name: external-secrets
ports:
@@ -49,5 +51,6 @@ should match snapshot of default values:
runAsUser: 1000
seccompProfile:
type: RuntimeDefault
+ dnsPolicy: ClusterFirst
hostNetwork: false
serviceAccountName: RELEASE-NAME-external-secrets
diff --git a/deploy/charts/external-secrets/tests/__snapshot__/crds_test.yaml.snap b/deploy/charts/external-secrets/tests/__snapshot__/crds_test.yaml.snap
index 8f4ee544edd..dc3fc6a6650 100644
--- a/deploy/charts/external-secrets/tests/__snapshot__/crds_test.yaml.snap
+++ b/deploy/charts/external-secrets/tests/__snapshot__/crds_test.yaml.snap
@@ -4,7 +4,9 @@ should match snapshot of default values:
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: secretstores.external-secrets.io
spec:
conversion:
@@ -20,7 +22,7 @@ should match snapshot of default values:
group: external-secrets.io
names:
categories:
- - externalsecrets
+ - external-secrets
kind: SecretStore
listKind: SecretStoreList
plural: secretstores
@@ -101,16 +103,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -130,11 +141,17 @@ should match snapshot of default values:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -153,54 +170,81 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessType:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessTypeParam:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -216,13 +260,22 @@ should match snapshot of default values:
description: The provider for the CA bundle to use to validate Akeyless Gateway certificate.
properties:
key:
- description: The key the value inside of the provider type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -269,16 +322,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessKeySecretSecretRef:
@@ -286,16 +348,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -335,11 +406,17 @@ should match snapshot of default values:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -355,16 +432,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretAccessKeySecretRef:
@@ -372,16 +458,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -413,16 +508,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientSecret:
@@ -430,16 +534,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -473,11 +586,17 @@ should match snapshot of default values:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -527,16 +646,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -561,11 +689,17 @@ should match snapshot of default values:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -593,16 +727,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -631,16 +774,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -666,40 +818,58 @@ should match snapshot of default values:
properties:
clientCert:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientKey:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -719,11 +889,17 @@ should match snapshot of default values:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -734,21 +910,30 @@ should match snapshot of default values:
properties:
bearerToken:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -756,6 +941,9 @@ should match snapshot of default values:
remoteNamespace:
default: default
description: Remote namespace to fetch the secrets from
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
server:
description: configures the Kubernetes server Address.
@@ -768,13 +956,22 @@ should match snapshot of default values:
description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider'
properties:
key:
- description: The key the value inside of the provider type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -811,16 +1008,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
privatekey:
@@ -828,16 +1034,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -894,11 +1109,17 @@ should match snapshot of default values:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -923,16 +1144,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -981,16 +1211,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -1010,16 +1249,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -1029,16 +1277,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1081,11 +1338,17 @@ should match snapshot of default values:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -1111,16 +1374,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -1151,16 +1423,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -1180,11 +1461,17 @@ should match snapshot of default values:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -1212,16 +1499,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -1238,16 +1534,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1263,13 +1568,22 @@ should match snapshot of default values:
description: The provider for the CA bundle to use to validate Vault server certificate.
properties:
key:
- description: The key the value inside of the provider type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -1342,13 +1656,22 @@ should match snapshot of default values:
description: The provider for the CA bundle to use to validate webhook server certificate.
properties:
key:
- description: The key the value inside of the provider type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -1389,16 +1712,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -1430,16 +1762,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1448,21 +1789,30 @@ should match snapshot of default values:
properties:
certSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1555,6 +1905,11 @@ should match snapshot of default values:
ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in
for a ClusterSecretStore instance.
properties:
+ namespaceRegexes:
+ description: Choose namespaces by using regex matching
+ items:
+ type: string
+ type: array
namespaceSelector:
description: Choose namespace using a labelSelector
properties:
@@ -1582,11 +1937,13 @@ should match snapshot of default values:
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
@@ -1600,6 +1957,9 @@ should match snapshot of default values:
namespaces:
description: Choose namespaces by name
items:
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: array
type: object
@@ -1643,16 +2003,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -1672,11 +2041,17 @@ should match snapshot of default values:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -1695,54 +2070,81 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessType:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessTypeParam:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1759,14 +2161,23 @@ should match snapshot of default values:
properties:
key:
description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -1813,16 +2224,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessKeySecretSecretRef:
@@ -1830,16 +2250,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -1884,11 +2313,17 @@ should match snapshot of default values:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -1904,16 +2339,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretAccessKeySecretRef:
@@ -1921,16 +2365,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
sessionTokenSecretRef:
@@ -1941,16 +2394,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1958,6 +2420,9 @@ should match snapshot of default values:
externalID:
description: AWS External ID set on assumed IAM roles
type: string
+ prefix:
+ description: Prefix adds a prefix to all retrieved values.
+ type: string
region:
description: AWS Region to be used for the provider
type: string
@@ -2019,21 +2484,56 @@ should match snapshot of default values:
authSecretRef:
description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. Optional for WorkloadIdentity.
properties:
+ clientCertificate:
+ description: The Azure ClientCertificate of the service principle used for authentication.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
clientId:
description: The Azure clientId of the service principle or managed identity used for authentication.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientSecret:
@@ -2041,16 +2541,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
tenantId:
@@ -2058,16 +2567,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2114,11 +2632,17 @@ should match snapshot of default values:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -2132,6 +2656,300 @@ should match snapshot of default values:
required:
- vaultUrl
type: object
+ beyondtrust:
+ description: Beyondtrust configures this store to sync secrets using Password Safe provider.
+ properties:
+ auth:
+ description: Auth configures how the operator authenticates with Beyondtrust.
+ properties:
+ apiKey:
+ description: APIKey If not provided then ClientID/ClientSecret become required.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ certificate:
+ description: Certificate (cert.pem) for use when authenticating with an OAuth client Id using a Client Certificate.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ certificateKey:
+ description: Certificate private key (key.pem). For use when authenticating with an OAuth client Id
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ clientId:
+ description: ClientID is the API OAuth Client ID.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ clientSecret:
+ description: ClientSecret is the API OAuth Client Secret.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ type: object
+ server:
+ description: Auth configures how API server works.
+ properties:
+ apiUrl:
+ type: string
+ clientTimeOutSeconds:
+ description: Timeout specifies a time limit for requests made by this Client. The timeout includes connection time, any redirects, and reading the response body. Defaults to 45 seconds.
+ type: integer
+ retrievalType:
+ description: The secret retrieval type. SECRET = Secrets Safe (credential, text, file). MANAGED_ACCOUNT = Password Safe account associated with a system.
+ type: string
+ separator:
+ description: A character that separates the folder names.
+ type: string
+ verifyCA:
+ type: boolean
+ required:
+ - apiUrl
+ - verifyCA
+ type: object
+ required:
+ - auth
+ - server
+ type: object
+ bitwardensecretsmanager:
+ description: BitwardenSecretsManager configures this store to sync secrets using BitwardenSecretsManager provider
+ properties:
+ apiURL:
+ type: string
+ auth:
+ description: |-
+ Auth configures how secret-manager authenticates with a bitwarden machine account instance.
+ Make sure that the token being used has permissions on the given secret.
+ properties:
+ secretRef:
+ description: BitwardenSecretsManagerSecretRef contains the credential ref to the bitwarden instance.
+ properties:
+ credentials:
+ description: AccessToken used for the bitwarden instance.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - credentials
+ type: object
+ required:
+ - secretRef
+ type: object
+ bitwardenServerSDKURL:
+ type: string
+ caBundle:
+ description: |-
+ Base64 encoded certificate for the bitwarden server sdk. The sdk MUST run with HTTPS to make sure no MITM attack
+ can be performed.
+ type: string
+ caProvider:
+ description: 'see: https://external-secrets.io/latest/spec/#external-secrets.io/v1alpha1.CAProvider'
+ properties:
+ key:
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace the Provider type is in.
+ Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type:
+ description: The type of provider to use such as "Secret", or "ConfigMap".
+ enum:
+ - Secret
+ - ConfigMap
+ type: string
+ required:
+ - name
+ - type
+ type: object
+ identityURL:
+ type: string
+ organizationID:
+ description: OrganizationID determines which organization this secret store manages.
+ type: string
+ projectID:
+ description: ProjectID determines which project this secret store manages.
+ type: string
+ required:
+ - auth
+ - organizationID
+ - projectID
+ type: object
chef:
description: Chef configures this store to sync secrets with chef server
properties:
@@ -2146,16 +2964,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -2186,40 +3013,58 @@ should match snapshot of default values:
type: string
apiKeyRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
userRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -2243,16 +3088,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -2270,11 +3124,17 @@ should match snapshot of default values:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -2297,14 +3157,23 @@ should match snapshot of default values:
properties:
key:
description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -2335,16 +3204,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -2359,16 +3237,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -2393,6 +3280,51 @@ should match snapshot of default values:
- clientSecret
- tenant
type: object
+ device42:
+ description: Device42 configures this store to sync secrets using the Device42 provider
+ properties:
+ auth:
+ description: Auth configures how secret-manager authenticates with a Device42 instance.
+ properties:
+ secretRef:
+ properties:
+ credentials:
+ description: Username / Password is used for authentication.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ required:
+ - secretRef
+ type: object
+ host:
+ description: URL configures the Device42 instance URL.
+ type: string
+ required:
+ - auth
+ - host
+ type: object
doppler:
description: Doppler configures this store to sync secrets using the Doppler provider
properties:
@@ -2409,16 +3341,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -2490,16 +3431,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2520,16 +3470,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2554,11 +3513,17 @@ should match snapshot of default values:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -2569,6 +3534,9 @@ should match snapshot of default values:
- serviceAccountRef
type: object
type: object
+ location:
+ description: Location optionally defines a location for a secret
+ type: string
projectID:
description: ProjectID project where secret is located
type: string
@@ -2586,16 +3554,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2651,16 +3628,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2671,26 +3657,127 @@ should match snapshot of default values:
required:
- auth
type: object
+ infisical:
+ description: Infisical configures this store to sync secrets using the Infisical provider
+ properties:
+ auth:
+ description: Auth configures how the Operator authenticates with the Infisical API
+ properties:
+ universalAuthCredentials:
+ properties:
+ clientId:
+ description: |-
+ A reference to a specific 'key' within a Secret resource.
+ In some instances, `key` is a required field.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ clientSecret:
+ description: |-
+ A reference to a specific 'key' within a Secret resource.
+ In some instances, `key` is a required field.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - clientId
+ - clientSecret
+ type: object
+ type: object
+ hostAPI:
+ default: https://app.infisical.com/api
+ type: string
+ secretsScope:
+ properties:
+ environmentSlug:
+ type: string
+ projectSlug:
+ type: string
+ recursive:
+ default: false
+ type: boolean
+ secretsPath:
+ default: /
+ type: string
+ required:
+ - environmentSlug
+ - projectSlug
+ type: object
+ required:
+ - auth
+ - secretsScope
+ type: object
keepersecurity:
description: KeeperSecurity configures this store to sync secrets using the KeeperSecurity provider
properties:
authRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
folderID:
@@ -2712,40 +3799,58 @@ should match snapshot of default values:
properties:
clientCert:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientKey:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2762,11 +3867,17 @@ should match snapshot of default values:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -2776,28 +3887,66 @@ should match snapshot of default values:
properties:
bearerToken:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
type: object
+ authRef:
+ description: A reference to a secret that contains the auth information.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
remoteNamespace:
default: default
description: Remote namespace to fetch the secrets from
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
server:
description: configures the Kubernetes server Address.
@@ -2811,14 +3960,23 @@ should match snapshot of default values:
properties:
key:
description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -2835,8 +3993,6 @@ should match snapshot of default values:
description: configures the Kubernetes server Address.
type: string
type: object
- required:
- - auth
type: object
onboardbase:
description: Onboardbase configures this store to sync secrets using the Onboardbase provider
@@ -2855,16 +4011,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
passcodeRef:
@@ -2872,16 +4037,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -2916,16 +4090,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -2963,16 +4146,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
privatekey:
@@ -2980,16 +4172,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3046,11 +4247,17 @@ should match snapshot of default values:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -3069,40 +4276,58 @@ should match snapshot of default values:
properties:
passwordSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
privateKeySecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3129,16 +4354,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -3156,6 +4390,50 @@ should match snapshot of default values:
- database
- host
type: object
+ previder:
+ description: Previder configures this store to sync secrets using the Previder provider
+ properties:
+ auth:
+ description: PreviderAuth contains a secretRef for credentials.
+ properties:
+ secretRef:
+ description: PreviderAuthSecretRef holds secret references for Previder Vault credentials.
+ properties:
+ accessToken:
+ description: The AccessToken is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - accessToken
+ type: object
+ type: object
+ baseUri:
+ type: string
+ required:
+ - auth
+ type: object
pulumi:
description: Pulumi configures this store to sync secrets using the Pulumi provider
properties:
@@ -3167,21 +4445,30 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
apiUrl:
- default: https://api.pulumi.com
+ default: https://api.pulumi.com/api/esc
description: APIURL is the URL of the Pulumi API.
type: string
environment:
@@ -3196,10 +4483,14 @@ should match snapshot of default values:
Organization are a space to collaborate on shared projects and stacks.
To create a new organization, visit https://app.pulumi.com/ and click "New Organization".
type: string
+ project:
+ description: Project is the name of the Pulumi ESC project the environment belongs to.
+ type: string
required:
- accessToken
- environment
- organization
+ - project
type: object
scaleway:
description: Scaleway
@@ -3212,16 +4503,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -3245,16 +4545,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -3267,6 +4576,87 @@ should match snapshot of default values:
- region
- secretKey
type: object
+ secretserver:
+ description: |-
+ SecretServer configures this store to sync secrets using SecretServer provider
+ https://docs.delinea.com/online-help/secret-server/start.htm
+ properties:
+ password:
+ description: Password is the secret server account password.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ serverURL:
+ description: |-
+ ServerURL
+ URL to your secret server installation
+ type: string
+ username:
+ description: Username is the secret server account username.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ required:
+ - password
+ - serverURL
+ - username
+ type: object
senhasegura:
description: Senhasegura configures this store to sync secrets using senhasegura provider
properties:
@@ -3277,21 +4667,30 @@ should match snapshot of default values:
type: string
clientSecretSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3344,16 +4743,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -3365,16 +4773,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3393,16 +4810,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -3412,16 +4838,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -3449,11 +4884,17 @@ should match snapshot of default values:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -3476,16 +4917,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretAccessKeySecretRef:
@@ -3493,16 +4943,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
sessionTokenSecretRef:
@@ -3513,16 +4972,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -3576,11 +5044,17 @@ should match snapshot of default values:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -3606,16 +5080,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3646,16 +5129,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -3675,11 +5167,17 @@ should match snapshot of default values:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -3707,16 +5205,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -3741,16 +5248,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
userPass:
@@ -3770,16 +5286,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -3805,14 +5330,23 @@ should match snapshot of default values:
properties:
key:
description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -3831,6 +5365,11 @@ should match snapshot of default values:
the option is enabled serverside.
https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header
type: boolean
+ headers:
+ additionalProperties:
+ type: string
+ description: Headers to be added in Vault request
+ type: object
namespace:
description: |-
Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows
@@ -3870,16 +5409,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
keySecretRef:
@@ -3890,16 +5438,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -3934,13 +5491,22 @@ should match snapshot of default values:
description: The provider for the CA bundle to use to validate webhook server certificate.
properties:
key:
- description: The key the value inside of the provider type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -3981,16 +5547,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -4022,16 +5597,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -4040,21 +5624,30 @@ should match snapshot of default values:
properties:
certSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -4075,16 +5668,25 @@ should match snapshot of default values:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -4093,21 +5695,30 @@ should match snapshot of default values:
properties:
certSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
diff --git a/deploy/charts/external-secrets/tests/__snapshot__/webhook_test.yaml.snap b/deploy/charts/external-secrets/tests/__snapshot__/webhook_test.yaml.snap
index ad80b9929ff..5b3f9602025 100644
--- a/deploy/charts/external-secrets/tests/__snapshot__/webhook_test.yaml.snap
+++ b/deploy/charts/external-secrets/tests/__snapshot__/webhook_test.yaml.snap
@@ -7,8 +7,8 @@ should match snapshot of default values:
app.kubernetes.io/instance: RELEASE-NAME
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: external-secrets-webhook
- app.kubernetes.io/version: v0.9.16
- helm.sh/chart: external-secrets-0.9.16
+ app.kubernetes.io/version: v0.13.0
+ helm.sh/chart: external-secrets-0.13.0
name: RELEASE-NAME-external-secrets-webhook
namespace: NAMESPACE
spec:
@@ -24,8 +24,8 @@ should match snapshot of default values:
app.kubernetes.io/instance: RELEASE-NAME
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: external-secrets-webhook
- app.kubernetes.io/version: v0.9.16
- helm.sh/chart: external-secrets-0.9.16
+ app.kubernetes.io/version: v0.13.0
+ helm.sh/chart: external-secrets-0.13.0
spec:
automountServiceAccountToken: true
containers:
@@ -37,7 +37,9 @@ should match snapshot of default values:
- --check-interval=5m
- --metrics-addr=:8080
- --healthz-addr=:8081
- image: ghcr.io/external-secrets/external-secrets:v0.9.16
+ - --loglevel=info
+ - --zap-time-encoding=epoch
+ image: oci.external-secrets.io/external-secrets/external-secrets:v0.13.0
imagePullPolicy: IfNotPresent
name: webhook
ports:
@@ -81,8 +83,8 @@ should match snapshot of default values:
app.kubernetes.io/instance: RELEASE-NAME
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: external-secrets-webhook
- app.kubernetes.io/version: v0.9.16
+ app.kubernetes.io/version: v0.13.0
external-secrets.io/component: webhook
- helm.sh/chart: external-secrets-0.9.16
+ helm.sh/chart: external-secrets-0.13.0
name: RELEASE-NAME-external-secrets-webhook
namespace: NAMESPACE
diff --git a/deploy/charts/external-secrets/tests/webhook_test.yaml b/deploy/charts/external-secrets/tests/webhook_test.yaml
index 8c6f761b084..e6a8a9a39be 100644
--- a/deploy/charts/external-secrets/tests/webhook_test.yaml
+++ b/deploy/charts/external-secrets/tests/webhook_test.yaml
@@ -161,6 +161,18 @@ tests:
templates:
- validatingwebhook.yaml
- crds/externalsecret.yaml
+ - it: should have the correct labels
+ set:
+ webhook.create: true
+ templates:
+ - validatingwebhook.yaml
+ asserts:
+ - equal:
+ path: metadata.labels["app.kubernetes.io/name"]
+ value: "external-secrets-webhook"
+ - equal:
+ path: metadata.labels["app.kubernetes.io/instance"]
+ value: "RELEASE-NAME"
- it: should override metrics port
set:
webhook.metrics.listen.port: 8888
diff --git a/deploy/charts/external-secrets/values.schema.json b/deploy/charts/external-secrets/values.schema.json
new file mode 100644
index 00000000000..f1edecd83d7
--- /dev/null
+++ b/deploy/charts/external-secrets/values.schema.json
@@ -0,0 +1,908 @@
+{
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
+ "properties": {
+ "affinity": {
+ "properties": {},
+ "type": "object"
+ },
+ "bitwarden-sdk-server": {
+ "properties": {
+ "enabled": {
+ "type": "boolean"
+ }
+ },
+ "type": "object"
+ },
+ "certController": {
+ "properties": {
+ "affinity": {
+ "properties": {},
+ "type": "object"
+ },
+ "create": {
+ "type": "boolean"
+ },
+ "deploymentAnnotations": {
+ "properties": {},
+ "type": "object"
+ },
+ "extraArgs": {
+ "properties": {},
+ "type": "object"
+ },
+ "extraEnv": {
+ "type": "array"
+ },
+ "extraVolumeMounts": {
+ "type": "array"
+ },
+ "extraVolumes": {
+ "type": "array"
+ },
+ "fullnameOverride": {
+ "type": "string"
+ },
+ "hostNetwork": {
+ "type": "boolean"
+ },
+ "image": {
+ "properties": {
+ "flavour": {
+ "type": "string"
+ },
+ "pullPolicy": {
+ "type": "string"
+ },
+ "repository": {
+ "type": "string"
+ },
+ "tag": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "imagePullSecrets": {
+ "type": "array"
+ },
+ "log": {
+ "properties": {
+ "level": {
+ "type": "string"
+ },
+ "timeEncoding": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "metrics": {
+ "properties": {
+ "listen": {
+ "properties": {
+ "port": {
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "service": {
+ "properties": {
+ "annotations": {
+ "properties": {},
+ "type": "object"
+ },
+ "enabled": {
+ "type": "boolean"
+ },
+ "port": {
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "type": "object"
+ },
+ "nameOverride": {
+ "type": "string"
+ },
+ "nodeSelector": {
+ "properties": {},
+ "type": "object"
+ },
+ "podAnnotations": {
+ "properties": {},
+ "type": "object"
+ },
+ "podDisruptionBudget": {
+ "properties": {
+ "enabled": {
+ "type": "boolean"
+ },
+ "minAvailable": {
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "podLabels": {
+ "properties": {},
+ "type": "object"
+ },
+ "podSecurityContext": {
+ "properties": {
+ "enabled": {
+ "type": "boolean"
+ }
+ },
+ "type": "object"
+ },
+ "priorityClassName": {
+ "type": "string"
+ },
+ "rbac": {
+ "properties": {
+ "create": {
+ "type": "boolean"
+ }
+ },
+ "type": "object"
+ },
+ "readinessProbe": {
+ "properties": {
+ "address": {
+ "type": "string"
+ },
+ "port": {
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "replicaCount": {
+ "type": "integer"
+ },
+ "requeueInterval": {
+ "type": "string"
+ },
+ "resources": {
+ "properties": {},
+ "type": "object"
+ },
+ "revisionHistoryLimit": {
+ "type": "integer"
+ },
+ "securityContext": {
+ "properties": {
+ "allowPrivilegeEscalation": {
+ "type": "boolean"
+ },
+ "capabilities": {
+ "properties": {
+ "drop": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object"
+ },
+ "enabled": {
+ "type": "boolean"
+ },
+ "readOnlyRootFilesystem": {
+ "type": "boolean"
+ },
+ "runAsNonRoot": {
+ "type": "boolean"
+ },
+ "runAsUser": {
+ "type": "integer"
+ },
+ "seccompProfile": {
+ "properties": {
+ "type": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "type": "object"
+ },
+ "serviceAccount": {
+ "properties": {
+ "annotations": {
+ "properties": {},
+ "type": "object"
+ },
+ "automount": {
+ "type": "boolean"
+ },
+ "create": {
+ "type": "boolean"
+ },
+ "extraLabels": {
+ "properties": {},
+ "type": "object"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "tolerations": {
+ "type": "array"
+ },
+ "topologySpreadConstraints": {
+ "type": "array"
+ }
+ },
+ "type": "object"
+ },
+ "commonLabels": {
+ "properties": {},
+ "type": "object"
+ },
+ "concurrent": {
+ "type": "integer"
+ },
+ "controllerClass": {
+ "type": "string"
+ },
+ "crds": {
+ "properties": {
+ "annotations": {
+ "properties": {},
+ "type": "object"
+ },
+ "conversion": {
+ "properties": {
+ "enabled": {
+ "type": "boolean"
+ }
+ },
+ "type": "object"
+ },
+ "createClusterExternalSecret": {
+ "type": "boolean"
+ },
+ "createClusterGenerator": {
+ "type": "boolean"
+ },
+ "createClusterSecretStore": {
+ "type": "boolean"
+ },
+ "createPushSecret": {
+ "type": "boolean"
+ }
+ },
+ "type": "object"
+ },
+ "createOperator": {
+ "type": "boolean"
+ },
+ "deploymentAnnotations": {
+ "properties": {},
+ "type": "object"
+ },
+ "dnsConfig": {
+ "properties": {},
+ "type": "object"
+ },
+ "dnsPolicy": {
+ "type": "string"
+ },
+ "extendedMetricLabels": {
+ "type": "boolean"
+ },
+ "extraArgs": {
+ "properties": {},
+ "type": "object"
+ },
+ "extraContainers": {
+ "type": "array"
+ },
+ "extraEnv": {
+ "type": "array"
+ },
+ "extraObjects": {
+ "type": "array"
+ },
+ "extraVolumeMounts": {
+ "type": "array"
+ },
+ "extraVolumes": {
+ "type": "array"
+ },
+ "fullnameOverride": {
+ "type": "string"
+ },
+ "global": {
+ "properties": {
+ "affinity": {
+ "properties": {},
+ "type": "object"
+ },
+ "compatibility": {
+ "properties": {
+ "openshift": {
+ "properties": {
+ "adaptSecurityContext": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "type": "object"
+ },
+ "nodeSelector": {
+ "properties": {},
+ "type": "object"
+ },
+ "tolerations": {
+ "type": "array"
+ },
+ "topologySpreadConstraints": {
+ "type": "array"
+ }
+ },
+ "type": "object"
+ },
+ "hostNetwork": {
+ "type": "boolean"
+ },
+ "image": {
+ "properties": {
+ "flavour": {
+ "type": "string"
+ },
+ "pullPolicy": {
+ "type": "string"
+ },
+ "repository": {
+ "type": "string"
+ },
+ "tag": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "imagePullSecrets": {
+ "type": "array"
+ },
+ "installCRDs": {
+ "type": "boolean"
+ },
+ "leaderElect": {
+ "type": "boolean"
+ },
+ "log": {
+ "properties": {
+ "level": {
+ "type": "string"
+ },
+ "timeEncoding": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "metrics": {
+ "properties": {
+ "listen": {
+ "properties": {
+ "port": {
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "service": {
+ "properties": {
+ "annotations": {
+ "properties": {},
+ "type": "object"
+ },
+ "enabled": {
+ "type": "boolean"
+ },
+ "port": {
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "type": "object"
+ },
+ "nameOverride": {
+ "type": "string"
+ },
+ "namespaceOverride": {
+ "type": "string"
+ },
+ "nodeSelector": {
+ "properties": {},
+ "type": "object"
+ },
+ "podAnnotations": {
+ "properties": {},
+ "type": "object"
+ },
+ "podDisruptionBudget": {
+ "properties": {
+ "enabled": {
+ "type": "boolean"
+ },
+ "minAvailable": {
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "podLabels": {
+ "properties": {},
+ "type": "object"
+ },
+ "podSecurityContext": {
+ "properties": {
+ "enabled": {
+ "type": "boolean"
+ }
+ },
+ "type": "object"
+ },
+ "podSpecExtra": {
+ "properties": {},
+ "type": "object"
+ },
+ "priorityClassName": {
+ "type": "string"
+ },
+ "processClusterExternalSecret": {
+ "type": "boolean"
+ },
+ "processClusterStore": {
+ "type": "boolean"
+ },
+ "processPushSecret": {
+ "type": "boolean"
+ },
+ "rbac": {
+ "properties": {
+ "create": {
+ "type": "boolean"
+ },
+ "servicebindings": {
+ "properties": {
+ "create": {
+ "type": "boolean"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "type": "object"
+ },
+ "replicaCount": {
+ "type": "integer"
+ },
+ "resources": {
+ "properties": {},
+ "type": "object"
+ },
+ "revisionHistoryLimit": {
+ "type": "integer"
+ },
+ "scopedNamespace": {
+ "type": "string"
+ },
+ "scopedRBAC": {
+ "type": "boolean"
+ },
+ "securityContext": {
+ "properties": {
+ "allowPrivilegeEscalation": {
+ "type": "boolean"
+ },
+ "capabilities": {
+ "properties": {
+ "drop": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object"
+ },
+ "enabled": {
+ "type": "boolean"
+ },
+ "readOnlyRootFilesystem": {
+ "type": "boolean"
+ },
+ "runAsNonRoot": {
+ "type": "boolean"
+ },
+ "runAsUser": {
+ "type": "integer"
+ },
+ "seccompProfile": {
+ "properties": {
+ "type": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "type": "object"
+ },
+ "service": {
+ "properties": {
+ "ipFamilies": {
+ "type": "array"
+ },
+ "ipFamilyPolicy": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "serviceAccount": {
+ "properties": {
+ "annotations": {
+ "properties": {},
+ "type": "object"
+ },
+ "automount": {
+ "type": "boolean"
+ },
+ "create": {
+ "type": "boolean"
+ },
+ "extraLabels": {
+ "properties": {},
+ "type": "object"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "serviceMonitor": {
+ "properties": {
+ "additionalLabels": {
+ "properties": {},
+ "type": "object"
+ },
+ "enabled": {
+ "type": "boolean"
+ },
+ "honorLabels": {
+ "type": "boolean"
+ },
+ "interval": {
+ "type": "string"
+ },
+ "metricRelabelings": {
+ "type": "array"
+ },
+ "namespace": {
+ "type": "string"
+ },
+ "relabelings": {
+ "type": "array"
+ },
+ "scrapeTimeout": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "tolerations": {
+ "type": "array"
+ },
+ "topologySpreadConstraints": {
+ "type": "array"
+ },
+ "webhook": {
+ "properties": {
+ "affinity": {
+ "properties": {},
+ "type": "object"
+ },
+ "certCheckInterval": {
+ "type": "string"
+ },
+ "certDir": {
+ "type": "string"
+ },
+ "certManager": {
+ "properties": {
+ "addInjectorAnnotations": {
+ "type": "boolean"
+ },
+ "cert": {
+ "properties": {
+ "annotations": {
+ "properties": {},
+ "type": "object"
+ },
+ "create": {
+ "type": "boolean"
+ },
+ "duration": {
+ "type": "string"
+ },
+ "issuerRef": {
+ "properties": {
+ "group": {
+ "type": "string"
+ },
+ "kind": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "renewBefore": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "enabled": {
+ "type": "boolean"
+ }
+ },
+ "type": "object"
+ },
+ "create": {
+ "type": "boolean"
+ },
+ "deploymentAnnotations": {
+ "properties": {},
+ "type": "object"
+ },
+ "extraArgs": {
+ "properties": {},
+ "type": "object"
+ },
+ "extraEnv": {
+ "type": "array"
+ },
+ "extraVolumeMounts": {
+ "type": "array"
+ },
+ "extraVolumes": {
+ "type": "array"
+ },
+ "failurePolicy": {
+ "type": "string"
+ },
+ "fullnameOverride": {
+ "type": "string"
+ },
+ "hostNetwork": {
+ "type": "boolean"
+ },
+ "image": {
+ "properties": {
+ "flavour": {
+ "type": "string"
+ },
+ "pullPolicy": {
+ "type": "string"
+ },
+ "repository": {
+ "type": "string"
+ },
+ "tag": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "imagePullSecrets": {
+ "type": "array"
+ },
+ "log": {
+ "properties": {
+ "level": {
+ "type": "string"
+ },
+ "timeEncoding": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "lookaheadInterval": {
+ "type": "string"
+ },
+ "metrics": {
+ "properties": {
+ "listen": {
+ "properties": {
+ "port": {
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "service": {
+ "properties": {
+ "annotations": {
+ "properties": {},
+ "type": "object"
+ },
+ "enabled": {
+ "type": "boolean"
+ },
+ "port": {
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "type": "object"
+ },
+ "nameOverride": {
+ "type": "string"
+ },
+ "nodeSelector": {
+ "properties": {},
+ "type": "object"
+ },
+ "podAnnotations": {
+ "properties": {},
+ "type": "object"
+ },
+ "podDisruptionBudget": {
+ "properties": {
+ "enabled": {
+ "type": "boolean"
+ },
+ "minAvailable": {
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "podLabels": {
+ "properties": {},
+ "type": "object"
+ },
+ "podSecurityContext": {
+ "properties": {
+ "enabled": {
+ "type": "boolean"
+ }
+ },
+ "type": "object"
+ },
+ "port": {
+ "type": "integer"
+ },
+ "priorityClassName": {
+ "type": "string"
+ },
+ "rbac": {
+ "properties": {
+ "create": {
+ "type": "boolean"
+ }
+ },
+ "type": "object"
+ },
+ "readinessProbe": {
+ "properties": {
+ "address": {
+ "type": "string"
+ },
+ "port": {
+ "type": "integer"
+ }
+ },
+ "type": "object"
+ },
+ "replicaCount": {
+ "type": "integer"
+ },
+ "resources": {
+ "properties": {},
+ "type": "object"
+ },
+ "revisionHistoryLimit": {
+ "type": "integer"
+ },
+ "secretAnnotations": {
+ "properties": {},
+ "type": "object"
+ },
+ "securityContext": {
+ "properties": {
+ "allowPrivilegeEscalation": {
+ "type": "boolean"
+ },
+ "capabilities": {
+ "properties": {
+ "drop": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array"
+ }
+ },
+ "type": "object"
+ },
+ "enabled": {
+ "type": "boolean"
+ },
+ "readOnlyRootFilesystem": {
+ "type": "boolean"
+ },
+ "runAsNonRoot": {
+ "type": "boolean"
+ },
+ "runAsUser": {
+ "type": "integer"
+ },
+ "seccompProfile": {
+ "properties": {
+ "type": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "type": "object"
+ },
+ "serviceAccount": {
+ "properties": {
+ "annotations": {
+ "properties": {},
+ "type": "object"
+ },
+ "automount": {
+ "type": "boolean"
+ },
+ "create": {
+ "type": "boolean"
+ },
+ "extraLabels": {
+ "properties": {},
+ "type": "object"
+ },
+ "name": {
+ "type": "string"
+ }
+ },
+ "type": "object"
+ },
+ "tolerations": {
+ "type": "array"
+ },
+ "topologySpreadConstraints": {
+ "type": "array"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "type": "object"
+}
diff --git a/deploy/charts/external-secrets/values.yaml b/deploy/charts/external-secrets/values.yaml
index bc795ceb553..071e7373712 100644
--- a/deploy/charts/external-secrets/values.yaml
+++ b/deploy/charts/external-secrets/values.yaml
@@ -14,18 +14,21 @@ global:
replicaCount: 1
+bitwarden-sdk-server:
+ enabled: false
+
# -- Specifies the amount of historic ReplicaSets k8s should keep (see https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#clean-up-policy)
revisionHistoryLimit: 10
image:
- repository: ghcr.io/external-secrets/external-secrets
+ repository: oci.external-secrets.io/external-secrets/external-secrets
pullPolicy: IfNotPresent
# -- The image tag to use. The default is the chart appVersion.
tag: ""
# -- The flavour of tag you want to use
# There are different image flavours available, like distroless and ubi.
# Please see GitHub release notes for image tags for these flavors.
- # By default the distroless image is used.
+ # By default, the distroless image is used.
flavour: ""
# -- If set, install and upgrade CRDs through helm chart.
@@ -36,10 +39,13 @@ crds:
createClusterExternalSecret: true
# -- If true, create CRDs for Cluster Secret Store.
createClusterSecretStore: true
+ # -- If true, create CRDs for Cluster Generator.
+ createClusterGenerator: true
# -- If true, create CRDs for Push Secret.
createPushSecret: true
annotations: {}
conversion:
+ # -- If webhook is set to false this also needs to be set to false otherwise the kubeapi will be hammered because the conversion is looking for a webhook endpoint.
enabled: true
imagePullSecrets: []
@@ -85,7 +91,10 @@ createOperator: true
# -- Specifies the number of concurrent ExternalSecret Reconciles external-secret executes at
# a time.
concurrent: 1
-
+# -- Specifices Log Params to the Webhook
+log:
+ level: info
+ timeEncoding: epoch
service:
# -- Set the ip family policy to configure dual-stack see [Configure dual-stack](https://kubernetes.io/docs/concepts/services-networking/dual-stack/#services)
ipFamilyPolicy: ""
@@ -240,7 +249,10 @@ webhook:
# -- Specifices the lookaheadInterval for certificate validity
lookaheadInterval: ""
replicaCount: 1
-
+ # -- Specifices Log Params to the Webhook
+ log:
+ level: info
+ timeEncoding: epoch
# -- Specifies the amount of historic ReplicaSets k8s should keep (see https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#clean-up-policy)
revisionHistoryLimit: 10
@@ -250,7 +262,7 @@ webhook:
# -- Specifies if webhook pod should use hostNetwork or not.
hostNetwork: false
image:
- repository: ghcr.io/external-secrets/external-secrets
+ repository: oci.external-secrets.io/external-secrets/external-secrets
pullPolicy: IfNotPresent
# -- The image tag to use. The default is the chart appVersion.
tag: ""
@@ -400,12 +412,15 @@ certController:
create: true
requeueInterval: "5m"
replicaCount: 1
-
+ # -- Specifices Log Params to the Webhook
+ log:
+ level: info
+ timeEncoding: epoch
# -- Specifies the amount of historic ReplicaSets k8s should keep (see https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#clean-up-policy)
revisionHistoryLimit: 10
image:
- repository: ghcr.io/external-secrets/external-secrets
+ repository: oci.external-secrets.io/external-secrets/external-secrets
pullPolicy: IfNotPresent
tag: ""
flavour: ""
@@ -510,6 +525,9 @@ certController:
# cpu: 10m
# memory: 32Mi
+# -- Specifies `dnsPolicy` to deployment
+dnsPolicy: ClusterFirst
+
# -- Specifies `dnsOptions` to deployment
dnsConfig: {}
diff --git a/deploy/crds/bundle.yaml b/deploy/crds/bundle.yaml
index 40d7fb18370..5c8e2105842 100644
--- a/deploy/crds/bundle.yaml
+++ b/deploy/crds/bundle.yaml
@@ -2,13 +2,15 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: clusterexternalsecrets.external-secrets.io
spec:
group: external-secrets.io
names:
categories:
- - externalsecrets
+ - external-secrets
kind: ClusterExternalSecret
listKind: ClusterExternalSecretList
plural: clusterexternalsecrets
@@ -65,7 +67,12 @@ spec:
type: object
type: object
externalSecretName:
- description: The name of the external secrets to be created defaults to the name of the ClusterExternalSecret
+ description: |-
+ The name of the external secrets to be created.
+ Defaults to the name of the ClusterExternalSecret
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
externalSecretSpec:
description: The spec for the ExternalSecrets to be created
@@ -116,21 +123,22 @@ spec:
- key
type: object
secretKey:
- description: |-
- SecretKey defines the key in which the controller stores
- the value. This is the key in the Kind=Secret
+ description: The key in the Kubernetes Secret to store the value.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
sourceRef:
description: |-
SourceRef allows you to override the source
- from which the value will pulled from.
+ from which the value will be pulled.
maxProperties: 1
+ minProperties: 1
properties:
generatorRef:
description: |-
GeneratorRef points to a generator custom resource.
-
Deprecated: The generatorRef is not implemented in .data[].
this will be removed with v1.
properties:
@@ -139,10 +147,26 @@ spec:
description: Specify the apiVersion of the generator resource
type: string
kind:
- description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc.
+ description: Specify the Kind of the generator resource
+ enum:
+ - ACRAccessToken
+ - ClusterGenerator
+ - ECRAuthorizationToken
+ - Fake
+ - GCRAccessToken
+ - GithubAccessToken
+ - QuayAccessToken
+ - Password
+ - STSSessionToken
+ - UUID
+ - VaultDynamicSecret
+ - Webhook
type: string
name:
description: Specify the name of the generator resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- kind
@@ -155,12 +179,16 @@ spec:
description: |-
Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
Defaults to `SecretStore`
+ enum:
+ - SecretStore
+ - ClusterSecretStore
type: string
name:
description: Name of the SecretStore resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
- required:
- - name
type: object
type: object
required:
@@ -296,6 +324,7 @@ spec:
When sourceRef points to a generator Extract or Find is not supported.
The generator returns a static map of values
maxProperties: 1
+ minProperties: 1
properties:
generatorRef:
description: GeneratorRef points to a generator custom resource.
@@ -305,10 +334,26 @@ spec:
description: Specify the apiVersion of the generator resource
type: string
kind:
- description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc.
+ description: Specify the Kind of the generator resource
+ enum:
+ - ACRAccessToken
+ - ClusterGenerator
+ - ECRAuthorizationToken
+ - Fake
+ - GCRAccessToken
+ - GithubAccessToken
+ - QuayAccessToken
+ - Password
+ - STSSessionToken
+ - UUID
+ - VaultDynamicSecret
+ - Webhook
type: string
name:
description: Specify the name of the generator resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- kind
@@ -321,12 +366,16 @@ spec:
description: |-
Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
Defaults to `SecretStore`
+ enum:
+ - SecretStore
+ - ClusterSecretStore
type: string
name:
description: Name of the SecretStore resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
- required:
- - name
type: object
type: object
type: object
@@ -334,8 +383,10 @@ spec:
refreshInterval:
default: 1h
description: |-
- RefreshInterval is the amount of time before the values are read again from the SecretStore provider
+ RefreshInterval is the amount of time before the values are read again from the SecretStore provider,
+ specified as Golang Duration strings.
Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h"
+ Example values: "1h", "2h30m", "5d", "10s"
May be set to zero to fetch and create it once. Defaults to 1h.
type: string
secretStoreRef:
@@ -345,12 +396,16 @@ spec:
description: |-
Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
Defaults to `SecretStore`
+ enum:
+ - SecretStore
+ - ClusterSecretStore
type: string
name:
description: Name of the SecretStore resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
- required:
- - name
type: object
target:
default:
@@ -363,8 +418,8 @@ spec:
creationPolicy:
default: Owner
description: |-
- CreationPolicy defines rules on how to create the resulting Secret
- Defaults to 'Owner'
+ CreationPolicy defines rules on how to create the resulting Secret.
+ Defaults to "Owner"
enum:
- Owner
- Orphan
@@ -374,8 +429,8 @@ spec:
deletionPolicy:
default: Retain
description: |-
- DeletionPolicy defines rules on how to delete the resulting Secret
- Defaults to 'Retain'
+ DeletionPolicy defines rules on how to delete the resulting Secret.
+ Defaults to "Retain"
enum:
- Delete
- Merge
@@ -386,9 +441,11 @@ spec:
type: boolean
name:
description: |-
- Name defines the name of the Secret resource to be managed
- This field is immutable
+ The name of the Secret resource to be managed.
Defaults to the .metadata.name of the ExternalSecret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
template:
description: Template defines a blueprint for the created Secret resource.
@@ -431,9 +488,14 @@ spec:
configMap:
properties:
items:
+ description: A list of keys in the ConfigMap/Secret to use as templates for Secret data
items:
properties:
key:
+ description: A key in the ConfigMap/Secret
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
templateAs:
default: Values
@@ -446,6 +508,10 @@ spec:
type: object
type: array
name:
+ description: The name of the ConfigMap/Secret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- items
@@ -456,9 +522,14 @@ spec:
secret:
properties:
items:
+ description: A list of keys in the ConfigMap/Secret to use as templates for Secret data
items:
properties:
key:
+ description: A key in the ConfigMap/Secret
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
templateAs:
default: Values
@@ -471,6 +542,10 @@ spec:
type: object
type: array
name:
+ description: The name of the ConfigMap/Secret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- items
@@ -519,11 +594,13 @@ spec:
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
@@ -566,11 +643,13 @@ spec:
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
@@ -585,6 +664,9 @@ spec:
namespaces:
description: Choose namespaces by name. This field is ORed with anything that NamespaceSelectors ends up choosing.
items:
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: array
refreshTime:
@@ -654,13 +736,15 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: clustersecretstores.external-secrets.io
spec:
group: external-secrets.io
names:
categories:
- - externalsecrets
+ - external-secrets
kind: ClusterSecretStore
listKind: ClusterSecretStoreList
plural: clustersecretstores
@@ -741,16 +825,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -770,11 +863,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -793,54 +892,81 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessType:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessTypeParam:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -856,13 +982,22 @@ spec:
description: The provider for the CA bundle to use to validate Akeyless Gateway certificate.
properties:
key:
- description: The key the value inside of the provider type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -909,16 +1044,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessKeySecretSecretRef:
@@ -926,16 +1070,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -975,11 +1128,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -995,16 +1154,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretAccessKeySecretRef:
@@ -1012,16 +1180,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1053,16 +1230,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientSecret:
@@ -1070,16 +1256,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1113,11 +1308,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -1167,16 +1368,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1201,11 +1411,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -1233,16 +1449,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1271,16 +1496,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1306,40 +1540,58 @@ spec:
properties:
clientCert:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientKey:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1359,11 +1611,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -1374,21 +1632,30 @@ spec:
properties:
bearerToken:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1396,6 +1663,9 @@ spec:
remoteNamespace:
default: default
description: Remote namespace to fetch the secrets from
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
server:
description: configures the Kubernetes server Address.
@@ -1408,13 +1678,22 @@ spec:
description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider'
properties:
key:
- description: The key the value inside of the provider type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -1451,16 +1730,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
privatekey:
@@ -1468,16 +1756,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -1534,11 +1831,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -1563,16 +1866,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1621,16 +1933,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -1650,16 +1971,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -1669,16 +1999,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1721,11 +2060,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -1751,16 +2096,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -1791,16 +2145,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -1820,11 +2183,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -1852,16 +2221,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -1878,16 +2256,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -1903,13 +2290,22 @@ spec:
description: The provider for the CA bundle to use to validate Vault server certificate.
properties:
key:
- description: The key the value inside of the provider type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -1982,13 +2378,22 @@ spec:
description: The provider for the CA bundle to use to validate webhook server certificate.
properties:
key:
- description: The key the value inside of the provider type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -2029,16 +2434,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -2070,16 +2484,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2088,21 +2511,30 @@ spec:
properties:
certSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2195,6 +2627,11 @@ spec:
ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in
for a ClusterSecretStore instance.
properties:
+ namespaceRegexes:
+ description: Choose namespaces by using regex matching
+ items:
+ type: string
+ type: array
namespaceSelector:
description: Choose namespace using a labelSelector
properties:
@@ -2222,11 +2659,13 @@ spec:
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
@@ -2240,6 +2679,9 @@ spec:
namespaces:
description: Choose namespaces by name
items:
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: array
type: object
@@ -2283,16 +2725,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -2312,11 +2763,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -2335,54 +2792,81 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessType:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessTypeParam:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2399,14 +2883,23 @@ spec:
properties:
key:
description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -2453,16 +2946,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessKeySecretSecretRef:
@@ -2470,16 +2972,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -2524,11 +3035,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -2544,16 +3061,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretAccessKeySecretRef:
@@ -2561,16 +3087,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
sessionTokenSecretRef:
@@ -2581,16 +3116,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2598,6 +3142,9 @@ spec:
externalID:
description: AWS External ID set on assumed IAM roles
type: string
+ prefix:
+ description: Prefix adds a prefix to all retrieved values.
+ type: string
region:
description: AWS Region to be used for the provider
type: string
@@ -2659,21 +3206,56 @@ spec:
authSecretRef:
description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. Optional for WorkloadIdentity.
properties:
+ clientCertificate:
+ description: The Azure ClientCertificate of the service principle used for authentication.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
clientId:
description: The Azure clientId of the service principle or managed identity used for authentication.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientSecret:
@@ -2681,16 +3263,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
tenantId:
@@ -2698,16 +3289,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -2754,11 +3354,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -2772,6 +3378,300 @@ spec:
required:
- vaultUrl
type: object
+ beyondtrust:
+ description: Beyondtrust configures this store to sync secrets using Password Safe provider.
+ properties:
+ auth:
+ description: Auth configures how the operator authenticates with Beyondtrust.
+ properties:
+ apiKey:
+ description: APIKey If not provided then ClientID/ClientSecret become required.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ certificate:
+ description: Certificate (cert.pem) for use when authenticating with an OAuth client Id using a Client Certificate.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ certificateKey:
+ description: Certificate private key (key.pem). For use when authenticating with an OAuth client Id
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ clientId:
+ description: ClientID is the API OAuth Client ID.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ clientSecret:
+ description: ClientSecret is the API OAuth Client Secret.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ type: object
+ server:
+ description: Auth configures how API server works.
+ properties:
+ apiUrl:
+ type: string
+ clientTimeOutSeconds:
+ description: Timeout specifies a time limit for requests made by this Client. The timeout includes connection time, any redirects, and reading the response body. Defaults to 45 seconds.
+ type: integer
+ retrievalType:
+ description: The secret retrieval type. SECRET = Secrets Safe (credential, text, file). MANAGED_ACCOUNT = Password Safe account associated with a system.
+ type: string
+ separator:
+ description: A character that separates the folder names.
+ type: string
+ verifyCA:
+ type: boolean
+ required:
+ - apiUrl
+ - verifyCA
+ type: object
+ required:
+ - auth
+ - server
+ type: object
+ bitwardensecretsmanager:
+ description: BitwardenSecretsManager configures this store to sync secrets using BitwardenSecretsManager provider
+ properties:
+ apiURL:
+ type: string
+ auth:
+ description: |-
+ Auth configures how secret-manager authenticates with a bitwarden machine account instance.
+ Make sure that the token being used has permissions on the given secret.
+ properties:
+ secretRef:
+ description: BitwardenSecretsManagerSecretRef contains the credential ref to the bitwarden instance.
+ properties:
+ credentials:
+ description: AccessToken used for the bitwarden instance.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - credentials
+ type: object
+ required:
+ - secretRef
+ type: object
+ bitwardenServerSDKURL:
+ type: string
+ caBundle:
+ description: |-
+ Base64 encoded certificate for the bitwarden server sdk. The sdk MUST run with HTTPS to make sure no MITM attack
+ can be performed.
+ type: string
+ caProvider:
+ description: 'see: https://external-secrets.io/latest/spec/#external-secrets.io/v1alpha1.CAProvider'
+ properties:
+ key:
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace the Provider type is in.
+ Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type:
+ description: The type of provider to use such as "Secret", or "ConfigMap".
+ enum:
+ - Secret
+ - ConfigMap
+ type: string
+ required:
+ - name
+ - type
+ type: object
+ identityURL:
+ type: string
+ organizationID:
+ description: OrganizationID determines which organization this secret store manages.
+ type: string
+ projectID:
+ description: ProjectID determines which project this secret store manages.
+ type: string
+ required:
+ - auth
+ - organizationID
+ - projectID
+ type: object
chef:
description: Chef configures this store to sync secrets with chef server
properties:
@@ -2786,16 +3686,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -2826,40 +3735,58 @@ spec:
type: string
apiKeyRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
userRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -2883,16 +3810,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -2910,11 +3846,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -2937,14 +3879,23 @@ spec:
properties:
key:
description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -2975,16 +3926,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -2999,16 +3959,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -3033,6 +4002,51 @@ spec:
- clientSecret
- tenant
type: object
+ device42:
+ description: Device42 configures this store to sync secrets using the Device42 provider
+ properties:
+ auth:
+ description: Auth configures how secret-manager authenticates with a Device42 instance.
+ properties:
+ secretRef:
+ properties:
+ credentials:
+ description: Username / Password is used for authentication.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ required:
+ - secretRef
+ type: object
+ host:
+ description: URL configures the Device42 instance URL.
+ type: string
+ required:
+ - auth
+ - host
+ type: object
doppler:
description: Doppler configures this store to sync secrets using the Doppler provider
properties:
@@ -3049,16 +4063,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3130,16 +4153,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -3160,16 +4192,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -3194,11 +4235,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -3209,6 +4256,9 @@ spec:
- serviceAccountRef
type: object
type: object
+ location:
+ description: Location optionally defines a location for a secret
+ type: string
projectID:
description: ProjectID project where secret is located
type: string
@@ -3226,16 +4276,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -3291,16 +4350,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -3311,102 +4379,227 @@ spec:
required:
- auth
type: object
- keepersecurity:
- description: KeeperSecurity configures this store to sync secrets using the KeeperSecurity provider
- properties:
- authRef:
- description: |-
- A reference to a specific 'key' within a Secret resource,
- In some instances, `key` is a required field.
- properties:
- key:
- description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
- type: string
- name:
- description: The name of the Secret resource being referred to.
- type: string
- namespace:
- description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
- type: string
- type: object
- folderID:
- type: string
- required:
- - authRef
- - folderID
- type: object
- kubernetes:
- description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider
+ infisical:
+ description: Infisical configures this store to sync secrets using the Infisical provider
properties:
auth:
- description: Auth configures how secret-manager authenticates with a Kubernetes instance.
- maxProperties: 1
- minProperties: 1
+ description: Auth configures how the Operator authenticates with the Infisical API
properties:
- cert:
- description: has both clientCert and clientKey as secretKeySelector
+ universalAuthCredentials:
properties:
- clientCert:
+ clientId:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
- clientKey:
+ clientSecret:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
+ required:
+ - clientId
+ - clientSecret
type: object
- serviceAccount:
- description: points to a service account that should be used for authentication
- properties:
- audiences:
- description: |-
- Audience specifies the `aud` claim for the service account token
- If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ type: object
+ hostAPI:
+ default: https://app.infisical.com/api
+ type: string
+ secretsScope:
+ properties:
+ environmentSlug:
+ type: string
+ projectSlug:
+ type: string
+ recursive:
+ default: false
+ type: boolean
+ secretsPath:
+ default: /
+ type: string
+ required:
+ - environmentSlug
+ - projectSlug
+ type: object
+ required:
+ - auth
+ - secretsScope
+ type: object
+ keepersecurity:
+ description: KeeperSecurity configures this store to sync secrets using the KeeperSecurity provider
+ properties:
+ authRef:
+ description: |-
+ A reference to a specific 'key' within a Secret resource.
+ In some instances, `key` is a required field.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ folderID:
+ type: string
+ required:
+ - authRef
+ - folderID
+ type: object
+ kubernetes:
+ description: Kubernetes configures this store to sync secrets using a Kubernetes cluster provider
+ properties:
+ auth:
+ description: Auth configures how secret-manager authenticates with a Kubernetes instance.
+ maxProperties: 1
+ minProperties: 1
+ properties:
+ cert:
+ description: has both clientCert and clientKey as secretKeySelector
+ properties:
+ clientCert:
+ description: |-
+ A reference to a specific 'key' within a Secret resource.
+ In some instances, `key` is a required field.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ clientKey:
+ description: |-
+ A reference to a specific 'key' within a Secret resource.
+ In some instances, `key` is a required field.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ serviceAccount:
+ description: points to a service account that should be used for authentication
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
then this audiences will be appended to the list
items:
type: string
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -3416,28 +4609,66 @@ spec:
properties:
bearerToken:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
type: object
+ authRef:
+ description: A reference to a secret that contains the auth information.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
remoteNamespace:
default: default
description: Remote namespace to fetch the secrets from
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
server:
description: configures the Kubernetes server Address.
@@ -3451,14 +4682,23 @@ spec:
properties:
key:
description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -3475,8 +4715,6 @@ spec:
description: configures the Kubernetes server Address.
type: string
type: object
- required:
- - auth
type: object
onboardbase:
description: Onboardbase configures this store to sync secrets using the Onboardbase provider
@@ -3495,16 +4733,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
passcodeRef:
@@ -3512,16 +4759,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3556,16 +4812,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3603,16 +4868,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
privatekey:
@@ -3620,16 +4894,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3686,11 +4969,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -3709,40 +4998,58 @@ spec:
properties:
passwordSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
privateKeySecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3769,16 +5076,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -3796,6 +5112,50 @@ spec:
- database
- host
type: object
+ previder:
+ description: Previder configures this store to sync secrets using the Previder provider
+ properties:
+ auth:
+ description: PreviderAuth contains a secretRef for credentials.
+ properties:
+ secretRef:
+ description: PreviderAuthSecretRef holds secret references for Previder Vault credentials.
+ properties:
+ accessToken:
+ description: The AccessToken is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - accessToken
+ type: object
+ type: object
+ baseUri:
+ type: string
+ required:
+ - auth
+ type: object
pulumi:
description: Pulumi configures this store to sync secrets using the Pulumi provider
properties:
@@ -3807,21 +5167,30 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
apiUrl:
- default: https://api.pulumi.com
+ default: https://api.pulumi.com/api/esc
description: APIURL is the URL of the Pulumi API.
type: string
environment:
@@ -3836,10 +5205,14 @@ spec:
Organization are a space to collaborate on shared projects and stacks.
To create a new organization, visit https://app.pulumi.com/ and click "New Organization".
type: string
+ project:
+ description: Project is the name of the Pulumi ESC project the environment belongs to.
+ type: string
required:
- accessToken
- environment
- organization
+ - project
type: object
scaleway:
description: Scaleway
@@ -3852,16 +5225,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -3885,16 +5267,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -3907,6 +5298,87 @@ spec:
- region
- secretKey
type: object
+ secretserver:
+ description: |-
+ SecretServer configures this store to sync secrets using SecretServer provider
+ https://docs.delinea.com/online-help/secret-server/start.htm
+ properties:
+ password:
+ description: Password is the secret server account password.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ serverURL:
+ description: |-
+ ServerURL
+ URL to your secret server installation
+ type: string
+ username:
+ description: Username is the secret server account username.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ required:
+ - password
+ - serverURL
+ - username
+ type: object
senhasegura:
description: Senhasegura configures this store to sync secrets using senhasegura provider
properties:
@@ -3917,21 +5389,30 @@ spec:
type: string
clientSecretSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -3984,16 +5465,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -4005,16 +5495,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -4033,16 +5532,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -4052,16 +5560,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -4089,11 +5606,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -4116,16 +5639,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretAccessKeySecretRef:
@@ -4133,16 +5665,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
sessionTokenSecretRef:
@@ -4153,16 +5694,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -4216,11 +5766,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -4246,16 +5802,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -4286,16 +5851,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -4315,11 +5889,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -4347,16 +5927,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -4381,16 +5970,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
userPass:
@@ -4410,16 +6008,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -4445,14 +6052,23 @@ spec:
properties:
key:
description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -4471,6 +6087,11 @@ spec:
the option is enabled serverside.
https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header
type: boolean
+ headers:
+ additionalProperties:
+ type: string
+ description: Headers to be added in Vault request
+ type: object
namespace:
description: |-
Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows
@@ -4510,16 +6131,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
keySecretRef:
@@ -4530,16 +6160,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -4574,13 +6213,22 @@ spec:
description: The provider for the CA bundle to use to validate webhook server certificate.
properties:
key:
- description: The key the value inside of the provider type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -4621,16 +6269,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -4662,16 +6319,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -4680,21 +6346,30 @@ spec:
properties:
certSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -4715,16 +6390,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -4733,21 +6417,30 @@ spec:
properties:
certSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -4816,13 +6509,15 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: externalsecrets.external-secrets.io
spec:
group: external-secrets.io
names:
categories:
- - externalsecrets
+ - external-secrets
kind: ExternalSecret
listKind: ExternalSecretList
plural: externalsecrets
@@ -4832,6 +6527,9 @@ spec:
scope: Namespaced
versions:
- additionalPrinterColumns:
+ - jsonPath: .spec.secretStoreRef.kind
+ name: Store
+ type: string
- jsonPath: .spec.secretStoreRef.name
name: Store
type: string
@@ -4895,6 +6593,10 @@ spec:
- key
type: object
secretKey:
+ description: The key in the Kubernetes Secret to store the value.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
required:
- remoteRef
@@ -4942,12 +6644,16 @@ spec:
description: |-
Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
Defaults to `SecretStore`
+ enum:
+ - SecretStore
+ - ClusterSecretStore
type: string
name:
description: Name of the SecretStore resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
- required:
- - name
type: object
target:
description: |-
@@ -4957,8 +6663,8 @@ spec:
creationPolicy:
default: Owner
description: |-
- CreationPolicy defines rules on how to create the resulting Secret
- Defaults to 'Owner'
+ CreationPolicy defines rules on how to create the resulting Secret.
+ Defaults to "Owner"
enum:
- Owner
- Merge
@@ -4969,9 +6675,11 @@ spec:
type: boolean
name:
description: |-
- Name defines the name of the Secret resource to be managed
- This field is immutable
+ The name of the Secret resource to be managed.
Defaults to the .metadata.name of the ExternalSecret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
template:
description: Template defines a blueprint for the created Secret resource.
@@ -5010,15 +6718,24 @@ spec:
configMap:
properties:
items:
+ description: A list of keys in the ConfigMap/Secret to use as templates for Secret data
items:
properties:
key:
+ description: A key in the ConfigMap/Secret
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
required:
- key
type: object
type: array
name:
+ description: The name of the ConfigMap/Secret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- items
@@ -5027,15 +6744,24 @@ spec:
secret:
properties:
items:
+ description: A list of keys in the ConfigMap/Secret to use as templates for Secret data
items:
properties:
key:
+ description: A key in the ConfigMap/Secret
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
required:
- key
type: object
type: array
name:
+ description: The name of the ConfigMap/Secret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- items
@@ -5057,10 +6783,13 @@ spec:
description: Binding represents a servicebinding.io Provisioned Service reference to the secret
properties:
name:
+ default: ""
description: |-
Name of the referent.
+ This field is effectively required, but due to backwards compatibility is
+ allowed to be empty. Instances of this type with an empty value here are
+ almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion, kind, uid?
type: string
type: object
x-kubernetes-map-type: atomic
@@ -5100,6 +6829,9 @@ spec:
subresources:
status: {}
- additionalPrinterColumns:
+ - jsonPath: .spec.secretStoreRef.kind
+ name: StoreType
+ type: string
- jsonPath: .spec.secretStoreRef.name
name: Store
type: string
@@ -5183,21 +6915,22 @@ spec:
- key
type: object
secretKey:
- description: |-
- SecretKey defines the key in which the controller stores
- the value. This is the key in the Kind=Secret
+ description: The key in the Kubernetes Secret to store the value.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
sourceRef:
description: |-
SourceRef allows you to override the source
- from which the value will pulled from.
+ from which the value will be pulled.
maxProperties: 1
+ minProperties: 1
properties:
generatorRef:
description: |-
GeneratorRef points to a generator custom resource.
-
Deprecated: The generatorRef is not implemented in .data[].
this will be removed with v1.
properties:
@@ -5206,10 +6939,26 @@ spec:
description: Specify the apiVersion of the generator resource
type: string
kind:
- description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc.
+ description: Specify the Kind of the generator resource
+ enum:
+ - ACRAccessToken
+ - ClusterGenerator
+ - ECRAuthorizationToken
+ - Fake
+ - GCRAccessToken
+ - GithubAccessToken
+ - QuayAccessToken
+ - Password
+ - STSSessionToken
+ - UUID
+ - VaultDynamicSecret
+ - Webhook
type: string
name:
description: Specify the name of the generator resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- kind
@@ -5222,12 +6971,16 @@ spec:
description: |-
Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
Defaults to `SecretStore`
+ enum:
+ - SecretStore
+ - ClusterSecretStore
type: string
name:
description: Name of the SecretStore resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
- required:
- - name
type: object
type: object
required:
@@ -5363,6 +7116,7 @@ spec:
When sourceRef points to a generator Extract or Find is not supported.
The generator returns a static map of values
maxProperties: 1
+ minProperties: 1
properties:
generatorRef:
description: GeneratorRef points to a generator custom resource.
@@ -5372,10 +7126,26 @@ spec:
description: Specify the apiVersion of the generator resource
type: string
kind:
- description: Specify the Kind of the resource, e.g. Password, ACRAccessToken etc.
+ description: Specify the Kind of the generator resource
+ enum:
+ - ACRAccessToken
+ - ClusterGenerator
+ - ECRAuthorizationToken
+ - Fake
+ - GCRAccessToken
+ - GithubAccessToken
+ - QuayAccessToken
+ - Password
+ - STSSessionToken
+ - UUID
+ - VaultDynamicSecret
+ - Webhook
type: string
name:
description: Specify the name of the generator resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- kind
@@ -5388,12 +7158,16 @@ spec:
description: |-
Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
Defaults to `SecretStore`
+ enum:
+ - SecretStore
+ - ClusterSecretStore
type: string
name:
description: Name of the SecretStore resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
- required:
- - name
type: object
type: object
type: object
@@ -5401,8 +7175,10 @@ spec:
refreshInterval:
default: 1h
description: |-
- RefreshInterval is the amount of time before the values are read again from the SecretStore provider
+ RefreshInterval is the amount of time before the values are read again from the SecretStore provider,
+ specified as Golang Duration strings.
Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h"
+ Example values: "1h", "2h30m", "5d", "10s"
May be set to zero to fetch and create it once. Defaults to 1h.
type: string
secretStoreRef:
@@ -5412,12 +7188,16 @@ spec:
description: |-
Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
Defaults to `SecretStore`
+ enum:
+ - SecretStore
+ - ClusterSecretStore
type: string
name:
description: Name of the SecretStore resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
- required:
- - name
type: object
target:
default:
@@ -5430,8 +7210,8 @@ spec:
creationPolicy:
default: Owner
description: |-
- CreationPolicy defines rules on how to create the resulting Secret
- Defaults to 'Owner'
+ CreationPolicy defines rules on how to create the resulting Secret.
+ Defaults to "Owner"
enum:
- Owner
- Orphan
@@ -5441,8 +7221,8 @@ spec:
deletionPolicy:
default: Retain
description: |-
- DeletionPolicy defines rules on how to delete the resulting Secret
- Defaults to 'Retain'
+ DeletionPolicy defines rules on how to delete the resulting Secret.
+ Defaults to "Retain"
enum:
- Delete
- Merge
@@ -5453,9 +7233,11 @@ spec:
type: boolean
name:
description: |-
- Name defines the name of the Secret resource to be managed
- This field is immutable
+ The name of the Secret resource to be managed.
Defaults to the .metadata.name of the ExternalSecret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
template:
description: Template defines a blueprint for the created Secret resource.
@@ -5498,9 +7280,14 @@ spec:
configMap:
properties:
items:
+ description: A list of keys in the ConfigMap/Secret to use as templates for Secret data
items:
properties:
key:
+ description: A key in the ConfigMap/Secret
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
templateAs:
default: Values
@@ -5513,6 +7300,10 @@ spec:
type: object
type: array
name:
+ description: The name of the ConfigMap/Secret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- items
@@ -5523,9 +7314,14 @@ spec:
secret:
properties:
items:
+ description: A list of keys in the ConfigMap/Secret to use as templates for Secret data
items:
properties:
key:
+ description: A key in the ConfigMap/Secret
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
templateAs:
default: Values
@@ -5538,6 +7334,10 @@ spec:
type: object
type: array
name:
+ description: The name of the ConfigMap/Secret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- items
@@ -5563,10 +7363,13 @@ spec:
description: Binding represents a servicebinding.io Provisioned Service reference to the secret
properties:
name:
+ default: ""
description: |-
Name of the referent.
+ This field is effectively required, but due to backwards compatibility is
+ allowed to be empty. Instances of this type with an empty value here are
+ almost certainly wrong.
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
- TODO: Add other useful fields. apiVersion, kind, uid?
type: string
type: object
x-kubernetes-map-type: atomic
@@ -5620,13 +7423,15 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: pushsecrets.external-secrets.io
spec:
group: external-secrets.io
names:
categories:
- - pushsecrets
+ - external-secrets
kind: PushSecret
listKind: PushSecretList
plural: pushsecrets
@@ -5707,7 +7512,7 @@ spec:
type: array
deletionPolicy:
default: None
- description: 'Deletion Policy to handle Secrets in the provider. Possible Values: "Delete/None". Defaults to "None".'
+ description: Deletion Policy to handle Secrets in the provider.
enum:
- Delete
- None
@@ -5720,9 +7525,10 @@ spec:
properties:
kind:
default: SecretStore
- description: |-
- Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
- Defaults to `SecretStore`
+ description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
+ enum:
+ - SecretStore
+ - ClusterSecretStore
type: string
labelSelector:
description: Optionally, sync to secret stores with label selector
@@ -5751,11 +7557,13 @@ spec:
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
@@ -5768,23 +7576,64 @@ spec:
x-kubernetes-map-type: atomic
name:
description: Optionally, sync to the SecretStore of the given name
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
type: object
type: array
selector:
description: The Secret Selector (k8s source) for the Push Secret
+ maxProperties: 1
+ minProperties: 1
properties:
+ generatorRef:
+ description: Point to a generator to create a Secret.
+ properties:
+ apiVersion:
+ default: generators.external-secrets.io/v1alpha1
+ description: Specify the apiVersion of the generator resource
+ type: string
+ kind:
+ description: Specify the Kind of the generator resource
+ enum:
+ - ACRAccessToken
+ - ClusterGenerator
+ - ECRAuthorizationToken
+ - Fake
+ - GCRAccessToken
+ - GithubAccessToken
+ - QuayAccessToken
+ - Password
+ - STSSessionToken
+ - UUID
+ - VaultDynamicSecret
+ - Webhook
+ type: string
+ name:
+ description: Specify the name of the generator resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ required:
+ - kind
+ - name
+ type: object
secret:
description: Select a Secret to Push.
properties:
name:
- description: Name of the Secret. The Secret must exist in the same namespace as the PushSecret manifest.
+ description: |-
+ Name of the Secret.
+ The Secret must exist in the same namespace as the PushSecret manifest.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- name
type: object
- required:
- - secret
type: object
template:
description: Template defines a blueprint for the created Secret resource.
@@ -5827,9 +7676,14 @@ spec:
configMap:
properties:
items:
+ description: A list of keys in the ConfigMap/Secret to use as templates for Secret data
items:
properties:
key:
+ description: A key in the ConfigMap/Secret
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
templateAs:
default: Values
@@ -5842,6 +7696,10 @@ spec:
type: object
type: array
name:
+ description: The name of the ConfigMap/Secret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- items
@@ -5852,9 +7710,14 @@ spec:
secret:
properties:
items:
+ description: A list of keys in the ConfigMap/Secret to use as templates for Secret data
items:
properties:
key:
+ description: A key in the ConfigMap/Secret
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
templateAs:
default: Values
@@ -5867,6 +7730,10 @@ spec:
type: object
type: array
name:
+ description: The name of the ConfigMap/Secret resource
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
required:
- items
@@ -5886,7 +7753,7 @@ spec:
type: object
updatePolicy:
default: Replace
- description: 'UpdatePolicy to handle Secrets in the provider. Possible Values: "Replace/IfNotExists". Defaults to "Replace".'
+ description: UpdatePolicy to handle Secrets in the provider.
enum:
- Replace
- IfNotExists
@@ -5995,13 +7862,15 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: secretstores.external-secrets.io
spec:
group: external-secrets.io
names:
categories:
- - externalsecrets
+ - external-secrets
kind: SecretStore
listKind: SecretStoreList
plural: secretstores
@@ -6082,16 +7951,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -6111,11 +7989,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -6134,54 +8018,81 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessType:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessTypeParam:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -6197,13 +8108,22 @@ spec:
description: The provider for the CA bundle to use to validate Akeyless Gateway certificate.
properties:
key:
- description: The key the value inside of the provider type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -6250,16 +8170,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessKeySecretSecretRef:
@@ -6267,16 +8196,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -6316,11 +8254,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -6336,16 +8280,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretAccessKeySecretRef:
@@ -6353,16 +8306,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -6394,16 +8356,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientSecret:
@@ -6411,16 +8382,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -6454,11 +8434,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -6508,16 +8494,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -6542,11 +8537,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -6574,16 +8575,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -6612,16 +8622,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -6647,40 +8666,58 @@ spec:
properties:
clientCert:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientKey:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -6700,11 +8737,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -6715,21 +8758,30 @@ spec:
properties:
bearerToken:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -6737,6 +8789,9 @@ spec:
remoteNamespace:
default: default
description: Remote namespace to fetch the secrets from
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
server:
description: configures the Kubernetes server Address.
@@ -6749,13 +8804,22 @@ spec:
description: 'see: https://external-secrets.io/v0.4.1/spec/#external-secrets.io/v1alpha1.CAProvider'
properties:
key:
- description: The key the value inside of the provider type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -6792,16 +8856,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
privatekey:
@@ -6809,16 +8882,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -6875,11 +8957,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -6904,16 +8992,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -6962,16 +9059,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -6991,16 +9097,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -7010,16 +9125,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -7062,11 +9186,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -7092,16 +9222,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -7132,16 +9271,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -7161,11 +9309,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -7193,16 +9347,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -7219,16 +9382,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -7244,13 +9416,22 @@ spec:
description: The provider for the CA bundle to use to validate Vault server certificate.
properties:
key:
- description: The key the value inside of the provider type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -7323,13 +9504,22 @@ spec:
description: The provider for the CA bundle to use to validate webhook server certificate.
properties:
key:
- description: The key the value inside of the provider type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -7370,16 +9560,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -7411,16 +9610,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -7429,21 +9637,30 @@ spec:
properties:
certSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -7536,6 +9753,11 @@ spec:
ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in
for a ClusterSecretStore instance.
properties:
+ namespaceRegexes:
+ description: Choose namespaces by using regex matching
+ items:
+ type: string
+ type: array
namespaceSelector:
description: Choose namespace using a labelSelector
properties:
@@ -7563,11 +9785,13 @@ spec:
items:
type: string
type: array
+ x-kubernetes-list-type: atomic
required:
- key
- operator
type: object
type: array
+ x-kubernetes-list-type: atomic
matchLabels:
additionalProperties:
type: string
@@ -7581,6 +9805,9 @@ spec:
namespaces:
description: Choose namespaces by name
items:
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: array
type: object
@@ -7624,16 +9851,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -7653,11 +9889,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -7676,54 +9918,81 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessType:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessTypeParam:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -7740,14 +10009,23 @@ spec:
properties:
key:
description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -7794,16 +10072,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
accessKeySecretSecretRef:
@@ -7811,16 +10098,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -7865,11 +10161,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -7885,16 +10187,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretAccessKeySecretRef:
@@ -7902,16 +10213,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
sessionTokenSecretRef:
@@ -7922,16 +10242,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -7939,6 +10268,9 @@ spec:
externalID:
description: AWS External ID set on assumed IAM roles
type: string
+ prefix:
+ description: Prefix adds a prefix to all retrieved values.
+ type: string
region:
description: AWS Region to be used for the provider
type: string
@@ -8000,21 +10332,56 @@ spec:
authSecretRef:
description: Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type. Optional for WorkloadIdentity.
properties:
+ clientCertificate:
+ description: The Azure ClientCertificate of the service principle used for authentication.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
clientId:
description: The Azure clientId of the service principle or managed identity used for authentication.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientSecret:
@@ -8022,16 +10389,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
tenantId:
@@ -8039,16 +10415,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -8095,11 +10480,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -8113,53 +10504,356 @@ spec:
required:
- vaultUrl
type: object
- chef:
- description: Chef configures this store to sync secrets with chef server
+ beyondtrust:
+ description: Beyondtrust configures this store to sync secrets using Password Safe provider.
properties:
auth:
- description: Auth defines the information necessary to authenticate against chef Server
+ description: Auth configures how the operator authenticates with Beyondtrust.
properties:
- secretRef:
- description: ChefAuthSecretRef holds secret references for chef server login credentials.
+ apiKey:
+ description: APIKey If not provided then ClientID/ClientSecret become required.
properties:
- privateKeySecretRef:
- description: SecretKey is the Signing Key in PEM format, used for authentication.
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
- required:
- - privateKeySecretRef
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
type: object
- required:
- - secretRef
- type: object
- serverUrl:
- description: ServerURL is the chef server URL used to connect to. If using orgs you should include your org in the url and terminate the url with a "/"
- type: string
- username:
- description: UserName should be the user ID on the chef server
- type: string
- required:
- - auth
- - serverUrl
- - username
- type: object
- conjur:
- description: Conjur configures this store to sync secrets using conjur provider
- properties:
- auth:
+ certificate:
+ description: Certificate (cert.pem) for use when authenticating with an OAuth client Id using a Client Certificate.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ certificateKey:
+ description: Certificate private key (key.pem). For use when authenticating with an OAuth client Id
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ clientId:
+ description: ClientID is the API OAuth Client ID.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ clientSecret:
+ description: ClientSecret is the API OAuth Client Secret.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ type: object
+ server:
+ description: Auth configures how API server works.
+ properties:
+ apiUrl:
+ type: string
+ clientTimeOutSeconds:
+ description: Timeout specifies a time limit for requests made by this Client. The timeout includes connection time, any redirects, and reading the response body. Defaults to 45 seconds.
+ type: integer
+ retrievalType:
+ description: The secret retrieval type. SECRET = Secrets Safe (credential, text, file). MANAGED_ACCOUNT = Password Safe account associated with a system.
+ type: string
+ separator:
+ description: A character that separates the folder names.
+ type: string
+ verifyCA:
+ type: boolean
+ required:
+ - apiUrl
+ - verifyCA
+ type: object
+ required:
+ - auth
+ - server
+ type: object
+ bitwardensecretsmanager:
+ description: BitwardenSecretsManager configures this store to sync secrets using BitwardenSecretsManager provider
+ properties:
+ apiURL:
+ type: string
+ auth:
+ description: |-
+ Auth configures how secret-manager authenticates with a bitwarden machine account instance.
+ Make sure that the token being used has permissions on the given secret.
+ properties:
+ secretRef:
+ description: BitwardenSecretsManagerSecretRef contains the credential ref to the bitwarden instance.
+ properties:
+ credentials:
+ description: AccessToken used for the bitwarden instance.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - credentials
+ type: object
+ required:
+ - secretRef
+ type: object
+ bitwardenServerSDKURL:
+ type: string
+ caBundle:
+ description: |-
+ Base64 encoded certificate for the bitwarden server sdk. The sdk MUST run with HTTPS to make sure no MITM attack
+ can be performed.
+ type: string
+ caProvider:
+ description: 'see: https://external-secrets.io/latest/spec/#external-secrets.io/v1alpha1.CAProvider'
+ properties:
+ key:
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace the Provider type is in.
+ Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type:
+ description: The type of provider to use such as "Secret", or "ConfigMap".
+ enum:
+ - Secret
+ - ConfigMap
+ type: string
+ required:
+ - name
+ - type
+ type: object
+ identityURL:
+ type: string
+ organizationID:
+ description: OrganizationID determines which organization this secret store manages.
+ type: string
+ projectID:
+ description: ProjectID determines which project this secret store manages.
+ type: string
+ required:
+ - auth
+ - organizationID
+ - projectID
+ type: object
+ chef:
+ description: Chef configures this store to sync secrets with chef server
+ properties:
+ auth:
+ description: Auth defines the information necessary to authenticate against chef Server
+ properties:
+ secretRef:
+ description: ChefAuthSecretRef holds secret references for chef server login credentials.
+ properties:
+ privateKeySecretRef:
+ description: SecretKey is the Signing Key in PEM format, used for authentication.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - privateKeySecretRef
+ type: object
+ required:
+ - secretRef
+ type: object
+ serverUrl:
+ description: ServerURL is the chef server URL used to connect to. If using orgs you should include your org in the url and terminate the url with a "/"
+ type: string
+ username:
+ description: UserName should be the user ID on the chef server
+ type: string
+ required:
+ - auth
+ - serverUrl
+ - username
+ type: object
+ conjur:
+ description: Conjur configures this store to sync secrets using conjur provider
+ properties:
+ auth:
properties:
apikey:
properties:
@@ -8167,40 +10861,58 @@ spec:
type: string
apiKeyRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
userRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -8224,16 +10936,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -8251,11 +10972,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -8278,14 +11005,23 @@ spec:
properties:
key:
description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -8316,16 +11052,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -8340,16 +11085,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -8374,6 +11128,51 @@ spec:
- clientSecret
- tenant
type: object
+ device42:
+ description: Device42 configures this store to sync secrets using the Device42 provider
+ properties:
+ auth:
+ description: Auth configures how secret-manager authenticates with a Device42 instance.
+ properties:
+ secretRef:
+ properties:
+ credentials:
+ description: Username / Password is used for authentication.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ required:
+ - secretRef
+ type: object
+ host:
+ description: URL configures the Device42 instance URL.
+ type: string
+ required:
+ - auth
+ - host
+ type: object
doppler:
description: Doppler configures this store to sync secrets using the Doppler provider
properties:
@@ -8390,16 +11189,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -8471,16 +11279,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -8501,16 +11318,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -8535,11 +11361,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -8550,6 +11382,9 @@ spec:
- serviceAccountRef
type: object
type: object
+ location:
+ description: Location optionally defines a location for a secret
+ type: string
projectID:
description: ProjectID project where secret is located
type: string
@@ -8567,16 +11402,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -8632,16 +11476,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -8652,26 +11505,127 @@ spec:
required:
- auth
type: object
+ infisical:
+ description: Infisical configures this store to sync secrets using the Infisical provider
+ properties:
+ auth:
+ description: Auth configures how the Operator authenticates with the Infisical API
+ properties:
+ universalAuthCredentials:
+ properties:
+ clientId:
+ description: |-
+ A reference to a specific 'key' within a Secret resource.
+ In some instances, `key` is a required field.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ clientSecret:
+ description: |-
+ A reference to a specific 'key' within a Secret resource.
+ In some instances, `key` is a required field.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - clientId
+ - clientSecret
+ type: object
+ type: object
+ hostAPI:
+ default: https://app.infisical.com/api
+ type: string
+ secretsScope:
+ properties:
+ environmentSlug:
+ type: string
+ projectSlug:
+ type: string
+ recursive:
+ default: false
+ type: boolean
+ secretsPath:
+ default: /
+ type: string
+ required:
+ - environmentSlug
+ - projectSlug
+ type: object
+ required:
+ - auth
+ - secretsScope
+ type: object
keepersecurity:
description: KeeperSecurity configures this store to sync secrets using the KeeperSecurity provider
properties:
authRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
folderID:
@@ -8693,40 +11647,58 @@ spec:
properties:
clientCert:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientKey:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -8743,11 +11715,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -8757,31 +11735,69 @@ spec:
properties:
bearerToken:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
type: object
- remoteNamespace:
- default: default
- description: Remote namespace to fetch the secrets from
- type: string
- server:
- description: configures the Kubernetes server Address.
+ authRef:
+ description: A reference to a secret that contains the auth information.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ remoteNamespace:
+ default: default
+ description: Remote namespace to fetch the secrets from
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ server:
+ description: configures the Kubernetes server Address.
properties:
caBundle:
description: CABundle is a base64-encoded CA certificate
@@ -8792,14 +11808,23 @@ spec:
properties:
key:
description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -8816,8 +11841,6 @@ spec:
description: configures the Kubernetes server Address.
type: string
type: object
- required:
- - auth
type: object
onboardbase:
description: Onboardbase configures this store to sync secrets using the Onboardbase provider
@@ -8836,16 +11859,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
passcodeRef:
@@ -8853,16 +11885,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -8897,16 +11938,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -8944,16 +11994,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
privatekey:
@@ -8961,16 +12020,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -9027,11 +12095,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -9050,40 +12124,58 @@ spec:
properties:
passwordSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
privateKeySecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -9110,16 +12202,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -9137,6 +12238,50 @@ spec:
- database
- host
type: object
+ previder:
+ description: Previder configures this store to sync secrets using the Previder provider
+ properties:
+ auth:
+ description: PreviderAuth contains a secretRef for credentials.
+ properties:
+ secretRef:
+ description: PreviderAuthSecretRef holds secret references for Previder Vault credentials.
+ properties:
+ accessToken:
+ description: The AccessToken is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - accessToken
+ type: object
+ type: object
+ baseUri:
+ type: string
+ required:
+ - auth
+ type: object
pulumi:
description: Pulumi configures this store to sync secrets using the Pulumi provider
properties:
@@ -9148,21 +12293,30 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
apiUrl:
- default: https://api.pulumi.com
+ default: https://api.pulumi.com/api/esc
description: APIURL is the URL of the Pulumi API.
type: string
environment:
@@ -9177,10 +12331,14 @@ spec:
Organization are a space to collaborate on shared projects and stacks.
To create a new organization, visit https://app.pulumi.com/ and click "New Organization".
type: string
+ project:
+ description: Project is the name of the Pulumi ESC project the environment belongs to.
+ type: string
required:
- accessToken
- environment
- organization
+ - project
type: object
scaleway:
description: Scaleway
@@ -9193,16 +12351,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -9226,16 +12393,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
value:
@@ -9248,6 +12424,87 @@ spec:
- region
- secretKey
type: object
+ secretserver:
+ description: |-
+ SecretServer configures this store to sync secrets using SecretServer provider
+ https://docs.delinea.com/online-help/secret-server/start.htm
+ properties:
+ password:
+ description: Password is the secret server account password.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ serverURL:
+ description: |-
+ ServerURL
+ URL to your secret server installation
+ type: string
+ username:
+ description: Username is the secret server account username.
+ properties:
+ secretRef:
+ description: SecretRef references a key in a secret that will be used as value.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ value:
+ description: Value can be specified directly to set a value without using a secret.
+ type: string
+ type: object
+ required:
+ - password
+ - serverURL
+ - username
+ type: object
senhasegura:
description: Senhasegura configures this store to sync secrets using senhasegura provider
properties:
@@ -9258,21 +12515,30 @@ spec:
type: string
clientSecretSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -9325,16 +12591,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -9346,16 +12621,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -9374,16 +12658,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -9393,16 +12686,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -9430,11 +12732,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -9457,16 +12765,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretAccessKeySecretRef:
@@ -9474,16 +12791,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
sessionTokenSecretRef:
@@ -9494,16 +12820,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -9557,11 +12892,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -9587,16 +12928,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -9627,16 +12977,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -9656,11 +13015,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -9688,16 +13053,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -9722,16 +13096,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
userPass:
@@ -9751,16 +13134,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -9786,14 +13178,23 @@ spec:
properties:
key:
description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -9812,6 +13213,11 @@ spec:
the option is enabled serverside.
https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header
type: boolean
+ headers:
+ additionalProperties:
+ type: string
+ description: Headers to be added in Vault request
+ type: object
namespace:
description: |-
Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows
@@ -9851,16 +13257,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
keySecretRef:
@@ -9871,16 +13286,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -9915,13 +13339,22 @@ spec:
description: The provider for the CA bundle to use to validate webhook server certificate.
properties:
key:
- description: The key the value inside of the provider type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -9962,16 +13395,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -10003,16 +13445,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -10021,21 +13472,30 @@ spec:
properties:
certSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -10056,16 +13516,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -10074,21 +13543,30 @@ spec:
properties:
certSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
+ A reference to a specific 'key' within a Secret resource.
In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -10157,18 +13635,19 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: acraccesstokens.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- - acraccesstoken
+ - external-secrets
+ - external-secrets-generators
kind: ACRAccessToken
listKind: ACRAccessTokenList
plural: acraccesstokens
- shortNames:
- - acraccesstoken
singular: acraccesstoken
scope: Namespaced
versions:
@@ -10183,7 +13662,6 @@ spec:
This can be scoped down to the repository level using .spec.scope.
In case scope is defined it will return an ACR Access Token.
-
See docs: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md
properties:
apiVersion:
@@ -10231,16 +13709,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
clientSecret:
@@ -10248,16 +13735,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -10282,11 +13778,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -10317,12 +13819,10 @@ spec:
if not provided it will return a refresh token that has full scope.
Note: you need to pin it down to the repository level, there is no wildcard available.
-
examples:
repository:my-repository:pull,push
repository:my-repository:pull
-
see docs for details: https://docs.docker.com/registry/spec/auth/scope/
type: string
tenantId:
@@ -10352,31 +13852,26 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
- name: ecrauthorizationtokens.generators.external-secrets.io
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
+ name: clustergenerators.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- - ecrauthorizationtoken
- kind: ECRAuthorizationToken
- listKind: ECRAuthorizationTokenList
- plural: ecrauthorizationtokens
- shortNames:
- - ecrauthorizationtoken
- singular: ecrauthorizationtoken
- scope: Namespaced
+ - external-secrets
+ - external-secrets-generators
+ kind: ClusterGenerator
+ listKind: ClusterGeneratorList
+ plural: clustergenerators
+ singular: clustergenerator
+ scope: Cluster
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
- description: |-
- ECRAuthorizationTokenSpec uses the GetAuthorizationToken API to retrieve an
- authorization token.
- The authorization token is valid for 12 hours.
- The authorizationToken returned is a base64 encoded string that can be decoded
- and used in a docker login command to authenticate to a registry.
- For more information, see Registry authentication (https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth) in the Amazon Elastic Container Registry User Guide.
+ description: ClusterGenerator represents a cluster-wide generator which can be referenced as part of `generatorRef` fields.
properties:
apiVersion:
description: |-
@@ -10397,106 +13892,2176 @@ spec:
type: object
spec:
properties:
- auth:
- description: Auth defines how to authenticate with AWS
+ generator:
+ description: Generator the spec for this generator, must match the kind.
+ maxProperties: 1
+ minProperties: 1
properties:
- jwt:
- description: Authenticate against AWS using service account tokens.
+ acrAccessTokenSpec:
+ description: |-
+ ACRAccessTokenSpec defines how to generate the access token
+ e.g. how to authenticate and which registry to use.
+ see: https://github.com/Azure/acr/blob/main/docs/AAD-OAuth.md#overview
properties:
- serviceAccountRef:
- description: A reference to a ServiceAccount resource.
+ auth:
properties:
- audiences:
- description: |-
- Audience specifies the `aud` claim for the service account token
- If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
- then this audiences will be appended to the list
- items:
- type: string
- type: array
- name:
- description: The name of the ServiceAccount resource being referred to.
- type: string
- namespace:
- description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
- type: string
- required:
- - name
+ managedIdentity:
+ description: ManagedIdentity uses Azure Managed Identity to authenticate with Azure.
+ properties:
+ identityId:
+ description: If multiple Managed Identity is assigned to the pod, you can select the one to be used
+ type: string
+ type: object
+ servicePrincipal:
+ description: ServicePrincipal uses Azure Service Principal credentials to authenticate with Azure.
+ properties:
+ secretRef:
+ description: |-
+ Configuration used to authenticate with Azure using static
+ credentials stored in a Kind=Secret.
+ properties:
+ clientId:
+ description: The Azure clientId of the service principle used for authentication.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ clientSecret:
+ description: The Azure ClientSecret of the service principle used for authentication.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ required:
+ - secretRef
+ type: object
+ workloadIdentity:
+ description: WorkloadIdentity uses Azure Workload Identity to authenticate with Azure.
+ properties:
+ serviceAccountRef:
+ description: |-
+ ServiceAccountRef specified the service account
+ that should be used when authenticating with WorkloadIdentity.
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ type: object
type: object
+ environmentType:
+ default: PublicCloud
+ description: |-
+ EnvironmentType specifies the Azure cloud environment endpoints to use for
+ connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint.
+ The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152
+ PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud
+ enum:
+ - PublicCloud
+ - USGovernmentCloud
+ - ChinaCloud
+ - GermanCloud
+ type: string
+ registry:
+ description: |-
+ the domain name of the ACR registry
+ e.g. foobarexample.azurecr.io
+ type: string
+ scope:
+ description: |-
+ Define the scope for the access token, e.g. pull/push access for a repository.
+ if not provided it will return a refresh token that has full scope.
+ Note: you need to pin it down to the repository level, there is no wildcard available.
+
+ examples:
+ repository:my-repository:pull,push
+ repository:my-repository:pull
+
+ see docs for details: https://docs.docker.com/registry/spec/auth/scope/
+ type: string
+ tenantId:
+ description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type.
+ type: string
+ required:
+ - auth
+ - registry
type: object
- secretRef:
- description: |-
- AWSAuthSecretRef holds secret references for AWS credentials
- both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.
+ ecrAuthorizationTokenSpec:
properties:
- accessKeyIDSecretRef:
- description: The AccessKeyID is used for authentication
+ auth:
+ description: Auth defines how to authenticate with AWS
properties:
- key:
+ jwt:
+ description: Authenticate against AWS using service account tokens.
+ properties:
+ serviceAccountRef:
+ description: A reference to a ServiceAccount resource.
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ type: object
+ secretRef:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
- type: string
- name:
+ AWSAuthSecretRef holds secret references for AWS credentials
+ both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.
+ properties:
+ accessKeyIDSecretRef:
+ description: The AccessKeyID is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ secretAccessKeySecretRef:
+ description: The SecretAccessKey is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ sessionTokenSecretRef:
+ description: |-
+ The SessionToken used for authentication
+ This must be defined if AccessKeyID and SecretAccessKey are temporary credentials
+ see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ type: object
+ region:
+ description: Region specifies the region to operate in.
+ type: string
+ role:
+ description: |-
+ You can assume a role before making calls to the
+ desired AWS service.
+ type: string
+ scope:
+ description: |-
+ Scope specifies the ECR service scope.
+ Valid options are private and public.
+ type: string
+ required:
+ - region
+ type: object
+ fakeSpec:
+ description: FakeSpec contains the static data.
+ properties:
+ controller:
+ description: |-
+ Used to select the correct ESO controller (think: ingress.ingressClassName)
+ The ESO controller is instantiated with a specific controller name and filters VDS based on this property
+ type: string
+ data:
+ additionalProperties:
+ type: string
+ description: |-
+ Data defines the static data returned
+ by this generator.
+ type: object
+ type: object
+ gcrAccessTokenSpec:
+ properties:
+ auth:
+ description: Auth defines the means for authenticating with GCP
+ properties:
+ secretRef:
+ properties:
+ secretAccessKeySecretRef:
+ description: The SecretAccessKey is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ workloadIdentity:
+ properties:
+ clusterLocation:
+ type: string
+ clusterName:
+ type: string
+ clusterProjectID:
+ type: string
+ serviceAccountRef:
+ description: A reference to a ServiceAccount resource.
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ required:
+ - clusterLocation
+ - clusterName
+ - serviceAccountRef
+ type: object
+ type: object
+ projectID:
+ description: ProjectID defines which project to use to authenticate with
+ type: string
+ required:
+ - auth
+ - projectID
+ type: object
+ githubAccessTokenSpec:
+ properties:
+ appID:
+ type: string
+ auth:
+ description: Auth configures how ESO authenticates with a Github instance.
+ properties:
+ privateKey:
+ properties:
+ secretRef:
+ description: |-
+ A reference to a specific 'key' within a Secret resource.
+ In some instances, `key` is a required field.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - secretRef
+ type: object
+ required:
+ - privateKey
+ type: object
+ installID:
+ type: string
+ permissions:
+ additionalProperties:
+ type: string
+ description: Map of permissions the token will have. If omitted, defaults to all permissions the GitHub App has.
+ type: object
+ repositories:
+ description: |-
+ List of repositories the token will have access to. If omitted, defaults to all repositories the GitHub App
+ is installed to.
+ items:
+ type: string
+ type: array
+ url:
+ description: URL configures the Github instance URL. Defaults to https://github.com/.
+ type: string
+ required:
+ - appID
+ - auth
+ - installID
+ type: object
+ passwordSpec:
+ description: PasswordSpec controls the behavior of the password generator.
+ properties:
+ allowRepeat:
+ default: false
+ description: set AllowRepeat to true to allow repeating characters.
+ type: boolean
+ digits:
+ description: |-
+ Digits specifies the number of digits in the generated
+ password. If omitted it defaults to 25% of the length of the password
+ type: integer
+ length:
+ default: 24
+ description: |-
+ Length of the password to be generated.
+ Defaults to 24
+ type: integer
+ noUpper:
+ default: false
+ description: Set NoUpper to disable uppercase characters
+ type: boolean
+ symbolCharacters:
+ description: |-
+ SymbolCharacters specifies the special characters that should be used
+ in the generated password.
+ type: string
+ symbols:
+ description: |-
+ Symbols specifies the number of symbol characters in the generated
+ password. If omitted it defaults to 25% of the length of the password
+ type: integer
+ required:
+ - allowRepeat
+ - length
+ - noUpper
+ type: object
+ quayAccessTokenSpec:
+ properties:
+ robotAccount:
+ description: Name of the robot account you are federating with
+ type: string
+ serviceAccountRef:
+ description: Name of the service account you are federating with
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ url:
+ description: URL configures the Quay instance URL. Defaults to quay.io.
+ type: string
+ required:
+ - robotAccount
+ - serviceAccountRef
+ type: object
+ stsSessionTokenSpec:
+ properties:
+ auth:
+ description: Auth defines how to authenticate with AWS
+ properties:
+ jwt:
+ description: Authenticate against AWS using service account tokens.
+ properties:
+ serviceAccountRef:
+ description: A reference to a ServiceAccount resource.
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ type: object
+ secretRef:
+ description: |-
+ AWSAuthSecretRef holds secret references for AWS credentials
+ both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.
+ properties:
+ accessKeyIDSecretRef:
+ description: The AccessKeyID is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ secretAccessKeySecretRef:
+ description: The SecretAccessKey is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ sessionTokenSecretRef:
+ description: |-
+ The SessionToken used for authentication
+ This must be defined if AccessKeyID and SecretAccessKey are temporary credentials
+ see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ type: object
+ region:
+ description: Region specifies the region to operate in.
+ type: string
+ requestParameters:
+ description: RequestParameters contains parameters that can be passed to the STS service.
+ properties:
+ serialNumber:
+ description: |-
+ SerialNumber is the identification number of the MFA device that is associated with the IAM user who is making
+ the GetSessionToken call.
+ Possible values: hardware device (such as GAHT12345678) or an Amazon Resource Name (ARN) for a virtual device
+ (such as arn:aws:iam::123456789012:mfa/user)
+ type: string
+ sessionDuration:
+ description: |-
+ SessionDuration The duration, in seconds, that the credentials should remain valid. Acceptable durations for
+ IAM user sessions range from 900 seconds (15 minutes) to 129,600 seconds (36 hours), with 43,200 seconds
+ (12 hours) as the default.
+ format: int64
+ type: integer
+ tokenCode:
+ description: TokenCode is the value provided by the MFA device, if MFA is required.
+ type: string
+ type: object
+ role:
+ description: |-
+ You can assume a role before making calls to the
+ desired AWS service.
+ type: string
+ required:
+ - region
+ type: object
+ uuidSpec:
+ description: UUIDSpec controls the behavior of the uuid generator.
+ type: object
+ vaultDynamicSecretSpec:
+ properties:
+ allowEmptyResponse:
+ default: false
+ description: Do not fail if no secrets are found. Useful for requests where no data is expected.
+ type: boolean
+ controller:
+ description: |-
+ Used to select the correct ESO controller (think: ingress.ingressClassName)
+ The ESO controller is instantiated with a specific controller name and filters VDS based on this property
+ type: string
+ method:
+ description: Vault API method to use (GET/POST/other)
+ type: string
+ parameters:
+ description: Parameters to pass to Vault write (for non-GET methods)
+ x-kubernetes-preserve-unknown-fields: true
+ path:
+ description: Vault path to obtain the dynamic secret from
+ type: string
+ provider:
+ description: Vault provider common spec
+ properties:
+ auth:
+ description: Auth configures how secret-manager authenticates with the Vault server.
+ properties:
+ appRole:
+ description: |-
+ AppRole authenticates with Vault using the App Role auth mechanism,
+ with the role and secret stored in a Kubernetes Secret resource.
+ properties:
+ path:
+ default: approle
+ description: |-
+ Path where the App Role authentication backend is mounted
+ in Vault, e.g: "approle"
+ type: string
+ roleId:
+ description: |-
+ RoleID configured in the App Role authentication backend when setting
+ up the authentication backend in Vault.
+ type: string
+ roleRef:
+ description: |-
+ Reference to a key in a Secret that contains the App Role ID used
+ to authenticate with Vault.
+ The `key` field must be specified and denotes which entry within the Secret
+ resource is used as the app role id.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ secretRef:
+ description: |-
+ Reference to a key in a Secret that contains the App Role secret used
+ to authenticate with Vault.
+ The `key` field must be specified and denotes which entry within the Secret
+ resource is used as the app role secret.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - path
+ - secretRef
+ type: object
+ cert:
+ description: |-
+ Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate
+ Cert authentication method
+ properties:
+ clientCert:
+ description: |-
+ ClientCert is a certificate to authenticate using the Cert Vault
+ authentication method
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ secretRef:
+ description: |-
+ SecretRef to a key in a Secret resource containing client private key to
+ authenticate with Vault using the Cert authentication method
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ iam:
+ description: |-
+ Iam authenticates with vault by passing a special AWS request signed with AWS IAM credentials
+ AWS IAM authentication method
+ properties:
+ externalID:
+ description: AWS External ID set on assumed IAM roles
+ type: string
+ jwt:
+ description: Specify a service account with IRSA enabled
+ properties:
+ serviceAccountRef:
+ description: A reference to a ServiceAccount resource.
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ type: object
+ path:
+ description: 'Path where the AWS auth method is enabled in Vault, e.g: "aws"'
+ type: string
+ region:
+ description: AWS region
+ type: string
+ role:
+ description: This is the AWS role to be assumed before talking to vault
+ type: string
+ secretRef:
+ description: Specify credentials in a Secret object
+ properties:
+ accessKeyIDSecretRef:
+ description: The AccessKeyID is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ secretAccessKeySecretRef:
+ description: The SecretAccessKey is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ sessionTokenSecretRef:
+ description: |-
+ The SessionToken used for authentication
+ This must be defined if AccessKeyID and SecretAccessKey are temporary credentials
+ see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ vaultAwsIamServerID:
+ description: 'X-Vault-AWS-IAM-Server-ID is an additional header used by Vault IAM auth method to mitigate against different types of replay attacks. More details here: https://developer.hashicorp.com/vault/docs/auth/aws'
+ type: string
+ vaultRole:
+ description: Vault Role. In vault, a role describes an identity with a set of permissions, groups, or policies you want to attach a user of the secrets engine
+ type: string
+ required:
+ - vaultRole
+ type: object
+ jwt:
+ description: |-
+ Jwt authenticates with Vault by passing role and JWT token using the
+ JWT/OIDC authentication method
+ properties:
+ kubernetesServiceAccountToken:
+ description: |-
+ Optional ServiceAccountToken specifies the Kubernetes service account for which to request
+ a token for with the `TokenRequest` API.
+ properties:
+ audiences:
+ description: |-
+ Optional audiences field that will be used to request a temporary Kubernetes service
+ account token for the service account referenced by `serviceAccountRef`.
+ Defaults to a single audience `vault` it not specified.
+ Deprecated: use serviceAccountRef.Audiences instead
+ items:
+ type: string
+ type: array
+ expirationSeconds:
+ description: |-
+ Optional expiration time in seconds that will be used to request a temporary
+ Kubernetes service account token for the service account referenced by
+ `serviceAccountRef`.
+ Deprecated: this will be removed in the future.
+ Defaults to 10 minutes.
+ format: int64
+ type: integer
+ serviceAccountRef:
+ description: Service account field containing the name of a kubernetes ServiceAccount.
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ required:
+ - serviceAccountRef
+ type: object
+ path:
+ default: jwt
+ description: |-
+ Path where the JWT authentication backend is mounted
+ in Vault, e.g: "jwt"
+ type: string
+ role:
+ description: |-
+ Role is a JWT role to authenticate using the JWT/OIDC Vault
+ authentication method
+ type: string
+ secretRef:
+ description: |-
+ Optional SecretRef that refers to a key in a Secret resource containing JWT token to
+ authenticate with Vault using the JWT/OIDC authentication method.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ required:
+ - path
+ type: object
+ kubernetes:
+ description: |-
+ Kubernetes authenticates with Vault by passing the ServiceAccount
+ token stored in the named Secret resource to the Vault server.
+ properties:
+ mountPath:
+ default: kubernetes
+ description: |-
+ Path where the Kubernetes authentication backend is mounted in Vault, e.g:
+ "kubernetes"
+ type: string
+ role:
+ description: |-
+ A required field containing the Vault Role to assume. A Role binds a
+ Kubernetes ServiceAccount with a set of Vault policies.
+ type: string
+ secretRef:
+ description: |-
+ Optional secret field containing a Kubernetes ServiceAccount JWT used
+ for authenticating with Vault. If a name is specified without a key,
+ `token` is the default. If one is not specified, the one bound to
+ the controller will be used.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ serviceAccountRef:
+ description: |-
+ Optional service account field containing the name of a kubernetes ServiceAccount.
+ If the service account is specified, the service account secret token JWT will be used
+ for authenticating with Vault. If the service account selector is not supplied,
+ the secretRef will be used instead.
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ required:
+ - mountPath
+ - role
+ type: object
+ ldap:
+ description: |-
+ Ldap authenticates with Vault by passing username/password pair using
+ the LDAP authentication method
+ properties:
+ path:
+ default: ldap
+ description: |-
+ Path where the LDAP authentication backend is mounted
+ in Vault, e.g: "ldap"
+ type: string
+ secretRef:
+ description: |-
+ SecretRef to a key in a Secret resource containing password for the LDAP
+ user used to authenticate with Vault using the LDAP authentication
+ method
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ username:
+ description: |-
+ Username is a LDAP user name used to authenticate using the LDAP Vault
+ authentication method
+ type: string
+ required:
+ - path
+ - username
+ type: object
+ namespace:
+ description: |-
+ Name of the vault namespace to authenticate to. This can be different than the namespace your secret is in.
+ Namespaces is a set of features within Vault Enterprise that allows
+ Vault environments to support Secure Multi-tenancy. e.g: "ns1".
+ More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces
+ This will default to Vault.Namespace field if set, or empty otherwise
+ type: string
+ tokenSecretRef:
+ description: TokenSecretRef authenticates with Vault by presenting a token.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ userPass:
+ description: UserPass authenticates with Vault by passing username/password pair
+ properties:
+ path:
+ default: user
+ description: |-
+ Path where the UserPassword authentication backend is mounted
+ in Vault, e.g: "user"
+ type: string
+ secretRef:
+ description: |-
+ SecretRef to a key in a Secret resource containing password for the
+ user used to authenticate with Vault using the UserPass authentication
+ method
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ username:
+ description: |-
+ Username is a user name used to authenticate using the UserPass Vault
+ authentication method
+ type: string
+ required:
+ - path
+ - username
+ type: object
+ type: object
+ caBundle:
+ description: |-
+ PEM encoded CA bundle used to validate Vault server certificate. Only used
+ if the Server URL is using HTTPS protocol. This parameter is ignored for
+ plain HTTP protocol connection. If not set the system root certificates
+ are used to validate the TLS connection.
+ format: byte
+ type: string
+ caProvider:
+ description: The provider for the CA bundle to use to validate Vault server certificate.
+ properties:
+ key:
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace the Provider type is in.
+ Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type:
+ description: The type of provider to use such as "Secret", or "ConfigMap".
+ enum:
+ - Secret
+ - ConfigMap
+ type: string
+ required:
+ - name
+ - type
+ type: object
+ forwardInconsistent:
+ description: |-
+ ForwardInconsistent tells Vault to forward read-after-write requests to the Vault
+ leader instead of simply retrying within a loop. This can increase performance if
+ the option is enabled serverside.
+ https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header
+ type: boolean
+ headers:
+ additionalProperties:
+ type: string
+ description: Headers to be added in Vault request
+ type: object
+ namespace:
+ description: |-
+ Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows
+ Vault environments to support Secure Multi-tenancy. e.g: "ns1".
+ More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces
+ type: string
+ path:
+ description: |-
+ Path is the mount path of the Vault KV backend endpoint, e.g:
+ "secret". The v2 KV secret engine version specific "/data" path suffix
+ for fetching secrets from Vault is optional and will be appended
+ if not present in specified path.
+ type: string
+ readYourWrites:
+ description: |-
+ ReadYourWrites ensures isolated read-after-write semantics by
+ providing discovered cluster replication states in each request.
+ More information about eventual consistency in Vault can be found here
+ https://www.vaultproject.io/docs/enterprise/consistency
+ type: boolean
+ server:
+ description: 'Server is the connection address for the Vault server, e.g: "https://vault.example.com:8200".'
+ type: string
+ tls:
+ description: |-
+ The configuration used for client side related TLS communication, when the Vault server
+ requires mutual authentication. Only used if the Server URL is using HTTPS protocol.
+ This parameter is ignored for plain HTTP protocol connection.
+ It's worth noting this configuration is different from the "TLS certificates auth method",
+ which is available under the `auth.cert` section.
+ properties:
+ certSecretRef:
+ description: |-
+ CertSecretRef is a certificate added to the transport layer
+ when communicating with the Vault server.
+ If no key for the Secret is specified, external-secret will default to 'tls.crt'.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ keySecretRef:
+ description: |-
+ KeySecretRef to a key in a Secret resource containing client private key
+ added to the transport layer when communicating with the Vault server.
+ If no key for the Secret is specified, external-secret will default to 'tls.key'.
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ version:
+ default: v2
+ description: |-
+ Version is the Vault KV secret engine version. This can be either "v1" or
+ "v2". Version defaults to "v2".
+ enum:
+ - v1
+ - v2
+ type: string
+ required:
+ - auth
+ - server
+ type: object
+ resultType:
+ default: Data
+ description: |-
+ Result type defines which data is returned from the generator.
+ By default it is the "data" section of the Vault API response.
+ When using e.g. /auth/token/create the "data" section is empty but
+ the "auth" section contains the generated token.
+ Please refer to the vault docs regarding the result data structure.
+ enum:
+ - Data
+ - Auth
+ type: string
+ retrySettings:
+ description: Used to configure http retries if failed
+ properties:
+ maxRetries:
+ format: int32
+ type: integer
+ retryInterval:
+ type: string
+ type: object
+ required:
+ - path
+ - provider
+ type: object
+ webhookSpec:
+ description: WebhookSpec controls the behavior of the external generator. Any body parameters should be passed to the server through the parameters field.
+ properties:
+ body:
+ description: Body
+ type: string
+ caBundle:
+ description: |-
+ PEM encoded CA bundle used to validate webhook server certificate. Only used
+ if the Server URL is using HTTPS protocol. This parameter is ignored for
+ plain HTTP protocol connection. If not set the system root certificates
+ are used to validate the TLS connection.
+ format: byte
+ type: string
+ caProvider:
+ description: The provider for the CA bundle to use to validate webhook server certificate.
+ properties:
+ key:
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type:
+ description: The type of provider to use such as "Secret", or "ConfigMap".
+ enum:
+ - Secret
+ - ConfigMap
+ type: string
+ required:
+ - name
+ - type
+ type: object
+ headers:
+ additionalProperties:
+ type: string
+ description: Headers
+ type: object
+ method:
+ description: Webhook Method
+ type: string
+ result:
+ description: Result formatting
+ properties:
+ jsonPath:
+ description: Json path of return value
+ type: string
+ type: object
+ secrets:
+ description: |-
+ Secrets to fill in templates
+ These secrets will be passed to the templating function as key value pairs under the given name
+ items:
+ properties:
+ name:
+ description: Name of this secret in templates
+ type: string
+ secretRef:
+ description: Secret ref to fill in credentials
+ properties:
+ key:
+ description: The key where the token is found.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ type: object
+ required:
+ - name
+ - secretRef
+ type: object
+ type: array
+ timeout:
+ description: Timeout
+ type: string
+ url:
+ description: Webhook url to call
+ type: string
+ required:
+ - result
+ - url
+ type: object
+ type: object
+ kind:
+ description: Kind the kind of this generator.
+ enum:
+ - ACRAccessToken
+ - ECRAuthorizationToken
+ - Fake
+ - GCRAccessToken
+ - GithubAccessToken
+ - QuayAccessToken
+ - Password
+ - STSSessionToken
+ - UUID
+ - VaultDynamicSecret
+ - Webhook
+ type: string
+ required:
+ - generator
+ - kind
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+ conversion:
+ strategy: Webhook
+ webhook:
+ conversionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: kubernetes
+ namespace: default
+ path: /convert
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
+ name: ecrauthorizationtokens.generators.external-secrets.io
+spec:
+ group: generators.external-secrets.io
+ names:
+ categories:
+ - external-secrets
+ - external-secrets-generators
+ kind: ECRAuthorizationToken
+ listKind: ECRAuthorizationTokenList
+ plural: ecrauthorizationtokens
+ singular: ecrauthorizationtoken
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: |-
+ ECRAuthorizationTokenSpec uses the GetAuthorizationToken API to retrieve an
+ authorization token.
+ The authorization token is valid for 12 hours.
+ The authorizationToken returned is a base64 encoded string that can be decoded
+ and used in a docker login command to authenticate to a registry.
+ For more information, see Registry authentication (https://docs.aws.amazon.com/AmazonECR/latest/userguide/Registries.html#registry_auth) in the Amazon Elastic Container Registry User Guide.
+ properties:
+ apiVersion:
+ description: |-
+ APIVersion defines the versioned schema of this representation of an object.
+ Servers should convert recognized schemas to the latest internal value, and
+ may reject unrecognized values.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+ type: string
+ kind:
+ description: |-
+ Kind is a string value representing the REST resource this object represents.
+ Servers may infer this from the endpoint the client submits requests to.
+ Cannot be updated.
+ In CamelCase.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+ type: string
+ metadata:
+ type: object
+ spec:
+ properties:
+ auth:
+ description: Auth defines how to authenticate with AWS
+ properties:
+ jwt:
+ description: Authenticate against AWS using service account tokens.
+ properties:
+ serviceAccountRef:
+ description: A reference to a ServiceAccount resource.
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ type: object
+ secretRef:
+ description: |-
+ AWSAuthSecretRef holds secret references for AWS credentials
+ both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.
+ properties:
+ accessKeyIDSecretRef:
+ description: The AccessKeyID is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ secretAccessKeySecretRef:
+ description: The SecretAccessKey is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ sessionTokenSecretRef:
+ description: |-
+ The SessionToken used for authentication
+ This must be defined if AccessKeyID and SecretAccessKey are temporary credentials
+ see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ type: object
+ type: object
+ region:
+ description: Region specifies the region to operate in.
+ type: string
+ role:
+ description: |-
+ You can assume a role before making calls to the
+ desired AWS service.
+ type: string
+ scope:
+ description: |-
+ Scope specifies the ECR service scope.
+ Valid options are private and public.
+ type: string
+ required:
+ - region
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+ conversion:
+ strategy: Webhook
+ webhook:
+ conversionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: kubernetes
+ namespace: default
+ path: /convert
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
+ name: fakes.generators.external-secrets.io
+spec:
+ group: generators.external-secrets.io
+ names:
+ categories:
+ - external-secrets
+ - external-secrets-generators
+ kind: Fake
+ listKind: FakeList
+ plural: fakes
+ singular: fake
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: |-
+ Fake generator is used for testing. It lets you define
+ a static set of credentials that is always returned.
+ properties:
+ apiVersion:
+ description: |-
+ APIVersion defines the versioned schema of this representation of an object.
+ Servers should convert recognized schemas to the latest internal value, and
+ may reject unrecognized values.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+ type: string
+ kind:
+ description: |-
+ Kind is a string value representing the REST resource this object represents.
+ Servers may infer this from the endpoint the client submits requests to.
+ Cannot be updated.
+ In CamelCase.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: FakeSpec contains the static data.
+ properties:
+ controller:
+ description: |-
+ Used to select the correct ESO controller (think: ingress.ingressClassName)
+ The ESO controller is instantiated with a specific controller name and filters VDS based on this property
+ type: string
+ data:
+ additionalProperties:
+ type: string
+ description: |-
+ Data defines the static data returned
+ by this generator.
+ type: object
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+ conversion:
+ strategy: Webhook
+ webhook:
+ conversionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: kubernetes
+ namespace: default
+ path: /convert
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
+ name: gcraccesstokens.generators.external-secrets.io
+spec:
+ group: generators.external-secrets.io
+ names:
+ categories:
+ - external-secrets
+ - external-secrets-generators
+ kind: GCRAccessToken
+ listKind: GCRAccessTokenList
+ plural: gcraccesstokens
+ singular: gcraccesstoken
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: |-
+ GCRAccessToken generates an GCP access token
+ that can be used to authenticate with GCR.
+ properties:
+ apiVersion:
+ description: |-
+ APIVersion defines the versioned schema of this representation of an object.
+ Servers should convert recognized schemas to the latest internal value, and
+ may reject unrecognized values.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+ type: string
+ kind:
+ description: |-
+ Kind is a string value representing the REST resource this object represents.
+ Servers may infer this from the endpoint the client submits requests to.
+ Cannot be updated.
+ In CamelCase.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+ type: string
+ metadata:
+ type: object
+ spec:
+ properties:
+ auth:
+ description: Auth defines the means for authenticating with GCP
+ properties:
+ secretRef:
+ properties:
+ secretAccessKeySecretRef:
+ description: The SecretAccessKey is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
- secretAccessKeySecretRef:
- description: The SecretAccessKey is used for authentication
+ type: object
+ workloadIdentity:
+ properties:
+ clusterLocation:
+ type: string
+ clusterName:
+ type: string
+ clusterProjectID:
+ type: string
+ serviceAccountRef:
+ description: A reference to a ServiceAccount resource.
properties:
- key:
+ audiences:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
- type: string
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
name:
- description: The name of the Secret resource being referred to.
+ description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
+ required:
+ - name
type: object
- sessionTokenSecretRef:
+ required:
+ - clusterLocation
+ - clusterName
+ - serviceAccountRef
+ type: object
+ type: object
+ projectID:
+ description: ProjectID defines which project to use to authenticate with
+ type: string
+ required:
+ - auth
+ - projectID
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+ conversion:
+ strategy: Webhook
+ webhook:
+ conversionReviewVersions:
+ - v1
+ clientConfig:
+ service:
+ name: kubernetes
+ namespace: default
+ path: /convert
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
+ name: githubaccesstokens.generators.external-secrets.io
+spec:
+ group: generators.external-secrets.io
+ names:
+ categories:
+ - external-secrets
+ - external-secrets-generators
+ kind: GithubAccessToken
+ listKind: GithubAccessTokenList
+ plural: githubaccesstokens
+ singular: githubaccesstoken
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: GithubAccessToken generates ghs_ accessToken
+ properties:
+ apiVersion:
+ description: |-
+ APIVersion defines the versioned schema of this representation of an object.
+ Servers should convert recognized schemas to the latest internal value, and
+ may reject unrecognized values.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources
+ type: string
+ kind:
+ description: |-
+ Kind is a string value representing the REST resource this object represents.
+ Servers may infer this from the endpoint the client submits requests to.
+ Cannot be updated.
+ In CamelCase.
+ More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds
+ type: string
+ metadata:
+ type: object
+ spec:
+ properties:
+ appID:
+ type: string
+ auth:
+ description: Auth configures how ESO authenticates with a Github instance.
+ properties:
+ privateKey:
+ properties:
+ secretRef:
description: |-
- The SessionToken used for authentication
- This must be defined if AccessKeyID and SecretAccessKey are temporary credentials
- see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html
+ A reference to a specific 'key' within a Secret resource.
+ In some instances, `key` is a required field.
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
+ required:
+ - secretRef
type: object
+ required:
+ - privateKey
type: object
- region:
- description: Region specifies the region to operate in.
+ installID:
type: string
- role:
+ permissions:
+ additionalProperties:
+ type: string
+ description: Map of permissions the token will have. If omitted, defaults to all permissions the GitHub App has.
+ type: object
+ repositories:
description: |-
- You can assume a role before making calls to the
- desired AWS service.
+ List of repositories the token will have access to. If omitted, defaults to all repositories the GitHub App
+ is installed to.
+ items:
+ type: string
+ type: array
+ url:
+ description: URL configures the Github instance URL. Defaults to https://github.com/.
type: string
required:
- - region
+ - appID
+ - auth
+ - installID
type: object
type: object
served: true
@@ -10518,27 +16083,29 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
- name: fakes.generators.external-secrets.io
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
+ name: passwords.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- - fake
- kind: Fake
- listKind: FakeList
- plural: fakes
- shortNames:
- - fake
- singular: fake
+ - external-secrets
+ - external-secrets-generators
+ kind: Password
+ listKind: PasswordList
+ plural: passwords
+ singular: password
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
description: |-
- Fake generator is used for testing. It lets you define
- a static set of credentials that is always returned.
+ Password generates a random password based on the
+ configuration parameters in spec.
+ You can specify the length, characterset and other attributes.
properties:
apiVersion:
description: |-
@@ -10558,20 +16125,41 @@ spec:
metadata:
type: object
spec:
- description: FakeSpec contains the static data.
+ description: PasswordSpec controls the behavior of the password generator.
properties:
- controller:
+ allowRepeat:
+ default: false
+ description: set AllowRepeat to true to allow repeating characters.
+ type: boolean
+ digits:
+ description: |-
+ Digits specifies the number of digits in the generated
+ password. If omitted it defaults to 25% of the length of the password
+ type: integer
+ length:
+ default: 24
+ description: |-
+ Length of the password to be generated.
+ Defaults to 24
+ type: integer
+ noUpper:
+ default: false
+ description: Set NoUpper to disable uppercase characters
+ type: boolean
+ symbolCharacters:
description: |-
- Used to select the correct ESO controller (think: ingress.ingressClassName)
- The ESO controller is instantiated with a specific controller name and filters VDS based on this property
+ SymbolCharacters specifies the special characters that should be used
+ in the generated password.
type: string
- data:
- additionalProperties:
- type: string
+ symbols:
description: |-
- Data defines the static data returned
- by this generator.
- type: object
+ Symbols specifies the number of symbol characters in the generated
+ password. If omitted it defaults to 25% of the length of the password
+ type: integer
+ required:
+ - allowRepeat
+ - length
+ - noUpper
type: object
type: object
served: true
@@ -10593,27 +16181,26 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
- name: gcraccesstokens.generators.external-secrets.io
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
+ name: quayaccesstokens.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- - gcraccesstoken
- kind: GCRAccessToken
- listKind: GCRAccessTokenList
- plural: gcraccesstokens
- shortNames:
- - gcraccesstoken
- singular: gcraccesstoken
+ - external-secrets
+ - external-secrets-generators
+ kind: QuayAccessToken
+ listKind: QuayAccessTokenList
+ plural: quayaccesstokens
+ singular: quayaccesstoken
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
- description: |-
- GCRAccessToken generates an GCP access token
- that can be used to authenticate with GCR.
+ description: QuayAccessToken generates Quay oauth token for pulling/pushing images
properties:
apiVersion:
description: |-
@@ -10634,71 +16221,43 @@ spec:
type: object
spec:
properties:
- auth:
- description: Auth defines the means for authenticating with GCP
+ robotAccount:
+ description: Name of the robot account you are federating with
+ type: string
+ serviceAccountRef:
+ description: Name of the service account you are federating with
properties:
- secretRef:
- properties:
- secretAccessKeySecretRef:
- description: The SecretAccessKey is used for authentication
- properties:
- key:
- description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
- type: string
- name:
- description: The name of the Secret resource being referred to.
- type: string
- namespace:
- description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
- type: string
- type: object
- type: object
- workloadIdentity:
- properties:
- clusterLocation:
- type: string
- clusterName:
- type: string
- clusterProjectID:
- type: string
- serviceAccountRef:
- description: A reference to a ServiceAccount resource.
- properties:
- audiences:
- description: |-
- Audience specifies the `aud` claim for the service account token
- If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
- then this audiences will be appended to the list
- items:
- type: string
- type: array
- name:
- description: The name of the ServiceAccount resource being referred to.
- type: string
- namespace:
- description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
- type: string
- required:
- - name
- type: object
- required:
- - clusterLocation
- - clusterName
- - serviceAccountRef
- type: object
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
type: object
- projectID:
- description: ProjectID defines which project to use to authenticate with
+ url:
+ description: URL configures the Quay instance URL. Defaults to quay.io.
type: string
required:
- - auth
- - projectID
+ - robotAccount
+ - serviceAccountRef
type: object
type: object
served: true
@@ -10720,25 +16279,30 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
- name: githubaccesstokens.generators.external-secrets.io
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
+ name: stssessiontokens.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- - githubaccesstoken
- kind: GithubAccessToken
- listKind: GithubAccessTokenList
- plural: githubaccesstokens
- shortNames:
- - githubaccesstoken
- singular: githubaccesstoken
+ - external-secrets
+ - external-secrets-generators
+ kind: STSSessionToken
+ listKind: STSSessionTokenList
+ plural: stssessiontokens
+ singular: stssessiontoken
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
- description: GithubAccessToken generates ghs_ accessToken
+ description: |-
+ STSSessionToken uses the GetSessionToken API to retrieve an authorization token.
+ The authorization token is valid for 12 hours.
+ The authorizationToken returned is a base64 encoded string that can be decoded.
+ For more information, see GetSessionToken (https://docs.aws.amazon.com/STS/latest/APIReference/API_GetSessionToken.html).
properties:
apiVersion:
description: |-
@@ -10759,47 +16323,160 @@ spec:
type: object
spec:
properties:
- appID:
- type: string
auth:
- description: Auth configures how ESO authenticates with a Github instance.
+ description: Auth defines how to authenticate with AWS
properties:
- privatKey:
+ jwt:
+ description: Authenticate against AWS using service account tokens.
properties:
- secretRef:
+ serviceAccountRef:
+ description: A reference to a ServiceAccount resource.
+ properties:
+ audiences:
+ description: |-
+ Audience specifies the `aud` claim for the service account token
+ If the service account uses a well-known annotation for e.g. IRSA or GCP Workload Identity
+ then this audiences will be appended to the list
+ items:
+ type: string
+ type: array
+ name:
+ description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ required:
+ - name
+ type: object
+ type: object
+ secretRef:
+ description: |-
+ AWSAuthSecretRef holds secret references for AWS credentials
+ both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.
+ properties:
+ accessKeyIDSecretRef:
+ description: The AccessKeyID is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ secretAccessKeySecretRef:
+ description: The SecretAccessKey is used for authentication
+ properties:
+ key:
+ description: |-
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
+ type: string
+ name:
+ description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
+ type: string
+ namespace:
+ description: |-
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
+ type: string
+ type: object
+ sessionTokenSecretRef:
description: |-
- A reference to a specific 'key' within a Secret resource,
- In some instances, `key` is a required field.
+ The SessionToken used for authentication
+ This must be defined if AccessKeyID and SecretAccessKey are temporary credentials
+ see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
- required:
- - secretRef
type: object
- required:
- - privatKey
type: object
- installID:
+ region:
+ description: Region specifies the region to operate in.
type: string
- url:
- description: URL configures the Github instance URL. Defaults to https://github.com/.
+ requestParameters:
+ description: RequestParameters contains parameters that can be passed to the STS service.
+ properties:
+ serialNumber:
+ description: |-
+ SerialNumber is the identification number of the MFA device that is associated with the IAM user who is making
+ the GetSessionToken call.
+ Possible values: hardware device (such as GAHT12345678) or an Amazon Resource Name (ARN) for a virtual device
+ (such as arn:aws:iam::123456789012:mfa/user)
+ type: string
+ sessionDuration:
+ description: |-
+ SessionDuration The duration, in seconds, that the credentials should remain valid. Acceptable durations for
+ IAM user sessions range from 900 seconds (15 minutes) to 129,600 seconds (36 hours), with 43,200 seconds
+ (12 hours) as the default.
+ format: int64
+ type: integer
+ tokenCode:
+ description: TokenCode is the value provided by the MFA device, if MFA is required.
+ type: string
+ type: object
+ role:
+ description: |-
+ You can assume a role before making calls to the
+ desired AWS service.
type: string
required:
- - appID
- - auth
- - installID
+ - region
type: object
type: object
served: true
@@ -10821,28 +16498,26 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
- name: passwords.generators.external-secrets.io
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
+ name: uuids.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- - password
- kind: Password
- listKind: PasswordList
- plural: passwords
- shortNames:
- - password
- singular: password
+ - external-secrets
+ - external-secrets-generators
+ kind: UUID
+ listKind: UUIDList
+ plural: uuids
+ singular: uuid
scope: Namespaced
versions:
- name: v1alpha1
schema:
openAPIV3Schema:
- description: |-
- Password generates a random password based on the
- configuration parameters in spec.
- You can specify the length, characterset and other attributes.
+ description: UUID generates a version 1 UUID (e56657e3-764f-11ef-a397-65231a88c216).
properties:
apiVersion:
description: |-
@@ -10862,41 +16537,7 @@ spec:
metadata:
type: object
spec:
- description: PasswordSpec controls the behavior of the password generator.
- properties:
- allowRepeat:
- default: false
- description: set AllowRepeat to true to allow repeating characters.
- type: boolean
- digits:
- description: |-
- Digits specifies the number of digits in the generated
- password. If omitted it defaults to 25% of the length of the password
- type: integer
- length:
- default: 24
- description: |-
- Length of the password to be generated.
- Defaults to 24
- type: integer
- noUpper:
- default: false
- description: Set NoUpper to disable uppercase characters
- type: boolean
- symbolCharacters:
- description: |-
- SymbolCharacters specifies the special characters that should be used
- in the generated password.
- type: string
- symbols:
- description: |-
- Symbols specifies the number of symbol characters in the generated
- password. If omitted it defaults to 25% of the length of the password
- type: integer
- required:
- - allowRepeat
- - length
- - noUpper
+ description: UUIDSpec controls the behavior of the uuid generator.
type: object
type: object
served: true
@@ -10918,18 +16559,19 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: vaultdynamicsecrets.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- - vaultdynamicsecret
+ - external-secrets
+ - external-secrets-generators
kind: VaultDynamicSecret
listKind: VaultDynamicSecretList
plural: vaultdynamicsecrets
- shortNames:
- - vaultdynamicsecret
singular: vaultdynamicsecret
scope: Namespaced
versions:
@@ -10956,6 +16598,10 @@ spec:
type: object
spec:
properties:
+ allowEmptyResponse:
+ default: false
+ description: Do not fail if no secrets are found. Useful for requests where no data is expected.
+ type: boolean
controller:
description: |-
Used to select the correct ESO controller (think: ingress.ingressClassName)
@@ -11001,16 +16647,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -11022,16 +16677,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -11050,16 +16714,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretRef:
@@ -11069,16 +16742,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -11106,11 +16788,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -11133,16 +16821,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
secretAccessKeySecretRef:
@@ -11150,16 +16847,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
sessionTokenSecretRef:
@@ -11170,16 +16876,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -11233,11 +16948,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -11263,16 +16984,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
required:
@@ -11303,16 +17033,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
serviceAccountRef:
@@ -11332,11 +17071,17 @@ spec:
type: array
name:
description: The name of the ServiceAccount resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ Namespace of the resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
required:
- name
@@ -11364,16 +17109,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -11398,16 +17152,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
userPass:
@@ -11427,16 +17190,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
username:
@@ -11462,14 +17234,23 @@ spec:
properties:
key:
description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
The namespace the Provider type is in.
Can only be defined when used in a ClusterSecretStore.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -11488,6 +17269,11 @@ spec:
the option is enabled serverside.
https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header
type: boolean
+ headers:
+ additionalProperties:
+ type: string
+ description: Headers to be added in Vault request
+ type: object
namespace:
description: |-
Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows
@@ -11527,16 +17313,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
keySecretRef:
@@ -11547,16 +17342,25 @@ spec:
properties:
key:
description: |-
- The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
- defaulted, in others it may be required.
+ A key in the referenced Secret.
+ Some instances of this field may be defaulted, in others it may be required.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: |-
- Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
- to the namespace of the referent.
+ The namespace of the Secret resource being referred to.
+ Ignored if referent is not cluster-scoped, otherwise defaults to the namespace of the referent.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type: object
type: object
@@ -11585,6 +17389,15 @@ spec:
- Data
- Auth
type: string
+ retrySettings:
+ description: Used to configure http retries if failed
+ properties:
+ maxRetries:
+ format: int32
+ type: integer
+ retryInterval:
+ type: string
+ type: object
required:
- path
- provider
@@ -11609,18 +17422,19 @@ apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
annotations:
- controller-gen.kubebuilder.io/version: v0.14.0
+ controller-gen.kubebuilder.io/version: v0.17.1
+ labels:
+ external-secrets.io/component: controller
name: webhooks.generators.external-secrets.io
spec:
group: generators.external-secrets.io
names:
categories:
- - webhook
+ - external-secrets
+ - external-secrets-generators
kind: Webhook
listKind: WebhookList
plural: webhooks
- shortNames:
- - webhookl
singular: webhook
scope: Namespaced
versions:
@@ -11668,13 +17482,22 @@ spec:
description: The provider for the CA bundle to use to validate webhook server certificate.
properties:
key:
- description: The key the value inside of the provider type to use, only used with "Secret" type
+ description: The key where the CA certificate can be found in the Secret or ConfigMap.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the object located at the provider type.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
namespace:
description: The namespace the Provider type is in.
+ maxLength: 63
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?$
type: string
type:
description: The type of provider to use such as "Secret", or "ConfigMap".
@@ -11715,9 +17538,15 @@ spec:
properties:
key:
description: The key where the token is found.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[-._a-zA-Z0-9]+$
type: string
name:
description: The name of the Secret resource being referred to.
+ maxLength: 253
+ minLength: 1
+ pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
type: string
type: object
required:
diff --git a/design/001-design-crd-v1beta1.md b/design/001-design-crd-v1beta1.md
index 3077a96eb47..38996549fc5 100644
--- a/design/001-design-crd-v1beta1.md
+++ b/design/001-design-crd-v1beta1.md
@@ -4,7 +4,7 @@ title: External Secrets CRD promotion
version: v1beta1
authors: all of us
creation-date: 2022-feb-08
-status: approved
+status: implemented
---
```
diff --git a/design/002-pushsecret.md b/design/002-pushsecret.md
index acf85bf0bc9..508d416f513 100644
--- a/design/002-pushsecret.md
+++ b/design/002-pushsecret.md
@@ -4,7 +4,7 @@ title: PushSecret
version: v1alpha1
authors:
creation-date: 2022-01-25
-status: draft
+status: implemented
---
```
diff --git a/design/003-cluster-external-secret-spec.md b/design/003-cluster-external-secret-spec.md
index 3e22f83c9eb..867f4e7838e 100644
--- a/design/003-cluster-external-secret-spec.md
+++ b/design/003-cluster-external-secret-spec.md
@@ -4,7 +4,7 @@ title: Adding Cluster External Secrets
version: v1alpha1
authors: Daniel "ADustyOldMuffin" Hix
creation-date: 2020-09-01
-status: draft
+status: implemented
---
```
diff --git a/design/004-datafrom-key-rewrite.md b/design/004-datafrom-key-rewrite.md
index c130f79c531..c0bbf2371ed 100644
--- a/design/004-datafrom-key-rewrite.md
+++ b/design/004-datafrom-key-rewrite.md
@@ -4,7 +4,7 @@ title: dataFrom key rewrite
version: v1alpha1
authors:
creation-date: 2022-05-25
-status: draft
+status: implemented
---
```
@@ -51,7 +51,7 @@ metadata:
name: sample
namespace: default
spec:
- refreshInterval: 1m
+ refreshInterval: 1h
target:
name: foobar
secretStoreRef:
diff --git a/design/005-secret-generator-group.md b/design/005-secret-generator-group.md
index c788eba45a4..f9bcf2b5c8b 100644
--- a/design/005-secret-generator-group.md
+++ b/design/005-secret-generator-group.md
@@ -4,7 +4,7 @@ title: Secret Generators
version: v1alpha1
authors: Christian Hünig, Jan Steffen, Moritz Johner
creation-date: 2022-07-08
-status: draft
+status: implemented
---
```
diff --git a/design/007-provider-versioning-strategy.md b/design/007-provider-versioning-strategy.md
index 8aa3525fae1..e516f324126 100644
--- a/design/007-provider-versioning-strategy.md
+++ b/design/007-provider-versioning-strategy.md
@@ -5,7 +5,7 @@ title: Provider Separation on specific CRDs
version: v1alpha1
authors: Gustavo Carvalho
creation-date: 2023-08-25
-status: draft
+status: approved
---
```
@@ -126,7 +126,7 @@ func (g *gitlabBase) getAuth(ctx context.Context) ([]byte, error) {
credentials := credentialsSecret.Data[g.store.Auth.SecretRef.AccessToken.Key]
if len(credentials) == 0 {
- return nil, fmt.Errorf(errMissingSAK)
+ return nil, errors.New(errMissingSAK)
}
return credentials, nil
}
diff --git a/design/008-pushsecret-update-policy.md b/design/008-pushsecret-update-policy.md
index aa86d9d16de..ea5ca7f6ed2 100644
--- a/design/008-pushsecret-update-policy.md
+++ b/design/008-pushsecret-update-policy.md
@@ -4,7 +4,7 @@ title: PushSecret Update Policy
version: v1alpha1
authors: Moritz Johner
creation-date: 2023-08-25
-status: draft
+status: partially implemented
---
```
diff --git a/design/009-pushsecret-generator.md b/design/009-pushsecret-generator.md
index 196412c07c7..f25ab3ddaf8 100644
--- a/design/009-pushsecret-generator.md
+++ b/design/009-pushsecret-generator.md
@@ -4,7 +4,7 @@ title: PushSecret generator integration
version: v1alpha1
authors: Moritz Johner
creation-date: 2023-08-25
-status: draft
+status: approved
---
```
diff --git a/design/010-pushsecret-metadata.md b/design/010-pushsecret-metadata.md
new file mode 100644
index 00000000000..e990a32189e
--- /dev/null
+++ b/design/010-pushsecret-metadata.md
@@ -0,0 +1,199 @@
+```yaml
+---
+title: PushSecret metadata
+version: v1alpha1
+authors: Moritz Johner
+creation-date: 2023-08-25
+status: draft
+---
+```
+
+# PushSecret Metadata
+
+[#2600](https://github.com/external-secrets/external-secrets/pull/2600) introduced a new feature that allows users to pass arbitrary `metadata` to the provider.
+
+The data is arbitrary json/yaml and can be anything.
+
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+metadata:
+ name: pushsecret-example
+spec:
+ # ...
+ data:
+ - match:
+ secretKey: key1
+ remoteRef:
+ remoteKey: test1
+ metadata:
+ annotations:
+ key1: value1
+ labels:
+ key1: value1
+
+```
+
+Here is an overview of current implementations of PushSecret metadata:
+
+```yaml
+# AWS Parameter Store
+apiVersion: kubernetes.external-secrets.io/v1alpha1
+kind: PushSecretMetadata
+spec:
+ secretType: StringList
+```
+
+```yaml
+# GCP Secrets Manager
+labels: {}
+annotations: {}
+```
+
+```yaml
+# AWS Secrets Manager
+secretPushFormat: "..."
+```
+
+## Problem Description
+
+We will never be able to make disruptive changes, we can only append to the existing structure.
+
+**Why is that a problem?**
+
+It limits our ability to fix mistakes that have been merged and released. Having an `apiVersion` field would allow us decode the metadata differently and apply the appropriate logic in a code branch.
+
+This would simplify fixing simple mis-nomers or doing large-scale refactorings in the future.
+
+ESO is a community based project and relies on contributions from different backgrounds and experience levels. As a result, the approach and perspective to a solution highly depends
+on the contributor and the reviewer. We will eventually have to align the structure or naming of metadata across providers once we see patterns emerge.
+
+## Proposed Solution
+
+I would propose to wrap the unstructured metadata in a Kubernetes *alike* resource containing an `apiVersion`, `kind` and `spec`.
+
+#### 1. Kubernetes Provider Example
+
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+metadata:
+ name: pushsecret-example
+spec:
+ # ...
+ data:
+ - match:
+ secretKey: key1
+ remoteRef:
+ remoteKey: test1
+ metadata:
+ apiVersion: kubernetes.external-secrets.io/v1alpha1
+ kind: PushSecretMetadata
+ spec:
+ sourceMergePolicy: Merge
+ targetMergePolicy: Merge
+ labels:
+ color: red
+ annotations:
+ yes: please
+```
+
+#### 2. AWS Secrets Manager Example
+
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+metadata:
+ name: pushsecret-example
+spec:
+ # ...
+ data:
+ - match:
+ secretKey: key1
+ remoteRef:
+ remoteKey: test1
+ metadata:
+ apiVersion: secretsmanager.aws.external-secrets.io/v1alpha1
+ kind: PushSecretMetadata
+ spec:
+ secretFormat: binary # string
+```
+
+#### 3. AWS Parameter Store Example
+
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+metadata:
+ name: pushsecret-example
+spec:
+ # ...
+ data:
+ - match:
+ secretKey: key1
+ remoteRef:
+ remoteKey: test1
+ metadata:
+ apiVersion: parameterstore.aws.external-secrets.io/v1alpha1
+ kind: PushSecretMetadata
+ spec:
+ tier: "Advanced"
+ type: "StringList"
+ keyID: "arn:..."
+ policies:
+ - type: "ExpirationNotification"
+ version: "1.0"
+ attributes:
+ before: "15"
+ unit: "Days"
+```
+
+**PROS**
+- familiar structure for Kubernetes users, other projectes use that pattern already
+- we may be able to re-use existing tooling, e.g. for validating the structure and generating documentation
+
+**CONS**
+- may confuse users if they encounter a nested custom resource
+- a little bit of boilerplate to chew through
+
+
+### What would we do with the existing implementations?
+
+We should keep them as a backward compatible measure for the `v1alpha1` stage and remove them with the `v1beta1` release. We can remove them from the documentation right away and only document the "new" scheme. The old scheme is still accessible through the version switch in the docs. This allows us to slowly direct users to the new scheme.
+
+With a PushSecret `v1beta1` we can consider removing those APIs.
+
+
+## Alternatives
+
+The minimum would be to have a `version` field which provides a hint for decoding the structure in `spec`. That is technically enough to meet the requirements outlined above.
+
+
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+metadata:
+ name: pushsecret-example
+spec:
+ # ...
+ data:
+ - match:
+ secretKey: key1
+ remoteRef:
+ remoteKey: test1
+ metadata:
+ version: kubernetes/v1alpha1
+ spec:
+ sourceMergePolicy: Merge
+ targetMergePolicy: Merge
+ labels:
+ color: red
+ annotations:
+ yes: please
+```
+
+**PROS**
+- more concise, less boilerplate
+
+**CONS**
+- no ability to directly re-use existing tooling
diff --git a/design/011-deprecate-olm.md b/design/011-deprecate-olm.md
new file mode 100644
index 00000000000..dc470c22363
--- /dev/null
+++ b/design/011-deprecate-olm.md
@@ -0,0 +1,43 @@
+```yaml
+---
+title: Deprecaation of OLM Builds
+authors: @gusfcarvalho
+creation-date: 2024-12-04
+status: approved
+---
+```
+This Proposal was approved on community meeting of 4th december 2024 (meeting notes: https://hackmd.io/GSGEpTVdRZCP6LDxV3FHJA?both)
+
+# Deprecaation of OLM Builds
+
+## Introduction
+
+As part of our Release process, we currently build & maintain several docker images, helm releases, bundle manifests for users, and OLM builds via a community effort based on olm helm operator.
+
+However, OLM helm operator itself would require a better support and constant maintenance, and its process when building OLM builds is already not automated anymore.
+## Summary
+Stpo building OLM Releases
+
+## Motivation
+Make maintenance lives easier for a project that is struggling to get maintainers together :)
+
+### Goals
+Remove OLM builds as part of our build assets
+
+## Proposal
+Archive repository & communicate on next release within the release notes.
+
+### API
+None
+
+### Behavior
+None
+
+### Drawbacks
+Users might complain - but then they can fork the archived repository to build their own OLM builds locally.
+
+## Alternatives
+Find community members to handle the maintanence aspect of it. Have a new dedicated OLM repository in/out of the org. Make this be maintained by other parties than external-secrets maintainers.
+
+Do not use the current olm helm operator anymore as anyways this is not really supported.
+
diff --git a/design/012-sync-to-custom-resource.md b/design/012-sync-to-custom-resource.md
new file mode 100644
index 00000000000..51a43c0cdb9
--- /dev/null
+++ b/design/012-sync-to-custom-resource.md
@@ -0,0 +1,114 @@
+```yaml
+---
+title: Sync to Custom Resources
+version: v1alpha1
+authors: Gustavo Carvalho
+creation-date: 2024-05-03
+status: approved
+---
+```
+
+# Sync to Custom Resources Design
+
+## Table of Contents
+
+
+// autogen please
+
+
+## Summary
+
+This design document describes how `ExternalSecrets` can leverage templates to generate non-kubernetes `Secret` resource as the target. This allows to push Sensitive information to specific CRs, `ConfiMaps`, etc.
+
+## Motivation
+
+Currently, several "semi-sensitive" information needs to be provisioned directly into CRs and ConfigMaps (such as OIDC Client IDs). While these information are not strictly speaking a secret, several regulated environments must treat them as such, causing several operational overhead to deal with this information - specially on a GitOps setup.
+
+## Proposal
+
+To simplify the workflow and enhance user experience, the proposal is to integrate a functionality to template the whole manifest. this would be additional logic to the existing `target: Manifest` directly into the `templateFrom.[].target` resource. This will allow users to specify a template to render any type of manifest, instead of the original Secret object.
+
+Problems with this proposal is that the whole reconciliation logic reads from a secret - this would need to be updated if templates using Manifests are set. In that case, we should query for that specific resource as specified on the target.
+
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+spec:
+ target:
+ name: target-custom-resource
+ manifest: # Information as to find it afterwards during reconcile cycle
+ APIVersion: my.custom.api/v1alpha1
+ Kind: CustomResource
+ template:
+ templateFrom:
+ - target: Spec #Additional target at root level (instead of data, metadata and annotations level).
+ # Other option would be to allow a gjson path such as target:'.spec' where . is the indicator of this type of expression for backwards compatibility.
+ literal: |
+ customSpecField1: {{ .fromSecretStore }}
+ field2:
+ couldBeNested:
+ - {{ .fromSecretStore }}
+ ## Name: is obtained from spec.target.name
+---
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+spec:
+ target:
+ name: target-configmap
+ manifest: # Information as to find it afterwards during reconcile cycle
+ APIVersion: v1
+ Kind: ConfigMap
+ template:
+ templateFrom:
+ - target: Data
+ literal: |
+ {{ .field1 }}: {{ .value1 }}
+---
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+spec:
+ target:
+ name: target-configmap
+ manifest: # Information as to find it afterwards during reconcile cycle
+ APIVersion: v1
+ Kind: ConfigMap # should render with no need of templates - as `.data` is the default Secret target.
+```
+
+Only one `templateFrom` entry can be set if its type is `target: Manifest`.
+
+## Consequences
+
+* **First Class support for GitOps tools**: GitOps tools often need configmap information considered as sensitive. This allows better integration with such tools
+
+* **Increased Complexity**: Instead of getting a single secret to start reconciling process, we would need to first verify if `target.manifest` is set - and use the manifest information to get the appropriate resource (defaulting to a kubernetes secret). Templateing Logic would only be increased to some extend, as we would need to change the `Secret` type to a runtime.Object
+
+* **Better Extensibility**: This feature allows ESO to not only be used by operators but also better integrated to systems.
+
+* **Backwards Compatible**: This feature would not change how `target` and `template` are currently used by existing installations.
+
+* **API and Documentation Update**: The API changes need to be well-documented to ensure users understand how to utilize the new feature effectively.
+
+
+## Acceptance Criteria
+
+* behavior:
+ * Reconciliation logic should not change when `target!= Secret`
+ * `creationPolicy`, `updatePolicy`, and `deletionPolicy` must be compatible when `target != Secret`
+ * One of the two must be implemented:
+ * a feature flag `--unsafe-allow-non-secret-targets` must be set to allow this feature. If not set, `template.manifest` should cause error to the reconciliation.
+ * Feature is always eniabled - but Warnings must be emited whenever `target.manifest` is used pointing to the use of sensitive information on open manifests.
+ * Warnings should be disabled with feature flags
+* deployment:
+ * Extra RBAC options must be available on helm values (to allow the usage of this feature)
+ * Helm values must allow the installation of this new feature (setting up the appropriate feature flags, etc)
+* tests:
+ * controller unit tests for `target.manifest` behavior and `target.template.target` behavior
+ * controller regression tests for `target.manifest` and `target.template.target` focused on different `creationPolicy` and `deletionPolicy`
+ * e2e test for `target.manifest` targeting a ConfigMap (first class support for ArgoCD and Flux)
+ * e2e test for `target.manifest` targeting a custom resource
+
+* the API changes need to be documented
+ * API/CRD spec inline documentation
+ * ExternalSecrets API documentation
+ * Guides section for `ExternalSecret` 'Creating Non-Secret Resources'.
+ * Warnings on the feature as non-Secret manifests are not meant to contain sensitive information.
diff --git a/docs/api/clusterexternalsecret.md b/docs/api/clusterexternalsecret.md
index 59ec8528f16..ea2e688901b 100644
--- a/docs/api/clusterexternalsecret.md
+++ b/docs/api/clusterexternalsecret.md
@@ -2,7 +2,7 @@
The `ClusterExternalSecret` is a cluster scoped resource that can be used to manage `ExternalSecret` resources in specific namespaces.
-With `namespaceSelector` you can select namespaces in which the ExternalSecret should be created.
+With `namespaceSelectors` you can select namespaces in which the ExternalSecret should be created.
If there is a conflict with an existing resource the controller will error out.
## Example
@@ -12,3 +12,10 @@ Below is an example of the `ClusterExternalSecret` in use.
```yaml
{% include 'full-cluster-external-secret.yaml' %}
```
+
+## Deprecations
+
+### namespaceSelector
+
+The field `namespaceSelector` has been deprecated in favor of `namespaceSelectors` and will be removed in a future
+version.
diff --git a/docs/api/controller-options.md b/docs/api/controller-options.md
index a846aff99e8..7fbcd6a2338 100644
--- a/docs/api/controller-options.md
+++ b/docs/api/controller-options.md
@@ -11,26 +11,28 @@ The external-secrets binary includes three components: `core controller`, `certc
The core controller is invoked without a subcommand and can be configured with the following flags:
-| Name | Type | Default | Description |
-| --------------------------------------------- | -------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
-| `--client-burst` | int | uses rest client default (10) | Maximum Burst allowed to be passed to rest.Client |
-| `--client-qps` | float32 | uses rest client default (5) | QPS configuration to be passed to rest.Client |
-| `--concurrent` | int | 1 | The number of concurrent reconciles. |
-| `--controller-class` | string | default | The controller is instantiated with a specific controller name and filters ES based on this property |
-| `--enable-cluster-external-secret-reconciler` | boolean | true | Enables the cluster external secret reconciler. |
-| `--enable-cluster-store-reconciler` | boolean | true | Enables the cluster store reconciler. |
-| `--enable-push-secret-reconciler` | boolean | true | Enables the push secret reconciler. |
-| `--enable-secrets-caching` | boolean | false | Enables the secrets caching for external-secrets pod. |
-| `--enable-configmaps-caching` | boolean | false | Enables the ConfigMap caching for external-secrets pod. |
-| `--enable-flood-gate` | boolean | true | Enable flood gate. External secret will be reconciled only if the ClusterStore or Store have an healthy or unknown state. |
-| `--enable-extended-metric-labels` | boolean | true | Enable recommended kubernetes annotations as labels in metrics. |
-| `--enable-leader-election` | boolean | false | Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager. |
-| `--experimental-enable-aws-session-cache` | boolean | false | Enable experimental AWS session cache. External secret will reuse the AWS session without creating a new one on each request. |
-| `--help` | | | help for external-secrets |
-| `--loglevel` | string | info | loglevel to use, one of: debug, info, warn, error, dpanic, panic, fatal |
-| `--metrics-addr` | string | :8080 | The address the metric endpoint binds to. |
-| `--namespace` | string | - | watch external secrets scoped in the provided namespace only. ClusterSecretStore can be used but only work if it doesn't reference resources from other namespaces |
-| `--store-requeue-interval` | duration | 5m0s | Default Time duration between reconciling (Cluster)SecretStores |
+| Name | Type | Default | Description |
+|-----------------------------------------------|----------|---------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `--client-burst` | int | 100 | Maximum Burst allowed to be passed to rest.Client |
+| `--client-qps` | float32 | 50 | QPS configuration to be passed to rest.Client |
+| `--concurrent` | int | 1 | The number of concurrent reconciles. |
+| `--controller-class` | string | default | The controller is instantiated with a specific controller name and filters ES based on this property |
+| `--enable-cluster-external-secret-reconciler` | boolean | true | Enables the cluster external secret reconciler. |
+| `--enable-cluster-store-reconciler` | boolean | true | Enables the cluster store reconciler. |
+| `--enable-push-secret-reconciler` | boolean | true | Enables the push secret reconciler. |
+| `--enable-secrets-caching` | boolean | false | Enable secrets caching for ALL secrets in the cluster (WARNING: can increase memory usage). |
+| `--enable-configmaps-caching` | boolean | false | Enable configmaps caching for ALL configmaps in the cluster (WARNING: can increase memory usage). |
+| `--enable-managed-secrets-caching` | boolean | true | Enable secrets caching for secrets managed by an ExternalSecret. |
+| `--enable-flood-gate` | boolean | true | Enable flood gate. External secret will be reconciled only if the ClusterStore or Store have an healthy or unknown state. |
+| `--enable-extended-metric-labels` | boolean | true | Enable recommended kubernetes annotations as labels in metrics. |
+| `--enable-leader-election` | boolean | false | Enable leader election for controller manager. Enabling this will ensure there is only one active controller manager. |
+| `--experimental-enable-aws-session-cache` | boolean | false | Enable experimental AWS session cache. External secret will reuse the AWS session without creating a new one on each request. |
+| `--help` | | | help for external-secrets |
+| `--loglevel` | string | info | loglevel to use, one of: debug, info, warn, error, dpanic, panic, fatal |
+| `--zap-time-encoding` | string | epoch | loglevel to use, one of: epoch, millis, nano, iso8601, rfc3339, rfc3339nano |
+| `--metrics-addr` | string | :8080 | The address the metric endpoint binds to. |
+| `--namespace` | string | - | watch external secrets scoped in the provided namespace only. ClusterSecretStore can be used but only work if it doesn't reference resources from other namespaces |
+| `--store-requeue-interval` | duration | 5m0s | Default Time duration between reconciling (Cluster)SecretStores |
## Cert Controller Flags
@@ -41,6 +43,7 @@ The core controller is invoked without a subcommand and can be configured with t
| `--healthz-addr` | string | :8081 | The address the health endpoint binds to. |
| `--help` | | | help for certcontroller |
| `--loglevel` | string | info | loglevel to use, one of: debug, info, warn, error, dpanic, panic, fatal |
+| `--zap-time-encoding` | string | epoch | time encoding to use, one of: epoch, millis, nano, iso8601, rfc3339, rfc3339nano |
| `--metrics-addr` | string | :8080 | The address the metric endpoint binds to. |
| `--secret-name` | string | external-secrets-webhook | Secret to store certs for webhook |
| `--secret-namespace` | string | default | namespace of the secret to store certs |
@@ -57,6 +60,7 @@ The core controller is invoked without a subcommand and can be configured with t
| `--healthz-addr` | string | :8081 | The address the health endpoint binds to. |
| `--help` | | | help for webhook |
| `--loglevel` | string | info | loglevel to use, one of: debug, info, warn, error, dpanic, panic, fatal |
+| `--zap-time-encoding` | string | epoch | time encoding to use, one of: epoch, millis, nano, iso8601, rfc3339, rfc3339nano |
| `--lookahead-interval` | duration | 2160h0m0s (90d) | certificate check interval |
| `--metrics-addr` | string | :8080 | The address the metric endpoint binds to. |
| `--port` | number | 10250 | Port number that the webhook server will serve. |
diff --git a/docs/api/generator/acr.md b/docs/api/generator/acr.md
index 49de64ed9d5..8a5aa7d7fa5 100644
--- a/docs/api/generator/acr.md
+++ b/docs/api/generator/acr.md
@@ -9,7 +9,6 @@ The token is generated for a particular ACR registry defined in `spec.registry`.
| username | username for the `docker login` command |
| password | password for the `docker login` command |
-
## Authentication
You must choose one out of three authentication mechanisms:
@@ -18,8 +17,10 @@ You must choose one out of three authentication mechanisms:
- managed identity
- workload identity
-The generated token will inherit the permissions from the assigned policy. I.e. when you assign a read-only policy all generated tokens will be read-only.
-You **must** [assign a Azure RBAC role](https://learn.microsoft.com/en-us/azure/role-based-access-control/role-assignments-steps), such as `AcrPush` or `AcrPull` to the service principal in order to be able to authenticate with the Azure container registry API.
+The generated token will inherit the permissions from the assigned policy. I.e. when you assign a read-only policy all generated tokens will be read-only.
+You **must** [assign a Azure RBAC role](https://learn.microsoft.com/en-us/azure/role-based-access-control/role-assignments-steps), such as `AcrPush` or `AcrPull` to the service principal or managed identity in order to be able to authenticate with the Azure container registry API.
+
+You can also use a kubelet managed identity with the default `AcrPull` role to authenticate to the integrated Azure Container Registry.
You can scope tokens to a particular repository using `spec.scope`.
@@ -33,7 +34,7 @@ If `spec.scope` if it is defined it obtains an ACR access token. If `spec.scope
- refresh tokens can are scoped to whatever policy is attached to the identity that creates the acr refresh token
The Scope grammar is defined in the [Docker Registry spec](https://docs.docker.com/registry/spec/auth/scope/).
-Note: You **can not** use a wildcards in the scope parameter, you can match exactly one repository and defined multiple actions like `pull` or `push`.
+Note: You **can not** use wildcards in the scope parameter -- you can match exactly one repository and can define multiple actions like `pull` or `push`.
Example scopes:
@@ -49,6 +50,13 @@ repository:my-repository:pull
```
Example `ExternalSecret` that references the ACR generator:
+
```yaml
{% include 'generator-acr-example.yaml' %}
-```
\ No newline at end of file
+```
+
+Example using AKS kubelet managed identity to create [Argo CD helm chart repository](https://argo-cd.readthedocs.io/en/latest/operator-manual/declarative-setup/#helm-chart-repositories) secret:
+
+```yaml
+{% include 'generator-acr-argocd-helm-repo.yaml' %}
+```
diff --git a/docs/api/generator/cluster.md b/docs/api/generator/cluster.md
new file mode 100644
index 00000000000..ccb9ab276e2
--- /dev/null
+++ b/docs/api/generator/cluster.md
@@ -0,0 +1,20 @@
+`ClusterGenerator` is a generator wrapper that is available to configure a generator
+cluster-wide. The purpose of this generator is that the user doesn't have to redefine
+the generator in every namespace. They could define it once in the cluster and then reference that
+in the consuming `ExternalSecret`.
+
+## Limitations
+
+With this, the generator will still create objects in the namespace in which the referencing ES lives.
+That has not changed as of now. It will change in future modifications.
+
+## Example Manifest
+
+```yaml
+{% include 'generator-cluster.yaml' %}
+```
+
+Example `ExternalSecret` that references the Cluster generator:
+```yaml
+{% include 'generator-cluster-example.yaml' %}
+```
diff --git a/docs/api/generator/github.md b/docs/api/generator/github.md
new file mode 100644
index 00000000000..839cda17847
--- /dev/null
+++ b/docs/api/generator/github.md
@@ -0,0 +1,54 @@
+## GitHub App Authentication Documentation
+
+### 1. Register a GitHub App
+To create a GitHub app, follow the instructions provided by GitHub:
+
+- **Visit**: [Registering a GitHub App](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/registering-a-github-app#registering-a-github-app)
+- **Procedure**:
+ - Fill in the necessary details for your app.
+ - Note the `App ID` provided after registration.
+ - At the bottom of the registration page, click on `Generate a private key`. Download and securely store this key.
+
+### 2. Store the Private Key
+After generating your private key, you need to store it securely. If you are using Kubernetes, you can store it as a secret:
+
+```bash
+kubectl create secret generic github-app-pem --from-file=key=path/to/your/private-key.pem
+```
+
+### 3. Set Permissions for the GitHub App
+Configure the necessary permissions for your GitHub app depending on what actions it needs to perform:
+
+- **Visit**: [Choosing Permissions for a GitHub App](https://docs.github.com/en/apps/creating-github-apps/registering-a-github-app/choosing-permissions-for-a-github-app#choosing-permissions-for-rest-api-access)
+- **Example**:
+ - For managing OCI images, set read and write permissions for packages.
+
+### 4. Install Your GitHub App
+Install the GitHub app on your repository or organization to start using it:
+
+- **Visit**: [Installing Your Own GitHub App](https://docs.github.com/en/apps/using-github-apps/installing-your-own-github-app)
+
+### 5. Obtain an Installation ID
+After installation, you need to get the installation ID to authenticate API requests:
+
+- **Visit**: [Generating an Installation Access Token for a GitHub App](https://docs.github.com/en/apps/creating-github-apps/authenticating-with-a-github-app/generating-an-installation-access-token-for-a-github-app#generating-an-installation-access-token)
+- **Procedure**:
+ - Find the installation ID from the URL or API response.
+
+### Example Kubernetes Manifest for GitHub Access Token Generator
+
+```yaml
+{% include 'generator-github.yaml' %}
+```
+
+```yaml
+{% include 'generator-github-example.yaml' %}
+```
+
+```yaml
+{% include 'generator-github-example-basicauth.yaml' %}
+```
+
+### Notes
+- Ensure that all sensitive data such as private keys and IDs are securely handled and stored.
+- Adjust the permissions and configurations according to your specific requirements and security policies.
diff --git a/docs/api/generator/quay.md b/docs/api/generator/quay.md
new file mode 100644
index 00000000000..cceabf3fe8f
--- /dev/null
+++ b/docs/api/generator/quay.md
@@ -0,0 +1,39 @@
+`QuayAccessToken` creates a short-lived Quay Access token that can be used to authenticate against quay.io or a self-hosted instance of Quay in order to push or pull images. This requires a [Quay Robot Account configured to federate](https://docs.projectquay.io/manage_quay.html#setting-robot-federation) with a Kubernetes service account.
+
+## Output Keys and Values
+
+| Key | Description |
+| ---------- | ------------------------------------------------------------------------------ |
+| registry | Domain name of the registry you are authenticating to (defaults to `quay.io`). |
+| auth | Base64 encoded authentication string. |
+| expiry | Time when token expires in UNIX time (seconds since January 1, 1970 UTC). |
+
+## Authentication
+
+To configure Robot Account federation, your cluster must have a publicly available [OIDC service account issuer](https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/#service-account-issuer-discovery) endpoint for Quay to validate tokens against against. You can determine the issuer and subject fields by creating and decoding a service account token for the service account you wish to federate with (this is the service account you will use in `spec.serviceAccountRef`). For example, if federating with the `default` service account in the `default` namespace:
+
+Obtain issuer:
+
+```bash
+kubectl create token default -n default | cut -d '.' -f 2 | sed 's/[^=]$/&==/' | base64 -d | jq -r '.iss'
+```
+
+Obtain subject:
+
+```bash
+kubectl create token default -n default | cut -d '.' -f 2 | sed 's/[^=]$/&==/' | base64 -d | jq -r '.sub'
+```
+
+Then use the instructions [here](https://docs.projectquay.io/manage_quay.html#setting-robot-federation) to set up a robot account and federation.
+
+## Example Manifest
+
+```yaml
+{% include 'generator-quay.yaml' %}
+```
+
+Example `ExternalSecret` that references the Quay generator:
+
+```yaml
+{% include 'generator-quay-example.yaml' %}
+```
diff --git a/docs/api/generator/sts.md b/docs/api/generator/sts.md
new file mode 100644
index 00000000000..bf1a3ef253e
--- /dev/null
+++ b/docs/api/generator/sts.md
@@ -0,0 +1,37 @@
+STSSessionToken uses the GetSessionToken API to retrieve a temporary session token.
+
+## Output Keys and Values
+
+| Key | Description |
+|-------------------|-------------------------------------------------------------------------------------|
+| access_key_id | The access key ID that identifies the temporary security credentials. |
+| secret_access_key | The secret access key that can be used to sign requests. |
+| session_token | The token that users must pass to the service API to use the temporary credentials. |
+| expiration | The date on which the current credentials expire. |
+
+## Authentication
+
+You can choose from three authentication mechanisms:
+
+* static credentials using `spec.auth.secretRef`
+* point to a IRSA Service Account with `spec.auth.jwt`
+* use credentials from the [SDK default credentials chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html#credentials-default) from the controller environment
+
+## Request Parameters
+
+Following request parameters can be provided:
+
+- duration seconds -> can specify the TTL of the generated token
+- serial number -> define the serial number of the MFA device used by the user
+- token code -> possible code generated by the above referenced MFA device
+
+## Example Manifest
+
+```yaml
+{% include 'generator-sts.yaml' %}
+```
+
+Example `ExternalSecret` that references the STS Session Token generator:
+```yaml
+{% include 'generator-sts-example.yaml' %}
+```
diff --git a/docs/api/generator/uuid.md b/docs/api/generator/uuid.md
new file mode 100644
index 00000000000..2426647fab5
--- /dev/null
+++ b/docs/api/generator/uuid.md
@@ -0,0 +1,35 @@
+The UUID generator provides random UUIDs that you can feed into your applications. A UUID (Universally Unique Identifier) is a 128-bit label used for information in computer systems. Please see below for the format in use.
+
+## Output Keys and Values
+
+| Key | Description |
+| ---- | ------------------ |
+| uuid | the generated UUID |
+
+## Parameters
+
+The UUID generator does not require any additional parameters.
+
+## Example Manifest
+
+```yaml
+{% include 'generator-uuid.yaml' %}
+```
+
+Example `ExternalSecret` that references the UUID generator:
+
+```yaml
+{% include 'generator-uuid-example.yaml' %}
+```
+
+Which will generate a `Kind=Secret` with a key called 'uuid' that may look like:
+
+```
+EA111697-E7D0-452C-A24C-8E396947E865
+```
+
+With default values you would get something like:
+
+```
+4BEE258F-64C9-4755-92DC-AFF76451471B
+```
diff --git a/docs/api/spec.md b/docs/api/spec.md
index 1efaa7d7fee..2b2ccca1bbb 100644
--- a/docs/api/spec.md
+++ b/docs/api/spec.md
@@ -281,6 +281,18 @@ SecretsManager
AWS STS assume role transitive session tags. Required when multiple rules are used with the provider
+
+
+prefix
+
+string
+
+
+
+(Optional)
+Prefix adds a prefix to all retrieved values.
+
+
AWSServiceType
@@ -869,6 +881,20 @@ External Secrets meta/v1.SecretKeySelector
The Azure ClientSecret of the service principle used for authentication.
+
+
+clientCertificate
+
+
+External Secrets meta/v1.SecretKeySelector
+
+
+
+
+(Optional)
+The Azure ClientCertificate of the service principle used for authentication.
+
+
AzureKVProvider
@@ -987,11 +1013,431 @@ string
+BeyondTrustProviderSecretRef
+
+
+(Appears on:
+BeyondtrustAuth )
+
+
+
+
+
+
+Field
+Description
+
+
+
+
+
+value
+
+string
+
+
+
+(Optional)
+Value can be specified directly to set a value without using a secret.
+
+
+
+
+secretRef
+
+
+External Secrets meta/v1.SecretKeySelector
+
+
+
+
+(Optional)
+SecretRef references a key in a secret that will be used as value.
+
+
+
+
+BeyondtrustAuth
+
+
+(Appears on:
+BeyondtrustProvider )
+
+
+
Configures a store to sync secrets using BeyondTrust Password Safe.
+
+
+BeyondtrustProvider
+
+
+(Appears on:
+SecretStoreProvider )
+
+
+
+
+
+
+Field
+Description
+
+
+
+
+
+auth
+
+
+BeyondtrustAuth
+
+
+
+
+Auth configures how the operator authenticates with Beyondtrust.
+
+
+
+
+server
+
+
+BeyondtrustServer
+
+
+
+
+Auth configures how API server works.
+
+
+
+
+BeyondtrustServer
+
+
+(Appears on:
+BeyondtrustProvider )
+
+
+
Configures a store to sync secrets using BeyondTrust Password Safe.
+
+
+
+
+Field
+Description
+
+
+
+
+
+apiUrl
+
+string
+
+
+
+
+
+
+
+retrievalType
+
+string
+
+
+
+The secret retrieval type. SECRET = Secrets Safe (credential, text, file). MANAGED_ACCOUNT = Password Safe account associated with a system.
+
+
+
+
+separator
+
+string
+
+
+
+A character that separates the folder names.
+
+
+
+
+verifyCA
+
+bool
+
+
+
+
+
+
+
+clientTimeOutSeconds
+
+int
+
+
+
+Timeout specifies a time limit for requests made by this Client. The timeout includes connection time, any redirects, and reading the response body. Defaults to 45 seconds.
+
+
+
+
+BitwardenSecretsManagerAuth
+
+
+(Appears on:
+BitwardenSecretsManagerProvider )
+
+
+
BitwardenSecretsManagerAuth contains the ref to the secret that contains the machine account token.
+
+
+BitwardenSecretsManagerProvider
+
+
+(Appears on:
+SecretStoreProvider )
+
+
+
BitwardenSecretsManagerProvider configures a store to sync secrets with a Bitwarden Secrets Manager instance.
+
+
+
+
+Field
+Description
+
+
+
+
+
+apiURL
+
+string
+
+
+
+
+
+
+
+identityURL
+
+string
+
+
+
+
+
+
+
+bitwardenServerSDKURL
+
+string
+
+
+
+
+
+
+
+caBundle
+
+string
+
+
+
+(Optional)
+Base64 encoded certificate for the bitwarden server sdk. The sdk MUST run with HTTPS to make sure no MITM attack
+can be performed.
+
+
+
+
+caProvider
+
+
+CAProvider
+
+
+
+
+(Optional)
+see: https://external-secrets.io/latest/spec/#external-secrets.io/v1alpha1.CAProvider
+
+
+
+
+organizationID
+
+string
+
+
+
+OrganizationID determines which organization this secret store manages.
+
+
+
+
+projectID
+
+string
+
+
+
+ProjectID determines which project this secret store manages.
+
+
+
+
+auth
+
+
+BitwardenSecretsManagerAuth
+
+
+
+
+Auth configures how secret-manager authenticates with a bitwarden machine account instance.
+Make sure that the token being used has permissions on the given secret.
+
+
+
+
+BitwardenSecretsManagerSecretRef
+
+
+(Appears on:
+BitwardenSecretsManagerAuth )
+
+
+
BitwardenSecretsManagerSecretRef contains the credential ref to the bitwarden instance.
+
+
CAProvider
(Appears on:
AkeylessProvider ,
+BitwardenSecretsManagerProvider ,
ConjurProvider ,
KubernetesServer ,
VaultProvider )
@@ -1301,7 +1747,8 @@ string
(Optional)
-The name of the external secrets to be created defaults to the name of the ClusterExternalSecret
+The name of the external secrets to be created.
+Defaults to the name of the ClusterExternalSecret
@@ -1489,7 +1936,8 @@ string
(Optional)
-The name of the external secrets to be created defaults to the name of the ClusterExternalSecret
+The name of the external secrets to be created.
+Defaults to the name of the ClusterExternalSecret
@@ -1847,9 +2295,22 @@ Kubernetes meta/v1.LabelSelector
+(Optional)
Choose namespaces by name
+
+
+namespaceRegexes
+
+[]string
+
+
+
+(Optional)
+Choose namespaces by using regex matching
+
+
ConjurAPIKey
@@ -2220,6 +2681,111 @@ External Secrets meta/v1.SecretKeySelector
+Device42Auth
+
+
+(Appears on:
+Device42Provider )
+
+
+
+
+Device42Provider
+
+
+(Appears on:
+SecretStoreProvider )
+
+
+
Device42Provider configures a store to sync secrets with a Device42 instance.
+
+
+
+
+Field
+Description
+
+
+
+
+
+host
+
+string
+
+
+
+URL configures the Device42 instance URL.
+
+
+
+
+auth
+
+
+Device42Auth
+
+
+
+
+Auth configures how secret-manager authenticates with a Device42 instance.
+
+
+
+
+Device42SecretRef
+
+
+(Appears on:
+Device42Auth )
+
+
+
+
DopplerAuth
@@ -2440,8 +3006,10 @@ Kubernetes meta/v1.Duration
-RefreshInterval is the amount of time before the values are read again from the SecretStore provider
+
RefreshInterval is the amount of time before the values are read again from the SecretStore provider,
+specified as Golang Duration strings.
Valid time units are “ns”, “us” (or “µs”), “ms”, “s”, “m”, “h”
+Example values: “1h”, “2h30m”, “5d”, “10s”
May be set to zero to fetch and create it once. Defaults to 1h.
@@ -2590,8 +3158,7 @@ string
-SecretKey defines the key in which the controller stores
-the value. This is the key in the Kind=Secret
+The key in the Kubernetes Secret to store the value.
@@ -2619,7 +3186,7 @@ StoreSourceRef
SourceRef allows you to override the source
-from which the value will pulled from.
+from which the value will be pulled.
@@ -3181,8 +3748,10 @@ Kubernetes meta/v1.Duration
-RefreshInterval is the amount of time before the values are read again from the SecretStore provider
+
RefreshInterval is the amount of time before the values are read again from the SecretStore provider,
+specified as Golang Duration strings.
Valid time units are “ns”, “us” (or “µs”), “ms”, “s”, “m”, “h”
+Example values: “1h”, “2h30m”, “5d”, “10s”
May be set to zero to fetch and create it once. Defaults to 1h.
@@ -3390,8 +3959,7 @@ string
(Optional)
-Name defines the name of the Secret resource to be managed
-This field is immutable
+
The name of the Secret resource to be managed.
Defaults to the .metadata.name of the ExternalSecret resource
@@ -3406,8 +3974,8 @@ ExternalSecretCreationPolicy
(Optional)
-CreationPolicy defines rules on how to create the resulting Secret
-Defaults to ‘Owner’
+CreationPolicy defines rules on how to create the resulting Secret.
+Defaults to “Owner”
@@ -3421,8 +3989,8 @@ ExternalSecretDeletionPolicy
(Optional)
-DeletionPolicy defines rules on how to delete the resulting Secret
-Defaults to ‘Retain’
+DeletionPolicy defines rules on how to delete the resulting Secret.
+Defaults to “Retain”
@@ -3903,7 +4471,18 @@ string
-ProjectID project where secret is located
+ProjectID project where secret is located
+
+
+
+
+location
+
+string
+
+
+
+Location optionally defines a location for a secret
@@ -4005,7 +4584,7 @@ string
-Specify the Kind of the resource, e.g. Password, ACRAccessToken etc.
+Specify the Kind of the generator resource
@@ -4347,6 +4926,92 @@ string
+InfisicalAuth
+
+
+(Appears on:
+InfisicalProvider )
+
+
+
+
+InfisicalProvider
+
+
+(Appears on:
+SecretStoreProvider )
+
+
+
InfisicalProvider configures a store to sync secrets using the Infisical provider.
+
+
+
+
+Field
+Description
+
+
+
+
+
+auth
+
+
+InfisicalAuth
+
+
+
+
+Auth configures how the Operator authenticates with the Infisical API
+
+
+
+
+secretsScope
+
+
+MachineIdentityScopeInWorkspace
+
+
+
+
+
+
+
+
+hostAPI
+
+string
+
+
+
+(Optional)
+
+
+
+
KeeperSecurityProvider
@@ -4475,6 +5140,7 @@ KubernetesServer
+(Optional)
configures the Kubernetes server Address.
@@ -4488,11 +5154,26 @@ KubernetesAuth
+(Optional)
Auth configures how secret-manager authenticates with a Kubernetes instance.
+authRef
+
+
+External Secrets meta/v1.SecretKeySelector
+
+
+
+
+(Optional)
+A reference to a secret that contains the auth information.
+
+
+
+
remoteNamespace
string
@@ -4561,12 +5242,78 @@ CAProvider
+MachineIdentityScopeInWorkspace
+
+
+(Appears on:
+InfisicalProvider )
+
+
+
+
+
+
+Field
+Description
+
+
+
+
+
+secretsPath
+
+string
+
+
+
+(Optional)
+
+
+
+
+recursive
+
+bool
+
+
+
+(Optional)
+
+
+
+
+environmentSlug
+
+string
+
+
+
+
+
+
+
+projectSlug
+
+string
+
+
+
+
+
+
+
NoSecretError
NoSecretError shall be returned when a GetSecret can not find the
desired secret. This is used for deletionPolicy.
+NotModifiedError
+
+
+
NotModifiedError to signal that the webhook received no changes,
+and it should just return without doing anything.
+
OnboardbaseAuthSecretRef
@@ -5155,7 +5902,125 @@ PasswordDepotSecretRef
SecretStoreProvider )
-
Configures a store to sync secrets with a Password Depot instance.
+Configures a store to sync secrets with a Password Depot instance.
+
+
+
+
+Field
+Description
+
+
+
+
+
+host
+
+string
+
+
+
+URL configures the Password Depot instance URL.
+
+
+
+
+database
+
+string
+
+
+
+Database to use as source
+
+
+
+
+auth
+
+
+PasswordDepotAuth
+
+
+
+
+Auth configures how secret-manager authenticates with a Password Depot instance.
+
+
+
+
+PasswordDepotSecretRef
+
+
+(Appears on:
+PasswordDepotAuth )
+
+
+
+
+PreviderAuth
+
+
+(Appears on:
+PreviderProvider )
+
+
+
PreviderAuth contains a secretRef for credentials.
+
+
+PreviderAuthSecretRef
+
+
+(Appears on:
+PreviderAuth )
+
+
+
PreviderAuthSecretRef holds secret references for Previder Vault credentials.
@@ -5167,48 +6032,27 @@ PasswordDepotSecretRef
-host
-
-string
-
-
-
-URL configures the Password Depot instance URL.
-
-
-
-
-database
-
-string
-
-
-
-Database to use as source
-
-
-
-
-auth
+accessToken
-
-PasswordDepotAuth
+
+External Secrets meta/v1.SecretKeySelector
-Auth configures how secret-manager authenticates with a Password Depot instance.
+The AccessToken is used for authentication
-PasswordDepotSecretRef
+PreviderProvider
(Appears on:
-PasswordDepotAuth )
+SecretStoreProvider )
+
PreviderProvider configures a store to sync secrets using the Previder Secret Manager provider.
+SecretServerProvider
+
+
+(Appears on:
+SecretStoreProvider )
+
+
+
See https://github.com/DelineaXPM/tss-sdk-go/blob/main/server/server.go .
+
+
+
+
+Field
+Description
+
+
+
+
+
+username
+
+
+SecretServerProviderRef
+
+
+
+
+Username is the secret server account username.
+
+
+
+
+password
+
+
+SecretServerProviderRef
+
+
+
+
+Password is the secret server account password.
+
+
+
+
+serverURL
+
+string
+
+
+
+ServerURL
+URL to your secret server installation
+
+
+
+
+SecretServerProviderRef
+
+
+(Appears on:
+SecretServerProvider )
+
+
+
+
+
+
+Field
+Description
+
+
+
+
+
+value
+
+string
+
+
+
+(Optional)
+Value can be specified directly to set a value without using a secret.
+
+
+
+
+secretRef
+
+
+External Secrets meta/v1.SecretKeySelector
+
+
+
+
+(Optional)
+SecretRef references a key in a secret that will be used as value.
+
+
+
+
SecretStore
@@ -5697,6 +6662,20 @@ AkeylessProvider
+bitwardensecretsmanager
+
+
+BitwardenSecretsManagerProvider
+
+
+
+
+(Optional)
+BitwardenSecretsManager configures this store to sync secrets using BitwardenSecretsManager provider
+
+
+
+
vault
@@ -5907,6 +6886,20 @@ DopplerProvider
+previder
+
+
+PreviderProvider
+
+
+
+
+(Optional)
+Previder configures this store to sync secrets using the Previder provider
+
+
+
+
onboardbase
@@ -5964,6 +6957,21 @@ DelineaProvider
+secretserver
+
+
+SecretServerProvider
+
+
+
+
+(Optional)
+SecretServer configures this store to sync secrets using SecretServer provider
+https://docs.delinea.com/online-help/secret-server/start.htm
+
+
+
+
chef
@@ -6030,6 +7038,48 @@ PassboltProvider
(Optional)
+
+
+device42
+
+
+Device42Provider
+
+
+
+
+(Optional)
+Device42 configures this store to sync secrets using the Device42 provider
+
+
+
+
+infisical
+
+
+InfisicalProvider
+
+
+
+
+(Optional)
+Infisical configures this store to sync secrets using the Infisical provider
+
+
+
+
+beyondtrust
+
+
+BeyondtrustProvider
+
+
+
+
+(Optional)
+Beyondtrust configures this store to sync secrets using Password Safe provider.
+
+
SecretStoreRef
@@ -6777,6 +7827,7 @@ string
+The name of the ConfigMap/Secret resource
@@ -6789,6 +7840,7 @@ string
+A list of keys in the ConfigMap/Secret to use as templates for Secret data
@@ -6817,6 +7869,7 @@ string
+A key in the ConfigMap/Secret
@@ -6907,6 +7960,48 @@ External Secrets meta/v1.SecretKeySelector
+UniversalAuthCredentials
+
+
+(Appears on:
+InfisicalAuth )
+
+
+
+
ValidationResult
(byte
alias)
@@ -7973,6 +9068,18 @@ the option is enabled serverside.
https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header
+
+
+headers
+
+map[string]string
+
+
+
+(Optional)
+Headers to be added in Vault request
+
+
VaultUserPassAuth
@@ -8083,7 +9190,7 @@ string
-The key the value inside of the provider type to use, only used with “Secret” type
+The key where the CA certificate can be found in the Secret or ConfigMap.
diff --git a/docs/contributing/process.md b/docs/contributing/process.md
index 5f0a84c3190..983d1acc83d 100644
--- a/docs/contributing/process.md
+++ b/docs/contributing/process.md
@@ -38,7 +38,11 @@ We have an extensive set of e2e tests that test the integration with *real* clou
Maintainers must trigger these kind of tests manually for PRs that come from forked repositories. These tests run inside a `kind` cluster in the GitHub Actions runner:
```
-/ok-to-test sha=xxxxxx
+/ok-to-test sha=
+```
+Examples:
+```
+/ok-to-test sha=b8ca0040200a7a05d57048d86a972fdf833b8c9b
```
#### Executing e2e tests locally
diff --git a/docs/eso-blogs.md b/docs/eso-blogs.md
index ff6475c3579..6de37abf812 100644
--- a/docs/eso-blogs.md
+++ b/docs/eso-blogs.md
@@ -2,6 +2,23 @@
A list of blogs written by people all over the community. Feel free to let us know if you are writing about ESO at some place! We would be happy to mention you here!
+## [Secrets Replication in Kubernetes with ESO](https://externalsecrets.com/blog/secrets-replication/)
+
+[@Gustavo Carvalho](https://www.linkedin.com/in/gustavo-carvalho-51427444/) describes how to use ESO to synchronize in-cluster secrets across multiple namespaces using only two manifests: `ClusterSecretStore` and `ClusterExternalSecret`.
+
+## [Pulumi ESC and External Secrets Operator: The Perfect Solution for Today's Cloud-Native Secret Management](https://www.pulumi.com/blog/cloud-native-secret-management-with-pulumi-esc-and-external-secrets-operator/)
+
+[@Engin Diri](https://www.linkedin.com/in/engin-diri/) walks through the integration of ESO with Pulumi ESC, offering a practical guide for enhancing security from cloud-native application development to infrastructure provisioning. This blog provides a hands-on guide to setting up ESO and Pulumi ESC, and demonstrates how to use them together to manage secrets in a Kubernetes cluster.
+
+## [From vulnerable to unhackable: secrets management in cloud-native environments](https://medium.com/@as_mallem/from-vulnerable-to-unhackable-secrets-management-in-cloud-native-environments-cb341bd97869/)
+
+[@Saliha Mallem](https://www.linkedin.com/in/saliha-mallem/) writes about integrating ESO with IBM Cloud Secrets Manager. In her blog, she outlines the steps to deploy ESO and demonstrates how to use both the Secrets Manager API and the Vault API for seamless integration. The blog is user-friendly and easy to follow.
+
+## [Enhancing Kubernetes Security and Flexibility with the CyberArk Conjur and ESO Integration](https://developer.cyberark.com/blog/enhancing-kubernetes-security-and-flexibility-with-the-cyberark-conjur-and-eso-integration/)
+
+[@szh](https://github.com/szh) Writes about using ESO with CyberArk Conjur. He includes detailed steps on how to
+set up a local environment with Docker Desktop and how to deploy ESO and Conjur OSS on it.
+
## [Comparing External Secrets Operator with Secret Storage CSI as Kubernetes External Secrets is Deprecated](https://mixi-developers.mixi.co.jp/compare-eso-with-secret-csi-402bf37f20bc)
@riddle writes about choosing ESO when comparing with Secret Store CSI Driver in their specific use case. They show us the relevant differences between the projects when looking at their scenario and requirements while integrating with ArgoCD. [Comparing External Secrets Operator with Secret Storage CSI as Kubernetes External Secrets is Deprecated](https://mixi-developers.mixi.co.jp/compare-eso-with-secret-csi-402bf37f20bc)
@@ -59,3 +76,11 @@ Emin writes about the Push Secret feature of ESO and how this new feature revers
## [GCP Secret Manager with self-hosted Kubernetes](https://medium.com/@jjlakis/gcp-secret-manager-with-self-hosted-kubernetes-db35d01d65f0)
Jacek writes about bringing GCP secrets to on-premises cluster through External Secrets Operator intergration with workload identity.
+
+## [Injecting AWS Secrets in a Kubernetes Cluster with External Secrets Operator](https://blog.devops.dev/injecting-external-secrets-in-a-kubernetes-cluster-1e9bbe0f0d5b)
+
+Ali writes about integrating AWS Secrets Manager and Parameter Store secrets within an EKS Cluster using ESO. He shows a quick setup of the operator, and how to fetch secrets in a repeatable fashion. The guide is bundled with cool illustrations and code snippets that describe the ESO architecture and injection process
+
+## [Encoding & Decoding Kubernetes Secrets — ESO Advanced Templating](https://blog.devops.dev/encoding-decoding-kubernetes-secrets-externalsecrets-operator-826b9680df63)
+
+Here, Ali briefly introduces templates within ESO and describes some use cases where templating can be crucial. Code snippets are included where needed too.
diff --git a/docs/eso-tools.md b/docs/eso-tools.md
new file mode 100644
index 00000000000..642ba737de1
--- /dev/null
+++ b/docs/eso-tools.md
@@ -0,0 +1,7 @@
+# ESO Tools
+
+This page lists tools that are useful for working with External Secrets Operator. help you work with External Secrets Operator better.
+
+## [secret2es](https://github.com/Sn0rt/secret2es)
+
+This tool allows administrators to migrate secrets originally created by argocd-vault-plugin to external-secrets ES object.
diff --git a/docs/examples/bitwarden.md b/docs/examples/bitwarden.md
index e5653c69ee2..2680b13c519 100644
--- a/docs/examples/bitwarden.md
+++ b/docs/examples/bitwarden.md
@@ -2,27 +2,29 @@
Bitwarden is an integrated open source password management solution for individuals, teams, and business organizations.
-## How is it working ?
+!!! note
-To make external-secret compatible with BitWarden, we need:
+ This documentation is for Bitwarden **Password Manager**.
+ It is different from [Bitwarden Secrets Manager](https://bitwarden.com/products/secrets-manager/), which enables developers, DevOps, and cybersecurity teams to centrally store, manage, and deploy secrets at scale.
+ To integrate with Bitwarden **Secrets Manager**, reference the [provider documentation](../provider/bitwarden-secrets-manager.md).
-* External-Secret >= 0.8.0
-* To use the Webhook Provider
-* 2 (Cluster)SecretStores
+## How does it work?
+
+To make external-secrets compatible with Bitwarden, we need:
+
+* External Secrets Operator >= 0.8.0
+* Multiple (Cluster)SecretStores using the webhook provider
* BitWarden CLI image running `bw serve`
-When you create a new external-secret object,
-External-Secret Webhook provider will do a query to the Bitwarden CLI pod,
-which is synced with the BitWarden server.
+When you create a new external-secret object, the External Secrets webhook provider will query the Bitwarden CLI pod that is synced with the Bitwarden server.
## Requirements
-* Bitwarden account (it works also with VaultWarden)
-* A Kubernetes secret which contains your BitWarden Credentials
-* You need a Docker image with BitWarden CLI installed.
- You could use `ghcr.io/charlesthomas/bitwarden-cli:2023.12.1` or build your own.
+* Bitwarden account (it also works with Vaultwarden!)
+* A Kubernetes secret which contains your Bitwarden credentials
+* A Docker image running the Bitwarden CLI. You could use `ghcr.io/charlesthomas/bitwarden-cli:2023.12.1` or build your own.
-Here an example of Dockerfile use to build this image:
+Here is an example of a Dockerfile used to build the image:
```dockerfile
FROM debian:sid
@@ -41,7 +43,7 @@ COPY entrypoint.sh /
CMD ["/entrypoint.sh"]
```
-And the content of `entrypoint.sh`
+And the content of `entrypoint.sh`:
```bash
#!/bin/bash
@@ -57,8 +59,7 @@ echo 'Running `bw server` on port 8087'
bw serve --hostname 0.0.0.0 #--disable-origin-protection
```
-
-## Deploy Bitwarden Credentials
+## Deploy Bitwarden credentials
```yaml
{% include 'bitwarden-cli-secrets.yaml' %}
@@ -70,30 +71,37 @@ bw serve --hostname 0.0.0.0 #--disable-origin-protection
{% include 'bitwarden-cli-deployment.yaml' %}
```
-> NOTE: Deploying a network policy is recommended since, there is no authentication to query the BitWarden CLI, which means that your secrets are exposed.
+> NOTE: Deploying a network policy is recommended since there is no authentication to query the Bitwarden CLI, which means that your secrets are exposed.
-> NOTE: In this example the Liveness probe is quering /sync to ensure that the BitWarden CLI is able to connect to the server and also to sync secrets. (The secret sync is only every 2 minutes in this example)
+> NOTE: In this example the Liveness probe is querying /sync to ensure that the Bitwarden CLI is able to connect to the server and is also synchronised. (The secret sync is only every 2 minutes in this example)
-## Deploy ClusterSecretStore (Or SecretStore)
+## Deploy (Cluster)SecretStores
-Here the two ClusterSecretStore to deploy
+There are four possible (Cluster)SecretStores to deploy, each can access different types of fields from an item in the Bitwarden vault. It is not required to deploy them all.
```yaml
{% include 'bitwarden-secret-store.yaml' %}
```
+## Usage
+
+(Cluster)SecretStores:
+
+* `bitwarden-login`: Use to get the `username` or `password` fields
+* `bitwarden-fields`: Use to get custom fields
+* `bitwarden-notes`: Use to get notes
+* `bitwarden-attachments`: Use to get attachments
+
+remoteRef:
-## How to use it ?
+* `key`: ID of a secret, which can be found in the URL `itemId` parameter:
+ `https://myvault.com/#/vault?type=login&itemId=........-....-....-....-............`s
-* If you need the `username` or the `password` of a secret, you have to use `bitwarden-login`
-* If you need a custom field of a secret, you have to use `bitwarden-fields`
-* If you need to use a Bitwarden Note for multiline strings (SSH keys, service account json files), you have to use `bitwarden-notes`
-* The `key` is the ID of a secret, which can be find in the URL with the `itemId` value:
- `https://myvault.com/#/vault?itemId=........-....-....-....-............`
-* The `property` is the name of the field:
- * `username` for the username of a secret (`bitwarden-login` SecretStore)
- * `password` for the password of a secret (`bitwarden-login` SecretStore)
- * `name_of_the_custom_field` for any custom field (`bitwarden-fields` SecretStore)
+* `property`: Name of the field to access
+ * `username` for the username of a secret (`bitwarden-login` SecretStore)
+ * `password` for the password of a secret (`bitwarden-login` SecretStore)
+ * `name_of_the_custom_field` for any custom field (`bitwarden-fields` SecretStore)
+ * `id_or_name_of_the_attachment` for any attachment (`bitwarden-attachment`, SecretStore)
```yaml
{% include 'bitwarden-secret.yaml' %}
diff --git a/docs/guides/common-k8s-secret-types.md b/docs/guides/common-k8s-secret-types.md
index 48ef1a4b72a..c4883948d27 100644
--- a/docs/guides/common-k8s-secret-types.md
+++ b/docs/guides/common-k8s-secret-types.md
@@ -32,7 +32,7 @@ This will generate a valid dockerconfigjson secret for you to use!
You can get the final value with:
```bash
-kubectl get secret secret-to-be-created -n -o jsonpath="{.data\.dockerconfigjson}" | base64 -d
+kubectl get secret secret-to-be-created -n -o jsonpath="{.data.\.dockerconfigjson}" | base64 -d
```
Alternately, if you only have the container registry name and password value, you can take advantage of the advanced ExternalSecret templating functions to create the secret:
diff --git a/docs/guides/generator.md b/docs/guides/generator.md
index 2876dc8024f..398df4c19e8 100644
--- a/docs/guides/generator.md
+++ b/docs/guides/generator.md
@@ -1,4 +1,3 @@
-
Generators allow you to generate values. They are used through a ExternalSecret `spec.DataFrom`. They are referenced from a custom resource using `sourceRef.generatorRef`.
If the External Secret should be refreshed via `spec.refreshInterval` the generator produces a map of values with the `generator.spec` as input. The generator does not keep track of the produced values. Every invocation produces a new set of values.
@@ -24,4 +23,47 @@ spec:
apiVersion: generators.external-secrets.io/v1alpha1
kind: ECRAuthorizationToken
name: "my-ecr"
-```
\ No newline at end of file
+```
+
+## Cluster Generate Resource
+
+It's possible to use a `Cluster` scoped generator. At the moment of this writing, this Generator
+will only help in locating the Generator cluster-wide. It doesn't mean that the generator can create resources in all
+namespaces. It will still only create a resource in the given namespace where the referencing `ExternalSecret` lives.
+
+To define a `ClusterGenerator` use the following config:
+
+```yaml
+apiVersion: generators.external-secrets.io/v1alpha1
+kind: ClusterGenerator
+metadata:
+ name: my-generator
+spec:
+ kind: Password
+ generator:
+ passwordSpec:
+ length: 42
+ digits: 5
+ symbols: 5
+ symbolCharacters: "-_$@"
+ noUpper: false
+ allowRepeat: true
+```
+
+All the generators are available as a ClusterGenerator spec. The `kind` field MUST match the kind of the Generator
+exactly. The following Spec fields are available:
+
+```go
+type GeneratorSpec struct {
+ ACRAccessTokenSpec *ACRAccessTokenSpec `json:"acrAccessTokenSpec,omitempty"`
+ ECRAuthorizationTokenSpec *ECRAuthorizationTokenSpec `json:"ecrAuthorizationTokenSpec,omitempty"`
+ FakeSpec *FakeSpec `json:"fakeSpec,omitempty"`
+ GCRAccessTokenSpec *GCRAccessTokenSpec `json:"gcrAccessTokenSpec,omitempty"`
+ GithubAccessTokenSpec *GithubAccessTokenSpec `json:"githubAccessTokenSpec,omitempty"`
+ PasswordSpec *PasswordSpec `json:"passwordSpec,omitempty"`
+ STSSessionTokenSpec *STSSessionTokenSpec `json:"stsSessionTokenSpec,omitempty"`
+ UUIDSpec *UUIDSpec `json:"uuidSpec,omitempty"`
+ VaultDynamicSecretSpec *VaultDynamicSecretSpec `json:"vaultDynamicSecretSpec,omitempty"`
+ WebhookSpec *WebhookSpec `json:"webhookSpec,omitempty"`
+}
+```
diff --git a/docs/guides/pushsecrets.md b/docs/guides/pushsecrets.md
index 2e0fc959bc2..56f05238973 100644
--- a/docs/guides/pushsecrets.md
+++ b/docs/guides/pushsecrets.md
@@ -3,7 +3,7 @@ Contrary to what `ExternalSecret` does by pulling secrets from secret providers
The update behavior of `PushSecret` is controlled by `spec.updatePolicy`. The default policy is `Replace`, such that secrets are overwritten in the provider, regardless of whether there already is a secret present in the provider at the given location. If you do not want `PushSecret` to overwrite existing secrets in the provider, you can set `spec.UpdatePolicy` to `IfNotExists`. With this policy, the provider becomes the source of truth. Please note that with using `spec.updatePolicy=IfNotExists` it is possible that the secret value referenced by the `PushSecret` within the cluster differs from the secret value at the given location in the provider.
-By default, the secret created in the secret provided will not be deleted even after deleting the `PushSecret`, unless you set `spec.deletionPolicy` to `Delete`.
+By default, the secret created in the secret provided will not be deleted even after deleting the `PushSecret`, unless you set `spec.deletionPolicy` to `Delete`.
``` yaml
@@ -14,7 +14,7 @@ By default, the secret created in the secret provided will not be deleted even a
An interesting use case for `kind=PushSecret` is backing up your current secret from one provider to another one.
-Imagine you have your secrets in GCP and you want to back them up in Azure Key Vault. You would then create a `SecretStore` for each provider, and an `ExternalSecret` to pull the secrets from GCP. This will generate a `kind=Secret` in your cluster that you can use as the source of a `PushSecret` configured with the Azure `SecretStore`.
+Imagine you have your secrets in GCP and you want to back them up in Azure Key Vault. You would then create a `SecretStore` for each provider, and an `ExternalSecret` to pull the secrets from GCP. This will generate a `kind=Secret` in your cluster that you can use as the source of a `PushSecret` configured with the Azure `SecretStore`.
![PushSecretBackup](../pictures/diagrams-pushsecret-backup.png)
@@ -22,15 +22,14 @@ Imagine you have your secrets in GCP and you want to back them up in Azure Key V
There are two ways to push an entire secret without defining all keys individually.
-By leaving off the secret key and remote property options.
+### 1. By leaving off the secret key and remote property options.
```yaml
{% include 'full-pushsecret-no-key-no-property.yaml' %}
```
-
This will result in all keys being pushed as they are into the remote location.
-By leaving off the secret key but setting the remote property option.
+### 2. By leaving off the secret key but setting the remote property option.
```yaml
{% include 'full-pushsecret-no-key-with-property.yaml' %}
@@ -38,8 +37,19 @@ By leaving off the secret key but setting the remote property option.
This will _marshal_ the entire secret data and push it into this single property as a JSON object.
-!!! warning inline
+!!! warning
+
This should _ONLY_ be done if the secret data is marshal-able. Values like, binary data cannot be marshaled and will result in error or invalid secret data.
-### Key conversion strategy
-You can also set `data[*].conversionStrategy: ReverseUnicode` to reverse the invalid character replaced by the `conversionStrategy: Unicode` configuration in the `ExternalSecret` object as [documented here](../guides/getallsecrets/#avoiding-name-conflicts).
+
+#### Key conversion strategy
+You can also set `data[*].conversionStrategy: ReverseUnicode` to reverse the invalid character replaced by the `conversionStrategy: Unicode` configuration in the `ExternalSecret` object as [documented here](../guides/getallsecrets.md#avoiding-name-conflicts).
+
+## Rotate Secrets
+
+You can use ESO to rotate secrets by using the PushSecret and Generator resources. ESO will consult the `Kind=Generator` to generate a new secret and then ESO will store it.
+Every `spec.refreshInterval` the secret will be rotated and the value will be replaced in the store unless `spec.updatePolicy=IfNotExist` is set. Then ESO will generate the secret once and won't rotate it.
+
+```yaml
+{% include 'pushsecret-generator-rotation-example.yaml' %}
+```
diff --git a/docs/guides/security-best-practices.md b/docs/guides/security-best-practices.md
index 1637474873d..d199db859b5 100644
--- a/docs/guides/security-best-practices.md
+++ b/docs/guides/security-best-practices.md
@@ -63,6 +63,14 @@ scopedRBAC: true
scopedNamespace: my-namespace
```
+### 5. Restrict Webhook TLS Ciphers
+
+Consider installing ESO restricting webhook ciphers. Use the following Helm values to scope webhook for specific TLS ciphers:
+```yaml
+webhook:
+ extraArgs:
+ tls-ciphers: "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"
+```
## Pod Security
The Pods of the External Secrets Operator have been configured to meet the [Pod Security Standards](https://kubernetes.io/docs/concepts/security/pod-security-standards/), specifically the restricted profile. This configuration ensures a strong security posture by implementing recommended best practices for hardening Pods, including those outlined in the [NSA Kubernetes Hardening Guide](https://media.defense.gov/2022/Aug/29/2003066362/-1/-1/0/CTR_KUBERNETES_HARDENING_GUIDANCE_1.2_20220829.PDF).
diff --git a/docs/guides/templating-v1.md b/docs/guides/templating-v1.md
index 914762318b9..23798214233 100644
--- a/docs/guides/templating-v1.md
+++ b/docs/guides/templating-v1.md
@@ -4,8 +4,14 @@
Templating Engine v1 is **deprecated** and will be removed in the future. Please migrate to engine v2 and take a look at our [upgrade guide](templating.md#migrating-from-v1) for changes.
+!!! note
-With External Secrets Operator you can transform the data from the external secret provider before it is stored as `Kind=Secret`. You can do this with the `Spec.Target.Template`. Each data value is interpreted as a [golang template](https://golang.org/pkg/text/template/).
+ Templating Engine v1 does NOT support templating the `spec.target.template.metadata` fields, or the keys of the `spec.target.template.data` map, it will treat them as plain strings.
+ To use templates in annotations/labels/data-keys, please use Templating Engine v2.
+
+With External Secrets Operator you can transform the data from the external secret provider before it is stored as `Kind=Secret`. You can do this with the `Spec.Target.Template`.
+
+Each data value is interpreted as a [Go template](https://golang.org/pkg/text/template/). Please note that referencing a non-existing key in the template will raise an error, instead of being suppressed.
## Examples
diff --git a/docs/guides/templating.md b/docs/guides/templating.md
index 069f2c5d323..a4d535c3090 100644
--- a/docs/guides/templating.md
+++ b/docs/guides/templating.md
@@ -1,6 +1,8 @@
# Advanced Templating v2
-With External Secrets Operator you can transform the data from the external secret provider before it is stored as `Kind=Secret`. You can do this with the `Spec.Target.Template`. Each data value is interpreted as a [golang template](https://golang.org/pkg/text/template/).
+With External Secrets Operator you can transform the data from the external secret provider before it is stored as `Kind=Secret`. You can do this with the `Spec.Target.Template`.
+
+Each data value is interpreted as a [Go template](https://golang.org/pkg/text/template/). Please note that referencing a non-existing key in the template will raise an error, instead of being suppressed.
!!! note
@@ -128,6 +130,12 @@ You can achieve that by using the `filterPEM` function to extract a specific typ
{% include 'filterpem-template-v2-external-secret.yaml' %}
```
+In case you have a secret that contains a (partial) certificate chain you can extract the `leaf`, `intermediate` or `root` certificate(s) using the `filterCertChain` function. See the following example on how to use the `filterPEM` and `filterCertChain` functions together to split the certificate chain into a `tlc.crt` part only containting the leaf certificate and a `ca.crt` part with all the intermediate certificates.
+
+```yaml
+{% include 'filtercertchain-template-v2-external-secret.yaml' %}
+```
+
## Templating with PushSecret
`PushSecret` templating is much like `ExternalSecrets` templating. In-fact under the hood, it's using the same data structure.
@@ -156,9 +164,12 @@ In addition to that you can use over 200+ [sprig functions](http://masterminds.g
| pkcs12keyPass | Same as `pkcs12key`. Uses the provided password to decrypt the PKCS#12 archive. |
| pkcs12cert | Extracts all certificates from a PKCS#12 archive and orders them if possible. If disjunct or multiple leaf certs are provided they are returned as-is. Sort order: `leaf / intermediate(s) / root`. |
| pkcs12certPass | Same as `pkcs12cert`. Uses the provided password to decrypt the PKCS#12 archive. |
-| pemToPkcs12 | Takes a PEM encoded certificate and key and creates a base64 enoded PKCS#12 archive. |
+| pemToPkcs12 | Takes a PEM encoded certificate and key and creates a base64 encoded PKCS#12 archive. |
| pemToPkcs12Pass | Same as `pemToPkcs12`. Uses the provided password to encrypt the PKCS#12 archive. |
+| fullPemToPkcs12 | Takes a PEM encoded certificates chain and key and creates a base64 encoded PKCS#12 archive. |
+| fullPemToPkcs12Pass | Same as `fullPemToPkcs12`. Uses the provided password to encrypt the PKCS#12 archive. |
| filterPEM | Filters PEM blocks with a specific type from a list of PEM blocks. |
+| filterCertChain | Filters PEM block(s) with a specific certificate type (`leaf`, `intermediate` or `root`) from a certificate chain of PEM blocks (PEM blocks with type `CERTIFICATE`). |
| jwkPublicKeyPem | Takes an json-serialized JWK and returns an PEM block of type `PUBLIC KEY` that contains the public key. [See here](https://golang.org/pkg/crypto/x509/#MarshalPKIXPublicKey) for details. |
| jwkPrivateKeyPem | Takes an json-serialized JWK as `string` and returns an PEM block of type `PRIVATE KEY` that contains the private key in PKCS #8 format. [See here](https://golang.org/pkg/crypto/x509/#MarshalPKCS8PrivateKey) for details. |
| toYaml | Takes an interface, marshals it to yaml. It returns a string, even on marshal error (empty string). |
diff --git a/docs/guides/using-esoctl-tool.md b/docs/guides/using-esoctl-tool.md
new file mode 100644
index 00000000000..aed3bac41d6
--- /dev/null
+++ b/docs/guides/using-esoctl-tool.md
@@ -0,0 +1,58 @@
+# Using the esoctl tool
+
+The tool can be found under `cmd/esoctl`. The `template` command can be used to test templates for `PushSecret` and `ExternalSecret`.
+
+To run render simply execute `make build` in the `cmd/esoctl` folder. This will result in a binary under `cmd/esoctl/bin`.
+
+Once the build succeeds, the command can be used as such:
+```console
+./bin/esoctl template --source-templated-object template-test/push-secret.yaml --source-secret-data-file template-test/secret.yaml
+```
+
+Where template-test looks like this:
+
+```
+❯ tree template-test/ (base)
+template-test/
+├── push-secret.yaml
+└── secret.yaml
+
+1 directory, 2 files
+```
+
+`PushSecret` is simply the following:
+
+
+```yaml
+{% include 'esoctl-tool-push-secret-snippet.yaml' %}
+```
+
+And secret data is:
+
+```yaml
+token: dG9rZW4=
+```
+
+Therefor if there is a PushSecret or an ExternalSecret object that the user would like to test the template for,
+simply put it into a file along with the data it's using, and run this command.
+
+The output will be something like this:
+
+```console
+➜ ./bin/esoctl template --source-templated-object template-test/push-secret.yaml --source-secret-data-file template-test/secret.yaml
+data:
+ token: VE9LRU4gd2FzIHRlbXBsYXRlZA==
+metadata:
+ creationTimestamp: null
+
+➜ echo -n "VE9LRU4gd2FzIHRlbXBsYXRlZA==" | base64 -d
+TOKEN was templated⏎
+```
+
+Further options can be used to provide templates from a ConfigMap or a Secret:
+```
+➜ ./bin/esoctl template --source-templated-object template-test/push-secret.yaml \
+ --source-secret-data-file template-test/secret.yaml \
+ --template-from-config-map template-test/template-config-map.yaml \
+ --template-from-secret template-test/template-secret.yaml
+```
diff --git a/docs/index.md b/docs/index.md
index 0f99806cea1..e1809dfd5c9 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -1,3 +1,10 @@
+# [Sponsored by](https://opencollective.com/external-secrets-org)
+
+[![cs-logo](./pictures/cs_logo.png)](https://container-solutions.com)
+[![External Secrets inc.](./pictures/ESI_Logo.svg)](https://externalsecrets.com)
+[![Form3](./pictures/form3_logo.png)](https://www.form3.tech/)
+[![Pento](./pictures/pento_logo.png)](https://pento.io)
+
# Introduction
![high-level](./pictures/diagrams-high-level-simple.png)
@@ -7,7 +14,7 @@ secret management systems like [AWS Secrets
Manager](https://aws.amazon.com/secrets-manager/), [HashiCorp
Vault](https://www.vaultproject.io/), [Google Secrets
Manager](https://cloud.google.com/secret-manager), [Azure Key
-Vault](https://azure.microsoft.com/en-us/services/key-vault/), [IBM Cloud Secrets Manager](https://www.ibm.com/cloud/secrets-manager), [CyberArk Conjur](https://www.conjur.org) and many more. The
+Vault](https://azure.microsoft.com/en-us/services/key-vault/), [IBM Cloud Secrets Manager](https://www.ibm.com/cloud/secrets-manager), [CyberArk Conjur](https://www.conjur.org), [Pulumi ESC](https://www.pulumi.com/product/esc/) and many more. The
operator reads information from external APIs and automatically injects the
values into a [Kubernetes
Secret](https://kubernetes.io/docs/concepts/configuration/secret/).
@@ -50,8 +57,3 @@ How to get involved:
![godaddy-logo](./pictures/godaddy_logo.png)
-### Sponsored by
-
-![cs-logo](./pictures/cs_logo.png)
-![Form3](./pictures/form3_logo.png)
-![Pento](./pictures/pento_logo.png)
diff --git a/docs/introduction/deprecation-policy.md b/docs/introduction/deprecation-policy.md
index 5a1705ea181..10775008c7a 100644
--- a/docs/introduction/deprecation-policy.md
+++ b/docs/introduction/deprecation-policy.md
@@ -27,9 +27,21 @@ We define the following scope that is covered by our deprecation policy. We foll
* Enums and constant values
* Controller Configuration: CLI flags & environment variables
* Metrics as defined in the [Kubernetes docs](https://kubernetes.io/docs/reference/using-api/deprecation-policy/#deprecating-a-metric)
-* a feature or specific behavior:
+* The following features or specific behavior:
* `ExternalSecret` [update mechanics](http://localhost:8000/api-externalsecret/#update-behavior)
### Non-Scope
-We do not provide stability guarantee for **source code imports**. The Interfaces and the behavior will change in a unexpected and backwards-incompatible way. However,
-The maintained helm chart is not part of this deprecation policy.
+Everything not listed in scope is not subject to this deprecation policy and it is subject to breaking changes, updates at any point in time, and deprecation - **as long as it follows the Deprecation Process listed below**.
+
+This includes, but insn't limited to :
+* Any feature / specific behavior not in Scope.
+* Source code imports
+* Helm Charts
+* Release process
+* Docker Images (including multi-arch builds)
+* Image Signature (including provenance, providers, keys)
+* OLM-specific builds
+
+## Depreaction Process:
+
+Deprecation process is described within the [project github repository](https://github.com/external-secrets/external-secrets/blob/main/DEPRECATING.md)
\ No newline at end of file
diff --git a/docs/introduction/getting-started.md b/docs/introduction/getting-started.md
index 44f4f610f45..1ec89c0a045 100644
--- a/docs/introduction/getting-started.md
+++ b/docs/introduction/getting-started.md
@@ -4,6 +4,8 @@ External-secrets runs within your Kubernetes cluster as a deployment resource.
It utilizes CustomResourceDefinitions to configure access to secret providers through SecretStore resources
and manages Kubernetes secret resources with ExternalSecret resources.
+This tutorial is intended for those who already have the PreRequisites complete. If there is a term that you don't comprehend, we suggest you to take a look at the Glossary for a general understanding.
+
> Note: The minimum supported version of Kubernetes is `1.16.0`. Users still running Kubernetes v1.15 or below should upgrade
> to a supported version before installing external-secrets.
@@ -13,7 +15,7 @@ The default install options will automatically install and manage the CRDs as pa
You can install those CRDs outside of `helm` using:
```bash
-kubectl apply -k "https://github.com/external-secrets/external-secrets//config/crds/bases?ref=v0.9.11"
+kubectl apply -k "https://raw.githubusercontent.com/external-secrets/external-secrets//deploy/crds/bundle.yaml"
```
Uncomment the relevant line in the next steps to disable the automatic install of CRDs.
diff --git a/docs/introduction/glossary.md b/docs/introduction/glossary.md
new file mode 100644
index 00000000000..cdaf911da3e
--- /dev/null
+++ b/docs/introduction/glossary.md
@@ -0,0 +1,303 @@
+# Glossary
+This glossary includes technologies related to ESO in alphabetic order.
+
+
+
+ Cluster
+
+ What is it?
+ A group of nodes (computers, VMs) that execute workloads in Kubernetes, i.e., run containerized applications.
+ It's a technique that groups multiple computational resources into a single logical unit. These resources are interconnected and work together to execute tasks, process data, and store information in a way that improves application performance, ensures high availability, reduces costs, and increases scalability, as resources can be shared and distributed efficiently to meet real-time application demands. Each computer is a "node," and there's no limit to the number of nodes that can be interconnected. The structure is : Project (Clusters(Nodes(Pods))).
+ The cluster is what provides the main advantage of Kubernetes: the ability to program and execute containers on a set of physical, virtual, on-premise, or cloud machines. Kubernetes containers are not tied to individual machines. In fact, they are abstracted across the entire cluster.
+
+ What is it for?
+ The cluster's function is to group multiple machines into a single, efficient system, allowing distributed applications to be executed with higher performance and scalability. In Kubernetes, it facilitates container management, reducing complexity, ensuring high availability, and reducing costs. A Kubernetes cluster typically has a master node that manages pods and the system's execution environment.
+
+ Useful links:
+
+
+
+
+
+
+ Docker
+
+ What is it?
+ Docker is an open platform for developing, shipping, and running containerized applications. It allows you to separate your applications from the infrastructure, facilitating the delivery of software quickly and efficiently, enabling the creation, sharing, and execution of containerized applications and microservices.
+
+ What is it for?
+ It enables infrastructure management. This significantly reduces the time between writing code and executing it in production.
+ It simplifies complex processes such as port mapping, file system concerns, and other standard configurations, allowing you to focus on writing code.
+ With Docker, you can develop an application and its supporting components using containers. In this context, the container becomes the unit for distributing and testing the application. Once ready, you can deploy the application to the production environment, whether it's local, cloud-based, or hybrid.
+
+ Useful links:
+
+ Official documentation for Docker
+
+
+
+
+ Golang
+
+ What is it?
+ An open-source programming language created by Google, known for its simplicity, performance, clarity, and conciseness.
+
+ What is it for?
+
+ Used in the development of applications, backend systems, and tools, especially in cloud and Kubernetes environments.
+ It's a language that offers concurrency mechanisms that facilitate writing programs capable of taking full advantage of multi-core machines and networks, while its innovative type system enables the construction of flexible and modular programs.
+ Go compiles quickly to machine code and, at the same time, offers convenience with garbage collection and the power of runtime reflection. It's a compiled, statically typed language that has the agility of dynamically typed and interpreted languages.
+
+
+ Useful links:
+
+
+
+
+ Helm
+
+ What is it?
+ A package manager for Kubernetes that facilitates the deployment and management of applications using templates called "charts."
+
+ What is it for?
+
+ Simplifies the configuration, installation, and update of applications in Kubernetes.
+
+
+ Useful links:
+
+
+
+
+
+
+ HPA
+
+ What is it?
+ Horizontal Pod Autoscaler (HPA)
+
+ What is it for?
+
+ It's used to control the number of Pods in a Deployment. For example, if CPU usage is too high, the HPA would increase the number of Pods.
+ It's also possible to use the Vertical Pod Autoscaler (VPA), which would increase the amount of resources for each Pod instead of increasing the number of Pods.
+
+
+
+
+ Ingress
+
+ What is it?
+
+ In a Kubernetes cluster where all requests arrive at the same IP and port, Ingresses are responsible for directing (based on rules you define via the Kubernetes API) these requests to the appropriate Services. It can also be used for other purposes.
+
+
+ What is it for?
+
+ It provides a single entry point for routing traffic to internal services.
+
+
+ Useful links:
+
+
+
+
+ Issuer
+
+ What is it?
+ A component in tools like Cert-Manager for issuing certificates.
+
+ What is it for?
+
+ Manages the issuance of automatic TLS certificates for services in Kubernetes.
+ It issues the SSL certificate for Ingresses to encrypt (with HTTPS) incoming and outgoing requests, for example.
+
+
+ Useful links:
+
+
+
+
+ Kind
+
+ What is it?
+
+ Kind means "Kubernetes in Docker", so it is a tool for running local Kubernetes clusters using Docker containers as cluster "nodes."
+
+
+ What is it for?
+
+ Kind was initially designed for testing Kubernetes itself, but it can also be used for local development or continuous integration (CI).
+ It enables the creation of Kubernetes clusters easily in local environments, facilitating testing and development without requiring complex infrastructure.
+
+
+ Useful links:
+
+
+
+
+ Kubectl
+
+ What is it?
+
+ Kubectl is a command-line tool for communicating with the control plane of a Kubernetes cluster, using the Kubernetes API.
+
+
+ What is it for?
+
+ It performs operations in Kubernetes, such as creating pods and monitoring the cluster status.
+ It allows you to interact with the Kubernetes cluster by performing operations like creating, managing, and viewing resources.
+ It searches for a configuration file called config
in the $HOME/.kube
directory, which contains information about how to connect to the cluster.
+
+
+ Useful links:
+
+
+
+
+ Kubernetes
+
+ What is it?
+
+ A container orchestration open source platform that automates the deployment, scaling, and management of applications.
+
+
+ What is it for?
+
+ Ensures high availability, scalability, and monitoring of containerized applications.
+
+
+ Useful links:
+
+
+
+
+
+ Nginx
+
+ What is it?
+
+ It is an open-source HTTP web server that can also function as a reverse proxy, load balancer, content cache, TCP/UDP proxy server, and email proxy server.
+ It is widely used due to its high performance and ability to handle large volumes of traffic.
+
+
+ What is it for?
+
+ Nginx is used to serve web content, manage network traffic, and balance load between servers, as well as act as a reverse proxy and content cache.
+ It can be used to improve the scalability and performance of web applications by efficiently distributing requests across multiple servers.
+ It has a main process that manages the configuration and several worker processes that handle request processing. The number of worker processes can be adjusted according to the number of processor cores.
+
+
+ Useful Links:
+
+
+
+
+ Lint
+
+ What is it?
+
+ A static code analysis process for identifying errors, style issues, and non-compliance with best coding practices.
+
+
+ What is it for?
+
+ Ensures code quality, consistency, and adherence to predefined standards by identifying syntax errors, formatting issues, and poor development practices before code execution.
+ It contributes to maintaining clean, readable, and efficient code.
+
+
+ Useful Links:
+
+
+
+
+ Pod
+
+ What is it?
+
+ The smallest unit of computation in Kubernetes, which groups one or more containers.
+
+
+ What is it for?
+
+ Manages containers that share resources and act as a single entity in a cluster.
+ The structure is: Project (Clusters(Nodes(Pods))).
+
+
+ Useful Links:
+
+
+
+
+ Secret
+
+ What is it?
+
+ Sensitive data we want to store, manage, and use with ESO.
+
+
+
+
+ Tilt
+
+ What is it?
+
+ A tool that helps with local development for Kubernetes, enabling quick visualization and management of changes to applications.
+
+
+ What is it for?
+
+ Facilitates the development workflow in Kubernetes by automatically updating the cluster's state based on code changes.
+ It has an interface and automates many tasks that would otherwise need to be done manually.
+
+
+ Useful Links:
+
+
+
+
+ yq
+
+ What is it?
+
+ A tool used to manipulate YAML files in the command line, similar to jq for JSON.
+
+
+ What is it for?
+
+ Edits, transforms, and queries YAML files. YAML files are used to configure applications, services, or clusters.
+
+
+ Useful Links:
+
+
diff --git a/docs/introduction/prerequisites.md b/docs/introduction/prerequisites.md
new file mode 100644
index 00000000000..2e63c976e2a
--- /dev/null
+++ b/docs/introduction/prerequisites.md
@@ -0,0 +1,239 @@
+# Prerequisites
+To collaborate on the External Secrets Operator (ESO) project, you need to install some tools on your computer. This guide explains what each tool is, why it is needed, the recommended version, and how to install it on the corresponding operating system.
+### Supported Operating Systems
+To collaborate on the External Secrets Operator (ESO) project, it is recommended to use Unix-based operating systems, such as Linux and macOS. ESO's development environment is primarily designed for these systems, and many of the tools and scripts used during development are built to work on them.
+### Can You Develop on Windows?
+It is possible to use Windows for development, but there are important considerations to keep in mind. Since ESO's development environment is not optimized for Windows, compatibility issues may arise with tools like Make, Tilt, and shell scripts. The project's automation scripts and commands are written for Unix environments, using bash scripting, which might not be compatible with Windows without adaptations. This tutorial will not cover the installation and configuration of tools on Windows due to its complexity and lack of testing.
+
+---
+
+## Install Go (Golang)
+
+
+ About Golang
+ What is Go?
+ Go, also known as Golang, is a programming language design at Google by Robert Griesemer, Rob Pike, and Ken Thompson. It is known for being efficient, easy to learn, and excellent for developing fast and scalable applications.
+ Why is Go needed?
+ In the External Secrets Operator project, Go is used to develop core parts of the code. It is required to compile, run, and contribute to the project's source code.
+
+
+
+ Golang Installation
+ Required Version
+ Minimum version: Go 1.20 or higher.
+ Recommended version: Go 1.23.3
+ As of this writing, the latest version of Go is 1.23.3 As of this writing, the latest version of Go is 1.23.3 , which worked perfectly with the External Secrets Operator project. Previous versions failed to test the application. Before testing the project, check your Go version.
+
+ How to Install Go
+
+Please consult the official documentation .
+
+
+## Install Helm
+
+
+ About Helm
+ What is Helm?
+ Helm is a package manager for Kubernetes, the platform that automates deployment, scaling, and management of containerized applications.
+
+ Why is Helm necessary?
+ In the External Secrets Operator project, Helm is used to simplify the installation and management of applications within Kubernetes, automating complex configuration and deployment processes.
+
+
+
+ Installing Helm
+ Required Version
+ Recommended version: Helm 3 (latest version of Helm 3).
+ How to Install Helm
+
+
+ Please consult the official Helm installation guide .
+
+
+---
+
+## Install yq
+
+
+ About yq
+ What is yq?
+ yq is a command-line tool for reading, manipulating, and writing YAML files, which are widely used for configurations.
+
+ Why is yq necessary?
+ In the External Secrets Operator project, yq is used to automate the editing of YAML configuration files, facilitating adjustments and implementations.
+
+
+
+ Installing yq
+ Required Version
+ Recommended version: yq v4.44.3 or higher.
+
+ How to Install yq
+
+ Please consult the official yq repository .
+
+
+
+---
+
+## Install jq
+
+
+ About jq
+ What is jq?
+ jq is a command-line tool for processing and manipulating JSON data.
+
+ Why is jq needed?
+ In the External Secrets Operator project, jq is essential for working with JSON data, enabling efficient filtering and transformation of information.
+
+
+
+ Installing jq
+ Required Version
+ Recommended version: jq 1.6 or later.
+
+ How to Install jq
+
+ Please consult the official jq website .
+
+
+## Kubernetes
+
+
+ About Kubernetes
+ What is Kubernetes?
+ Kubernetes is an open-source platform for automating the deployment, scaling, and management of containerized applications. It orchestrates containerized workloads across a cluster of machines, ensuring high availability and efficient resource utilization.
+
+ Why is Kubernetes needed?
+ In the External Secrets Operator project, Kubernetes provides the infrastructure to deploy and manage containerized applications. It allows integration with cloud-native services, enabling scalability, fault tolerance, and streamlined operations in dynamic environments.
+
+
+ To work with Kubernetes, we need to install and configure some tools first. This will be explained in the sections below.
+
+ For more details, check the [official documentation](https://kubernetes.io/docs/home/).
+
+
+
+---
+## Install Docker
+
+
+ About Docker
+ What is Docker?
+ Docker is a platform for building, deploying, and running applications in containers. Containers package an application with all its dependencies into a standard unit for development and deployment.
+
+ Why is Docker needed?
+ In the External Secrets Operator project, Docker is used to create container images and run services in isolated environments. It is essential for developing, testing, and deploying the application within a Kubernetes environment.
+
+
+
+ Installing Docker
+ How to Install Docker
+
+ Please consult the official Docker documentation .
+
+
+
+ Required Docker Configuration
+ Configure Docker for Non-Root Usage
+ By default, Docker requires superuser (root) privileges to run. To simplify usage, it is recommended to add the current user to the docker
group to execute commands without sudo
.
+
+
+ Steps to configure Docker without root on Linux
+ 1. Create the docker group (if it doesn't exist):
+ sudo groupadd docker
+ 2. Add the current user to the docker group:
+ sudo usermod -aG docker $USER
+ 3. Apply group changes without logging out:
+ newgrp docker
+ 4. Verify Docker can run without sudo:
+ docker run hello-world
+ If the command works without errors, the configuration is successful.
+
+
+
+---
+## Install kubectl
+
+
+ About kubectl
+ What is kubectl?
+ kubectl is the command-line tool for managing Kubernetes clusters. It enables running commands on the cluster, managing resources, and debugging applications.
+ Why is kubectl needed?
+ In the External Secrets Operator project, kubectl is used to interact with local or remote Kubernetes clusters, apply configurations, and check the state of deployed resources.
+
+
+
+ Installing kubectl
+ Required Version
+ A version compatible with the installed Kubernetes version (usually the latest stable version).
+ How to Install kubectl
+
+ Please consult the official kubectl documentation .
+
+
+---
+## Install ctlptl and Create a Kind Cluster with Local Registry
+
+
+ About ctlptl
+ What is ctlptl?
+ ctlptl (Control Plane Tool) is a tool for managing local Kubernetes development clusters. It simplifies the creation and management of clusters like Kind (Kubernetes in Docker) and the configuration of local container registries.
+ Why is ctlptl necessary?
+ In the External Secrets Operator project, ctlptl is used to create and manage a local Kubernetes cluster using Kind, as well as to configure a local container registry to store Docker images during development.
+
+
+
+ Installing ctlptl
+ Required Version
+ The latest available version of ctlptl.
+
+ How to Install ctlptl
+ Please consult the official ctlptl installation guide .
+
+
+
+Create a Kind Cluster with Local Registry
+
+
+ About Kind
+ Kind (Kubernetes in Docker) is a tool to run local Kubernetes clusters using Docker containers as cluster nodes.
+
+
+
+ How to Create a Kind Cluster with Local Registry
+ 1. Create a local container registry:
+ docker run -d --restart=always -p "5000:5000" --name kind-registry registry:2
+ 2. Create a Kind cluster using ctlptl and connect it to the local registry:
+ ctlptl create cluster kind --registry=kind-registry
+ This will create a Kind cluster configured to use the local registry at localhost:5000
.
+ 3. Verify the cluster is running:
+ kubectl cluster-info --context kind-kind
+ 4. List clusters managed by ctlptl:
+ ctlptl get clusters
+
+
+---
+## Install Tilt
+
+
+ About Tilt
+ What is Tilt?
+ Tilt is a tool that accelerates development in Kubernetes environments. It automates building, deploying, and monitoring code, enabling a faster development cycle.
+ Why is Tilt necessary?
+ In the External Secrets Operator project, Tilt is used to develop and test code changes efficiently, reflecting updates almost instantly in the local Kubernetes environment.
+
+
+
+ Installing Tilt
+ Required Version
+
+ Prerequisites: Install Docker, kubectl, Kind, and ctlptl.
+ Recommended version: Latest available version.
+
+
+ How to Install Tilt
+
+ Please consult the official Tilt installation guide .
+
\ No newline at end of file
diff --git a/docs/introduction/stability-support.md b/docs/introduction/stability-support.md
index cabcd87100d..3825fc05c0d 100644
--- a/docs/introduction/stability-support.md
+++ b/docs/introduction/stability-support.md
@@ -17,22 +17,25 @@ We want to cover the following cases:
- regular go dependency updates
- backport bug fixes on demand
-| ESO Version | Kubernetes Version | Release Date | End of Life |
-| ----------- | ------------------ | ------------ | -------------- |
-| 0.9.x | 1.19 → 1.29 | Jun 22, 2023 | Release of 1.1 |
-| 0.8.x | 1.19 → 1.28 | Mar 16, 2023 | Release of 1.0 |
-| 0.7.x | 1.19 → 1.26 | Dec 11, 2022 | Jun 22, 2023 |
-| 0.6.x | 1.19 → 1.24 | Oct 9, 2022 | Mar 16, 2023 |
-| 0.5.x | 1.19 → 1.24 | Apr 6, 2022 | Dec 11, 2022 |
-| 0.4.x | 1.16 → 1.24 | Feb 2, 2022 | Oct 9, 2022 |
-| 0.3.x | 1.16 → 1.24 | Jul 25, 2021 | Apr 6, 2022 |
+| ESO Version | Kubernetes Version | Release Date | End of Life |
+| ----------- | ------------------ | ------------ | --------------- |
+| 0.12.x | 1.19 → 1.31 | Dec 24, 2024 | Release of 0.14 |
+| 0.11.x | 1.19 → 1.31 | Dec 2, 2024 | Release of 0.13 |
+| 0.10.x | 1.19 → 1.31 | Aug 3, 2024 | Dec 24, 2024 |
+| 0.9.x | 1.19 → 1.30 | Jun 22, 2023 | Dec 2, 2024 |
+| 0.8.x | 1.19 → 1.28 | Mar 16, 2023 | Aug 3, 2024 |
+| 0.7.x | 1.19 → 1.26 | Dec 11, 2022 | Jun 22, 2023 |
+| 0.6.x | 1.19 → 1.24 | Oct 9, 2022 | Mar 16, 2023 |
+| 0.5.x | 1.19 → 1.24 | Apr 6, 2022 | Dec 11, 2022 |
+| 0.4.x | 1.16 → 1.24 | Feb 2, 2022 | Oct 9, 2022 |
+| 0.3.x | 1.16 → 1.24 | Jul 25, 2021 | Apr 6, 2022 |
## Provider Stability and Support Level
The following table describes the stability level of each provider and who's responsible.
| Provider | Stability | Maintainer |
-|------------------------------------------------------------------------------------------------------------| :-------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
+| ---------------------------------------------------------------------------------------------------------- | :-------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| [AWS Secrets Manager](https://external-secrets.io/latest/provider/aws-secrets-manager/) | stable | [external-secrets](https://github.com/external-secrets) |
| [AWS Parameter Store](https://external-secrets.io/latest/provider/aws-parameter-store/) | stable | [external-secrets](https://github.com/external-secrets) |
| [Hashicorp Vault](https://external-secrets.io/latest/provider/hashicorp-vault/) | stable | [external-secrets](https://github.com/external-secrets) |
@@ -44,17 +47,23 @@ The following table describes the stability level of each provider and who's res
| [GitLab Variables](https://external-secrets.io/latest/provider/gitlab-variables/) | alpha | [@Jabray5](https://github.com/Jabray5) |
| Alibaba Cloud KMS | alpha | [@ElsaChelala](https://github.com/ElsaChelala) |
| [Oracle Vault](https://external-secrets.io/latest/provider/oracle-vault) | alpha | [@KianTigger](https://github.com/KianTigger) [@EladGabay](https://github.com/EladGabay) |
-| [Akeyless](https://external-secrets.io/latest/provider/akeyless) | alpha | [@renanaAkeyless](https://github.com/renanaAkeyless) |
+| [Akeyless](https://external-secrets.io/latest/provider/akeyless) | stable | [external-secrets](https://github.com/external-secrets) |
| [1Password](https://external-secrets.io/latest/provider/1password-automation) | alpha | [@SimSpaceCorp](https://github.com/Simspace) [@snarlysodboxer](https://github.com/snarlysodboxer) |
| [Generic Webhook](https://external-secrets.io/latest/provider/webhook) | alpha | [@willemm](https://github.com/willemm) |
| [senhasegura DevOps Secrets Management (DSM)](https://external-secrets.io/latest/provider/senhasegura-dsm) | alpha | [@lfraga](https://github.com/lfraga) |
| [Doppler SecretOps Platform](https://external-secrets.io/latest/provider/doppler) | alpha | [@ryan-blunden](https://github.com/ryan-blunden/) [@nmanoogian](https://github.com/nmanoogian/) |
| [Keeper Security](https://www.keepersecurity.com/) | alpha | [@ppodevlab](https://github.com/ppodevlab) |
| [Scaleway](https://external-secrets.io/latest/provider/scaleway) | alpha | [@azert9](https://github.com/azert9/) |
-| [Conjur](https://external-secrets.io/latest/provider/conjur) | stable | [@davidh-cyberark](https://github.com/davidh-cyberark/) [@szh](https://github.com/szh) |
+| [Conjur](https://external-secrets.io/latest/provider/conjur) | stable | [@davidh-cyberark](https://github.com/davidh-cyberark/) [@szh](https://github.com/szh) |
| [Delinea](https://external-secrets.io/latest/provider/delinea) | alpha | [@michaelsauter](https://github.com/michaelsauter/) |
-| [Pulumi ESC](https://external-secrets.io/latest/provider/pulumi) | alpha | [@dirien](https://github.com/dirien) |
-| [Passbolt](https://external-secrets.io/latest/provider/passbolt) | alpha | |
+| [Beyondtrust](https://external-secrets.io/latest/provider/beyondtrust) | alpha | [@btfhernandez](https://github.com/btfhernandez/) |
+| [SecretServer](https://external-secrets.io/latest/provider/secretserver) | alpha | [@billhamilton](https://github.com/pacificcode/) |
+| [Pulumi ESC](https://external-secrets.io/latest/provider/pulumi) | alpha | [@dirien](https://github.com/dirien) |
+| [Passbolt](https://external-secrets.io/latest/provider/passbolt) | alpha | |
+| [Infisical](https://external-secrets.io/latest/provider/infisical) | alpha | [@akhilmhdh](https://github.com/akhilmhdh) |
+| [Device42](https://external-secrets.io/latest/provider/device42) | alpha | |
+| [Bitwarden Secrets Manager](https://external-secrets.io/latest/provider/bitwarden-secrets-manager) | alpha | [@skarlso](https://github.com/Skarlso) |
+| [Previder](https://external-secrets.io/latest/provider/previder) | stable | [@previder](https://github.com/previder) |
## Provider Feature Support
@@ -73,7 +82,7 @@ The following table show the support for features across different providers.
| GitLab Variables | x | x | | | x | | |
| Alibaba Cloud KMS | | | | | x | | |
| Oracle Vault | | | | | x | | |
-| Akeyless | x | x | | | x | | |
+| Akeyless | x | x | | x | x | x | x |
| 1Password | x | | | | x | x | x |
| Generic Webhook | | | | | | | x |
| senhasegura DSM | | | | | x | | |
@@ -82,8 +91,14 @@ The following table show the support for features across different providers.
| Scaleway | x | x | | | x | x | x |
| Conjur | x | x | | | x | | |
| Delinea | x | | | | x | | |
+| Beyondtrust | x | | | | x | | |
+| SecretServer | x | | | | x | | |
| Pulumi ESC | x | | | | x | | |
| Passbolt | x | | | | x | | |
+| Infisical | x | | | x | x | | |
+| Device42 | | | | | x | | |
+| Bitwarden Secrets Manager | x | | | | x | x | x |
+| Previder | x | | | | x | | |
## Support Policy
diff --git a/docs/pictures/ESI_Logo.svg b/docs/pictures/ESI_Logo.svg
new file mode 100755
index 00000000000..94886090400
--- /dev/null
+++ b/docs/pictures/ESI_Logo.svg
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/docs/pictures/external-secrets-operator.png b/docs/pictures/external-secrets-operator.png
new file mode 100644
index 00000000000..5264eeea380
Binary files /dev/null and b/docs/pictures/external-secrets-operator.png differ
diff --git a/docs/pictures/previder-provider.png b/docs/pictures/previder-provider.png
new file mode 100644
index 00000000000..55d867d63cd
Binary files /dev/null and b/docs/pictures/previder-provider.png differ
diff --git a/docs/pictures/pulumi-esc.png b/docs/pictures/pulumi-esc.png
new file mode 100644
index 00000000000..e5d3fe89ca6
Binary files /dev/null and b/docs/pictures/pulumi-esc.png differ
diff --git a/docs/provider/1password-automation.md b/docs/provider/1password-automation.md
index 4da28886bdf..833f47539ca 100644
--- a/docs/provider/1password-automation.md
+++ b/docs/provider/1password-automation.md
@@ -3,9 +3,11 @@
External Secrets Operator integrates with [1Password Secrets Automation](https://1password.com/products/secrets/) for secret management.
### Important note about this documentation
+
_**The 1Password API calls the entries in vaults 'Items'. These docs use the same term.**_
### Behavior
+
* How an Item is equated to an ExternalSecret:
* `remoteRef.key` is equated to an Item's Title
* `remoteRef.property` is equated to:
@@ -28,6 +30,7 @@ _**The 1Password API calls the entries in vaults 'Items'. These docs use the sam
* `find.tags` are not supported at this time.
### Prerequisites
+
* 1Password requires running a 1Password Connect Server to which the API requests will be made.
* External Secrets does not run this server. See [Deploy a Connect Server](#deploy-a-connect-server).
* One Connect Server is needed per 1Password Automation Environment.
@@ -35,6 +38,7 @@ _**The 1Password API calls the entries in vaults 'Items'. These docs use the sam
* 1Password Connect Server version 1.5.6 or higher.
### Setup Authentication
+
_Authentication requires a `1password-credentials.json` file provided to the Connect Server, and a related 'Access Token' for the client in this provider to authenticate to that Connect Server. Both of these are generated by 1Password._
1. Setup an Automation Environment [at 1Password.com](https://support.1password.com/secrets-automation/), or [via the op CLI](https://github.com/1Password/connect/blob/a0a5f3d92e68497098d9314721335a7bb68a3b2d/README.md#create-server-and-access-token).
@@ -58,6 +62,7 @@ _Authentication requires a `1password-credentials.json` file provided to the Con
```
### Deploy a Connect Server
+
* Follow the remaining instructions in the [Quick Start guide](https://github.com/1Password/connect/blob/a0a5f3d92e68497098d9314721335a7bb68a3b2d/README.md#quick-start).
* Deploy at minimum a Deployment and Service for a Connect Server, to go along with the Secret for the Server created in the [Setup Authentication section](#setup-authentication).
* The Service's name will be referenced in SecretStores/ClusterSecretStores.
@@ -65,15 +70,20 @@ _Authentication requires a `1password-credentials.json` file provided to the Con
* Unencrypted secret values are passed over the connection between the Operator and the Connect Server. **Encrypting the connection is recommended.**
### Creating Compatible 1Password Items
+
_Also see [examples below](#examples) for matching SecretStore and ExternalSecret specs._
+
#### Manually (Password type)
+
1. Click the plus button to create a new Password type Item.
1. Change the title to what you want `remoteRef.key` to be.
1. Set what you want `remoteRef.property` to be in the field sections where is says 'label', and values where it says 'new field'.
1. Click the 'Save' button.
![create-password-screenshot](../pictures/screenshot_1password_create_password.png)
+
#### Manually (Document type)
+
* Click the plus button to create a new Document type Item.
* Choose the file to upload and upload it.
* Change the title to match `remoteRef.key`
@@ -81,7 +91,9 @@ _Also see [examples below](#examples) for matching SecretStore and ExternalSecre
* Click the 'Save' button.
![create-document-screenshot](../pictures/screenshot_1password_create_document.png)
+
#### Scripting (Password type with op [CLI](https://developer.1password.com/docs/cli/v1/get-started/))
+
* Create `file.json` with the following contents, swapping in your keys and values. Note: `section.name`'s and `section.title`'s values are ignored by the Operator, but cannot be empty for the `op` CLI
```json
{
@@ -126,10 +138,13 @@ _Also see [examples below](#examples) for matching SecretStore and ExternalSecre
}
```
* Run `op item create --template file.json`
+
#### Scripting (Document type)
+
* Unfortunately the `op` CLI doesn't seem to support uploading multiple files to the same Item, and the current Go lib has a [bug](https://github.com/1Password/connect-sdk-go/issues/45). `op` can be used to create a Document type Item with one file in it, but for now it's necessary to add multiple files to the same Document via the GUI.
#### In-built field labeled `password` on Password type Items
+
* TL;DR if you need a field labeled `password`, use the in-built one rather than the one in a fields Section.
![password-field-example](../pictures/screenshot_1password_password_field.png)
@@ -139,6 +154,7 @@ _Also see [examples below](#examples) for matching SecretStore and ExternalSecre
* The in-built `password` field is not otherwise special for the purposes of ExternalSecrets. It can be ignored when not in use.
### Examples
+
Examples of using the `my-env-config` and `my-cert` Items [seen above](#manually-password-type).
* Note: with this configuration a 1Password Item titled `my-env-config` is correlated to a ExternalSecret named `my-env-config` that results in a Kubernetes secret named `my-env-config`, all with matching names for the key/value pairs. This is a way to increase comprehensibility.
@@ -153,10 +169,13 @@ Examples of using the `my-env-config` and `my-cert` Items [seen above](#manually
```
### Additional Notes
+
#### General
+
* It's intuitive to use Document type Items for Kubernetes secrets mounted as files, and Password type Items for ones that will be mounted as environment variables, but either can be used for either. It comes down to what's more convenient.
#### Why no version history
+
* 1Password only supports version history on their in-built `password` field. Therefore, implementing version history in this provider would require one Item in 1Password per `remoteRef` in an ExternalSecret. Additionally `remoteRef.property` would be pointless/unusable.
* For example, a Kubernetes secret with 15 keys (say, used in `envFrom`,) would require 15 Items in the 1Password vault, instead of 15 Fields in 1 Item. This would quickly get untenable for more than a few secrets, because:
* All Items would have to have unique names which means `secretKey` couldn't match the Item name the `remoteRef` is targeting.
@@ -165,11 +184,13 @@ Examples of using the `my-env-config` and `my-cert` Items [seen above](#manually
* To support new and old versions of a secret value at the same time, create a new Item in 1Password with the new value, and point some ExternalSecrets at a time to the new Item.
#### Keeping misconfiguration from working
+
* One instance of the ExternalSecrets Operator _can_ work with many Connect Server instances, but it may not be the best approach.
* With one Operator instance per Connect Server instance, namespaces and RBAC can be used to improve security posture, and perhaps just as importantly, it's harder to misconfigure something and have it work (supply env A's secret values to env B for example.)
* You can run as many 1Password Connect Servers as you need security boundaries to help protect against accidental misconfiguration.
#### Patching ExternalSecrets with Kustomize
+
* An overlay can provide a SecretStore specific to that overlay, and then use JSON6902 to patch all the ExternalSecrets coming from base to point to that SecretStore. Here's an example `overlays/staging/kustomization.yaml`:
```yaml
---
@@ -189,3 +210,15 @@ Examples of using the `my-env-config` and `my-cert` Items [seen above](#manually
path: /spec/secretStoreRef/name
value: staging
```
+
+### Push Secret
+
+To push a secret from Kubernetes cluster and create it as a secret in 1Password, a `Kind=PushSecret` resource is needed.
+
+Updating the vault on an existing PushSecret is currently not supported. To update the vault, create a new PushSecret with the updated vault.
+
+```yaml
+{% include '1password-push-secret.yaml' %}
+```
+
+Then it will create an item in onepassword `op://staging/1pw-secret-name/password` equal to `my-secret`.
diff --git a/docs/provider/akeyless.md b/docs/provider/akeyless.md
index 2dfe9ef3de3..b10616df1fb 100644
--- a/docs/provider/akeyless.md
+++ b/docs/provider/akeyless.md
@@ -1,7 +1,9 @@
## Akeyless Secrets Management Platform
External Secrets Operator integrates with the [Akeyless Secrets Management Platform](https://www.akeyless.io/).
-### Create Secret Store:
+
+### Create Secret Store
+
SecretStore resource specifies how to access Akeyless. This resource is namespaced.
**NOTE:** Make sure the Akeyless provider is listed in the Kind=SecretStore.
@@ -9,7 +11,7 @@ If you use a customer fragment, define the value of akeylessGWApiURL as the URL
Akeyelss provide several Authentication Methods:
-### Authentication with Kubernetes:
+### Authentication with Kubernetes
Options for obtaining Kubernetes credentials include:
@@ -18,11 +20,12 @@ Options for obtaining Kubernetes credentials include:
3. Using transient credentials from the mounted service account token within the external-secrets operator
#### Create the Akeyless Secret Store Provider with Kubernetes Auth-Method
+
```yaml
{% include 'akeyless-secret-store-k8s-auth.yaml' %}
```
-**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` for `serviceAccountRef` and `secretRef` according to the namespaces where the secrets reside.
+**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` for `serviceAccountRef` and `secretRef` according to the namespaces where the secrets reside.
### Authentication With Cloud-Identity or Api-Access-Key
@@ -38,6 +41,7 @@ The supported auth-methods and their parameters are:
| `azure_ad` | azure object id (optional) |
| `api_key` | The access key. |
| `k8s` | The k8s configuration name |
+
For more information see [Akeyless Authentication Methods](https://docs.akeyless.io/docs/access-and-authentication-methods)
#### Creating an Akeyless Credentials Secret
@@ -61,9 +65,11 @@ stringData:
```yaml
{% include 'akeyless-secret-store.yaml' %}
```
+
**NOTE:** In case of a `ClusterSecretStore`, be sure to provide `namespace` for `accessID`, `accessType` and `accessTypeParam` according to the namespaces where the secrets reside.
#### Create the Akeyless Secret Store With CAs for TLS handshake
+
```yaml
....
spec:
@@ -103,11 +109,27 @@ DataFrom can be used to get a secret as a JSON string and attempt to parse it.
```
### Getting the Kubernetes Secret
+
The operator will fetch the secret and inject it as a `Kind=Secret`.
-```
+
+```bash
kubectl get secret database-credentials -o jsonpath='{.data.db-password}' | base64 -d
```
-```
+```bash
kubectl get secret database-credentials-json -o jsonpath='{.data}'
```
+
+### Pushing a secret
+
+To push a secret from Kubernetes cluster and create it as a secret to Akeyless, a `Kind=PushSecret` resource is needed.
+
+{% include 'akeyless-push-secret.yaml' %}
+
+Then when you create a matching secret as follows:
+
+```bash
+kubectl create secret generic --from-literal=cache-pass=mypassword k8s-created-secret
+```
+
+Then it will create a secret in akeyless `eso-created/my-secret` with value `{"cache-pass":"mypassword"}`
diff --git a/docs/provider/aws-parameter-store.md b/docs/provider/aws-parameter-store.md
index cf7be0b0bcf..690a65246d1 100644
--- a/docs/provider/aws-parameter-store.md
+++ b/docs/provider/aws-parameter-store.md
@@ -21,6 +21,8 @@ way users of the `SecretStore` can only access the secrets necessary.
### IAM Policy
+#### Fetching Parameters
+
The example policy below shows the minimum required permissions for fetching SSM parameters. This policy permits pinning down access to secrets with a path matching `dev-*`. Other operations may require additional permission. For example, finding parameters based on tags will also require `ssm:DescribeParameters` and `tag:GetResources` permission with `"Resource": "*"`. Generally, the specific permission required will be logged as an error if an operation fails.
For further information see [AWS Documentation](https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-paramstore-access.html).
@@ -40,11 +42,29 @@ For further information see [AWS Documentation](https://docs.aws.amazon.com/syst
}
```
+#### Pushing Parameters
+
+The example policy below shows the minimum required permissions for pushing SSM parameters. Like with the fetching policy it restricts the path in which it can push secrets too.
+
+``` json
+{
+ "Action": [
+ "ssm:GetParameter*",
+ "ssm:PutParameter*",
+ "ssm:AddTagsToResource",
+ "ssm:ListTagsForResource"
+ ],
+ "Effect": "Allow",
+ "Resource": "arn:aws:ssm:us-east-2:1234567889911:parameter/dev-*"
+}
+```
+
### JSON Secret Values
You can store JSON objects in a parameter. You can access nested values or arrays using [gjson syntax](https://github.com/tidwall/gjson/blob/master/SYNTAX.md):
Consider the following JSON object that is stored in the Parameter Store key `friendslist`:
+
``` json
{
"name": {"first": "Tom", "last": "Anderson"},
@@ -57,6 +77,7 @@ Consider the following JSON object that is stored in the Parameter Store key `fr
```
This is an example on how you would look up nested keys in the above json object:
+
``` yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
@@ -87,6 +108,7 @@ spec:
key: database-credentials
property: dev
```
+
### Parameter Versions
ParameterStore creates a new version of a parameter every time it is updated with a new value. The parameter can be referenced via the `version` property
@@ -101,9 +123,28 @@ The SetSecret method for the Parameter Store allows the user to set the value st
{% include "full-pushsecret.yaml" %}
```
+#### Additional Metadata for PushSecret
+
+Optionally, it is possible to configure additional options for the parameter. These are as follows:
+- type
+- keyID
+- tier & policies
+
+To control this behaviour you can set the following provider's `metadata`:
+
+```yaml
+{% include 'aws-pm-push-secret-with-metadata.yaml' %}
+```
+
+- `secretType` takes three options. `String`, `StringList`, and `SecureString`, where `String` is the _default_
+- `kmsKeyID` takes a KMS Key `$ID` or `$ARN` (in case a key source is created in another account) as a string, where `alias/aws/ssm` is the _default_. This property is only used if `secretType` is set as `SecureString`.
+-
+
+
+
#### Check successful secret sync
-To be able to check that the secret has been succesfully synced you can run the following command:
+To be able to check that the secret has been successfully synced you can run the following command:
```bash
kubectl get pushsecret pushsecret-example
diff --git a/docs/provider/aws-secrets-manager.md b/docs/provider/aws-secrets-manager.md
index 97b4aa563db..4a6a713e6ed 100644
--- a/docs/provider/aws-secrets-manager.md
+++ b/docs/provider/aws-secrets-manager.md
@@ -12,14 +12,29 @@ way users of the `SecretStore` can only access the secrets necessary.
{% include 'aws-sm-store.yaml' %}
```
**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `accessKeyIDSecretRef` and `secretAccessKeySecretRef` with the namespaces where the secrets reside.
+
+**NOTE:** When using `dataFrom` without a `path` defined, the provider will fall back to using `ListSecrets`. `ListSecrets`
+then proceeds to fetch each individual secret in turn. To use `BatchGetSecretValue` and avoid excessive API calls define
+a `path` prefix or use `Tags` filter.
+
### IAM Policy
Create a IAM Policy to pin down access to secrets matching `dev-*`.
+For Batch permissions read the following post https://aws.amazon.com/about-aws/whats-new/2023/11/aws-secrets-manager-batch-retrieval-secrets/.
+
``` json
{
"Version": "2012-10-17",
"Statement": [
+ {
+ "Action" : [
+ "secretsmanager:ListSecrets",
+ "secretsmanager:BatchGetSecretValue"
+ ],
+ "Effect" : "Allow",
+ "Resource" : "*"
+ },
{
"Effect": "Allow",
"Action": [
diff --git a/docs/provider/azure-key-vault.md b/docs/provider/azure-key-vault.md
index c7ba7f9ef46..5723f05839b 100644
--- a/docs/provider/azure-key-vault.md
+++ b/docs/provider/azure-key-vault.md
@@ -34,7 +34,7 @@ az keyvault set-policy --name kv-name-with-certs --object-id "$KUBELET_IDENTITY_
#### Service Principal key authentication
-A service Principal client and Secret is created and the JSON keyfile is stored in a `Kind=Secret`. The `ClientID` and `ClientSecret` should be configured for the secret. This service principal should have proper access rights to the keyvault to be managed by the operator
+A service Principal client and Secret is created and the JSON keyfile is stored in a `Kind=Secret`. The `ClientID` and `ClientSecret` or `ClientCertificate` (in PEM format) should be configured for the secret. This service principal should have proper access rights to the keyvault to be managed by the operator.
#### Managed Identity authentication
diff --git a/docs/provider/beyondtrust.md b/docs/provider/beyondtrust.md
new file mode 100644
index 00000000000..1f21a4e43da
--- /dev/null
+++ b/docs/provider/beyondtrust.md
@@ -0,0 +1,138 @@
+## BeyondTrust Password Safe
+
+External Secrets Operator integrates with [BeyondTrust Password Safe](https://www.beyondtrust.com/docs/beyondinsight-password-safe/).
+
+Warning: The External Secrets Operator secure usage involves taking several measures. Please see [Security Best Practices](https://external-secrets.io/latest/guides/security-best-practices/) for more information.
+
+Warning: If the BT provider secret is deleted it will still exist in the Kubernetes secrets.
+
+### Prerequisites
+The BT provider supports retrieval of a secret from BeyondInsight/Password Safe versions 23.1 or greater.
+
+For this provider to retrieve a secret the Password Safe/Secrets Safe instance must be preconfigured with the secret in question and authorized to read it.
+
+### Authentication
+
+BeyondTrust [OAuth Authentication](https://www.beyondtrust.com/docs/beyondinsight-password-safe/ps/admin/configure-api-registration.htm).
+
+1. Create an API access registration in BeyondInsight
+2. Create or use an existing Secrets Safe Group
+3. Create or use an existing Application User
+4. Add API registration to the Application user
+5. Add the user to the group
+6. Add the Secrets Safe Feature to the group
+
+> NOTE: The ClientID and ClientSecret must be stored in a Kubernetes secret in order for the SecretStore to read the configuration.
+
+If you're using client credentials authentication:
+```sh
+kubectl create secret generic bt-secret --from-literal ClientSecret=""
+kubectl create secret generic bt-id --from-literal ClientId=""
+```
+
+If you're using API Key authentication:
+```sh
+kubectl create secret generic bt-apikey --from-literal ApiKey=""
+```
+
+### Client Certificate
+
+If using `retrievalType: MANAGED_ACCOUNT`, you will also need to download the pfx certificate from Secrets Safe, extract that certificate and create two Kubernetes secrets.
+
+```sh
+openssl pkcs12 -in client_certificate.pfx -nocerts -out ps_key.pem -nodes
+openssl pkcs12 -in client_certificate.pfx -clcerts -nokeys -out ps_cert.pem
+
+# Copy the text from the ps_key.pem to a file.
+-----BEGIN PRIVATE KEY-----
+...
+-----END PRIVATE KEY-----
+
+# Copy the text from the ps_cert.pem to a file.
+-----BEGIN CERTIFICATE-----
+...
+-----END CERTIFICATE-----
+
+kubectl create secret generic bt-certificate --from-file=ClientCertificate=./ps_cert.pem
+kubectl create secret generic bt-certificatekey --from-file=ClientCertificateKey=./ps_key.pem
+```
+
+### Creating a SecretStore
+
+You can follow the below example to create a `SecretStore` resource.
+You can also use a `ClusterSecretStore` allowing you to reference secrets from all namespaces. [ClusterSecretStore](https://external-secrets.io/latest/api/clustersecretstore/)
+
+```sh
+kubectl apply -f secret-store.yml
+```
+
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+ name: secretstore-beyondtrust
+spec:
+ provider:
+ beyondtrust:
+ server:
+ apiUrl: https://example.com:443/BeyondTrust/api/public/v3/
+ retrievalType: MANAGED_ACCOUNT # or SECRET
+ verifyCA: true
+ clientTimeOutSeconds: 45
+ auth:
+ certificate: # omit certificates if retrievalType is SECRET
+ secretRef:
+ name: bt-certificate
+ key: ClientCertificate
+ certificateKey:
+ secretRef:
+ name: bt-certificatekey
+ key: ClientCertificateKey
+ clientSecret: # define this section if using client credentials authentication
+ secretRef:
+ name: bt-secret
+ key: ClientSecret
+ clientId: # define this section if using client credentials authentication
+ secretRef:
+ name: bt-id
+ key: ClientId
+ apiKey: # define this section if using Api Key authentication
+ secretRef:
+ name: bt-apikey
+ key: ApiKey
+```
+
+### Creating an ExternalSecret
+
+You can follow the below example to create a `ExternalSecret` resource. Secrets can be referenced by path.
+You can also use a `ClusterExternalSecret` allowing you to reference secrets from all namespaces.
+
+```sh
+kubectl apply -f external-secret.yml
+```
+
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: beyondtrust-external-secret
+spec:
+ refreshInterval: 1h
+ secretStoreRef:
+ kind: SecretStore
+ name: secretstore-beyondtrust
+ target:
+ name: my-beyondtrust-secret # name of secret to create in k8s secrets (etcd)
+ creationPolicy: Owner
+ data:
+ - secretKey: secretKey
+ remoteRef:
+ key: system01/managed_account01
+```
+
+### Get the K8s secret
+
+```shell
+# WARNING: this command will reveal the stored secret in plain text
+kubectl get secret my-beyondtrust-secret -o jsonpath="{.data.secretKey}" | base64 --decode && echo
+```
\ No newline at end of file
diff --git a/docs/provider/bitwarden-secrets-manager.md b/docs/provider/bitwarden-secrets-manager.md
new file mode 100644
index 00000000000..72475a1c31f
--- /dev/null
+++ b/docs/provider/bitwarden-secrets-manager.md
@@ -0,0 +1,142 @@
+## Bitwarden Secrets Manager Provider
+
+This section describes how to set up the Bitwarden Secrets Manager provider for External Secrets Operator (ESO).
+
+!!! note
+
+ [Bitwarden Secrets Manager](https://bitwarden.com/products/secrets-manager/)
+ enables developers, DevOps, and cybersecurity teams to centrally store, manage, and deploy secrets at scale.
+ This is different from [Bitwarden Password Manager](https://bitwarden.com/products/personal/).
+ To integrate with Bitwarden **Password Manager**, reference the [example documentation](../examples/bitwarden.md).
+
+### Prerequisites
+
+In order for the bitwarden provider to work, we need a second service. This service is the [Bitwarden SDK Server](https://github.com/external-secrets/bitwarden-sdk-server).
+The Bitwarden SDK is Rust based and requires CGO enabled. In order to not restrict the capabilities of ESO, and the image
+size ( the bitwarden Rust SDK libraries are over 150MB in size ) it has been decided to create a soft wrapper
+around the SDK that runs as a separate service providing ESO with a light REST API to pull secrets through.
+
+#### Bitwarden SDK server
+
+The server itself can be installed together with ESO. The ESO Helm Chart packages this service as a dependency.
+The Bitwarden SDK Server's full name is hardcoded to bitwarden-sdk-server. This is so that the exposed service URL
+gets a determinable endpoint.
+
+In order to install the service install ESO with the following helm directive:
+
+```
+helm install external-secrets \
+ external-secrets/external-secrets \
+ -n external-secrets \
+ --create-namespace \
+ --set bitwarden-sdk-server.enabled=true
+```
+
+##### Certificate
+
+The Bitwarden SDK Server _NEEDS_ to run as an HTTPS service. That means that any installation that wants to communicate with the Bitwarden
+provider will need to generate a certificate. The best approach for that is to use cert-manager. It's easy to set up
+and can generate a certificate that the store can use to connect with the server.
+
+For a sample set up look at the bitwarden sdk server's test setup. It contains a self-signed certificate issuer for
+cert-manager.
+
+### External secret store
+
+With that out of the way, let's take a look at how a secret store would look like.
+
+```yaml
+{% include 'bitwarden-secrets-manager-secret-store.yaml' %}
+```
+
+The api url and identity url are optional. The secret should contain the token for the Machine account for bitwarden.
+
+!!! note inline end
+Make sure that the machine account has Read-Write access to the Project that the secrets are in.
+
+!!! note inline end
+A secret store is organization/project dependent. Meaning a 1 store == 1 organization/project. This is so that we ensure
+that no other project's secrets can be modified accidentally _or_ intentionally.
+
+### External Secrets
+
+There are two ways to fetch secrets from the provider.
+
+#### Find by UUID
+
+In order to fetch a secret by using its UUID simply provide that as remote key in the external secrets like this:
+
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: bitwarden
+spec:
+ refreshInterval: 1h
+ secretStoreRef:
+ # This name must match the metadata.name in the `SecretStore`
+ name: bitwarden-secretsmanager
+ kind: SecretStore
+ data:
+ - secretKey: test
+ remoteRef:
+ key: "339062b8-a5a1-4303-bf1d-b1920146a622"
+```
+
+#### Find by Name
+
+To find a secret using its name, we need a bit more information. Mainly, these are the rules to find a secret:
+
+- if name is a UUID get the secret
+- if name is NOT a UUID Property is mandatory that defines the projectID to look for
+- if name + projectID + organizationID matches, we return that secret
+- if more than one name exists for the same projectID within the same organization we error
+
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: bitwarden
+spec:
+ refreshInterval: 1h
+ secretStoreRef:
+ # This name must match the metadata.name in the `SecretStore`
+ name: bitwarden-secretsmanager
+ kind: SecretStore
+ data:
+ - secretKey: test
+ remoteRef:
+ key: "secret-name"
+```
+
+### Push Secret
+
+Pushing a secret is also implemented. Pushing a secret requires even more restrictions because Bitwarden Secrets Manager
+allows creating the same secret with the same key multiple times. In order to avoid overwriting, or potentially, returning
+the wrong secret, we restrict push secret with the following rules:
+
+- name, projectID, organizationID and value AND NOTE equal, we won't push it again.
+- name, projectID, organizationID and ONLY the value does not equal ( INCLUDING THE NOTE ) we update
+- any of the above isn't true, we create the secret ( this means that it will create a secret in a separate project )
+
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+metadata:
+ name: pushsecret-bitwarden # Customisable
+spec:
+ refreshInterval: 1h # Refresh interval for which push secret will reconcile
+ secretStoreRefs: # A list of secret stores to push secrets to
+ - name: bitwarden-secretsmanager
+ kind: SecretStore
+ selector:
+ secret:
+ name: my-secret # Source Kubernetes secret to be pushed
+ data:
+ - match:
+ secretKey: key # Source Kubernetes secret key to be pushed
+ remoteRef:
+ remoteKey: remote-key-name # Remote reference (where the secret is going to be pushed)
+ metadata:
+ note: "Note of the secret to add."
+```
diff --git a/docs/provider/chef.md b/docs/provider/chef.md
index 51622bbed8a..93df9b33b04 100644
--- a/docs/provider/chef.md
+++ b/docs/provider/chef.md
@@ -97,7 +97,7 @@ metadata:
labels:
app.kubernetes.io/name: external-secrets
spec:
- refreshInterval: 15m
+ refreshInterval: 1h
secretStoreRef:
name: vivid-clustersecretstore # name of ClusterSecretStore
kind: ClusterSecretStore
diff --git a/docs/provider/conjur.md b/docs/provider/conjur.md
index 931605a88c6..ad83076c43a 100644
--- a/docs/provider/conjur.md
+++ b/docs/provider/conjur.md
@@ -6,7 +6,9 @@ This section describes how to set up the Conjur provider for External Secrets Op
Before installing the Conjur provider, you need:
-* A running Conjur Server, with:
+* A running Conjur Server ([OSS](https://github.com/cyberark/conjur),
+[Enterprise](https://www.cyberark.com/products/secrets-manager-enterprise/), or
+[Cloud](https://www.cyberark.com/products/multi-cloud-secrets/)), with:
* An accessible Conjur endpoint (for example: `https://myapi.example.com`).
* Your configured Conjur authentication info (such as `hostid`, `apikey`, or JWT service ID). For more information on configuring Conjur, see [Policy statement reference](https://docs.cyberark.com/conjur-open-source/Latest/en/Content/Operations/Policy/policy-statement-ref.htm).
* Support for your authentication method (`apikey` is supported by default, `jwt` requires additional configuration).
diff --git a/docs/provider/delinea.md b/docs/provider/delinea.md
index 94fcdff4977..bf0eabca58e 100644
--- a/docs/provider/delinea.md
+++ b/docs/provider/delinea.md
@@ -47,7 +47,7 @@ kind: ExternalSecret
metadata:
name: secret
spec:
- refreshInterval: 20s
+ refreshInterval: 1h
secretStoreRef:
kind: SecretStore
name: secret-store
diff --git a/docs/provider/device42.md b/docs/provider/device42.md
new file mode 100644
index 00000000000..b27a635dce5
--- /dev/null
+++ b/docs/provider/device42.md
@@ -0,0 +1,58 @@
+External Secrets Operator integrates with [Device42 API](https://api.device42.com/#!/Passwords/getPassword) to sync Device42 secrets into a Kubernetes cluster.
+
+
+### Authentication
+
+`username` and `password` is required to talk to the Device42 API.
+
+```yaml
+apiVersion: v1
+kind: Secret
+metadata:
+ name: device42-credentials
+data:
+ username: dGVzdA== # "test"
+ password: dGVzdA== # "test"
+```
+
+### Creating a SecretStore
+
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+ name: device42-secret-store
+spec:
+ provider:
+ device42:
+ host:
+ auth:
+ secretRef:
+ credentials:
+ name:
+ key:
+ namespace:
+```
+
+### Referencing Secrets
+
+Secrets can be referenced by defining the `key` containing the Id of the secret.
+The `password` field is return from device42
+
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: device42-external-secret
+spec:
+ refreshInterval: 1h
+ secretStoreRef:
+ kind: SecretStore
+ name: device42-secret-store
+ target:
+ name:
+ data:
+ - secretKey:
+ remoteRef:
+ key:
+```
diff --git a/docs/provider/google-secrets-manager.md b/docs/provider/google-secrets-manager.md
index 7e523f1a738..c51e768aadc 100644
--- a/docs/provider/google-secrets-manager.md
+++ b/docs/provider/google-secrets-manager.md
@@ -111,3 +111,153 @@ The operator will fetch the GCP Secret Manager secret and inject it as a `Kind=S
```
kubectl get secret secret-to-be-created -n -o jsonpath='{.data.dev-secret-test}' | base64 -d
```
+
+### PushSecret owning an existing Google Secret Manager Secret
+
+There are some use cases where you want to use PushSecret for an existing Google Secret Manager Secret that already has labels defined. For example when the creation of the secret is managed by another controller like Kubernetes Config Connector (KCC) and the updating of the secret is managed by ESO.
+
+To allow ESO to take ownership of the existing Google Secret Manager Secret, you need to add the label `"managed-by": "external-secrets"`.
+
+By default, the PushSecret spec will replace any existing labels on the existing GCP Secret Manager Secret. To prevent this, a new field was added to the `spec.data.metadata` object called `mergePolicy` which defaults to `Replace` to ensure that there are no breaking changes and is backward compatible. The other option for this field is `Merge` which will merge the existing labels on the Google Secret Manager Secret with the labels defined in the PushSecret spec. This ensures that the existing labels defined on the Google Secret Manager Secret are retained.
+
+Example of using the `mergePolicy` field:
+
+```yaml
+{% raw %}
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+metadata:
+ name: pushsecret-example
+ namespace: default
+spec:
+ updatePolicy: Replace
+ deletionPolicy: None
+ refreshInterval: 1h
+ secretStoreRefs:
+ - name: gcp-secretstore
+ kind: SecretStore
+ selector:
+ secret:
+ name: bestpokemon
+ template:
+ data:
+ bestpokemon: "{{ .bestpokemon }}"
+ data:
+ - conversionStrategy: None
+ metadata:
+ mergePolicy: Merge
+ labels:
+ anotherLabel: anotherValue
+ match:
+ secretKey: bestpokemon
+ remoteRef:
+ remoteKey: best-pokemon
+{% endraw %}
+```
+
+### Secret Replication and Encryption Configuration
+
+#### Location and Replication
+
+By default, secrets are automatically replicated across multiple regions. You can specify a single location for your secrets by setting the `location` field:
+
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+ name: gcp-secret-store
+spec:
+ provider:
+ gcpsm:
+ projectID: my-project
+ location: us-east1 # Specify a single location
+```
+
+#### Customer-Managed Encryption Keys (CMEK)
+
+You can use your own encryption keys to encrypt secrets at rest. To use Customer-Managed Encryption Keys (CMEK), you need to:
+
+1. Create a Cloud KMS key
+2. Grant the service account the `roles/cloudkms.cryptoKeyEncrypterDecrypter` role on the key
+3. Specify the key in the PushSecret metadata
+
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+metadata:
+ name: pushsecret-example
+spec:
+ # ... other fields ...
+ data:
+ - match:
+ secretKey: mykey
+ remoteRef:
+ remoteKey: my-secret
+ metadata:
+ apiVersion: kubernetes.external-secrets.io/v1alpha1
+ kind: PushSecretMetadata
+ spec:
+ cmekKeyName: "projects/my-project/locations/us-east1/keyRings/my-keyring/cryptoKeys/my-key"
+```
+
+Note: When using CMEK, you must specify a location in the SecretStore as customer-managed encryption keys are region-specific.
+
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+ name: gcp-secret-store
+spec:
+ provider:
+ gcpsm:
+ projectID: my-project
+ location: us-east1 # Required when using CMEK
+```
+
+### Migration Guide: PushSecret Metadata Format (v0.11.x to v0.12.0)
+
+In version 0.12.0, the metadata format for PushSecrets has been standardized to use a structured format. If you're upgrading from v0.11.x, you'll need to update your PushSecret specifications.
+
+#### Old Format (v0.11.x)
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+spec:
+ data:
+ - match:
+ secretKey: mykey
+ remoteRef:
+ remoteKey: my-secret
+ metadata:
+ annotations:
+ key1: "value1"
+ labels:
+ key2: "value2"
+ topics:
+ - "topic1"
+ - "topic2"
+```
+
+#### New Format (v0.12.0+)
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+spec:
+ data:
+ - match:
+ secretKey: mykey
+ remoteRef:
+ remoteKey: my-secret
+ metadata:
+ apiVersion: kubernetes.external-secrets.io/v1alpha1
+ kind: PushSecretMetadata
+ spec:
+ annotations:
+ key1: "value1"
+ labels:
+ key2: "value2"
+ topics:
+ - "topic1"
+ - "topic2"
+ cmekKeyName: "projects/my-project/locations/us-east1/keyRings/my-keyring/cryptoKeys/my-key" # Optional: for CMEK
+```
diff --git a/docs/provider/hashicorp-vault.md b/docs/provider/hashicorp-vault.md
index 27c271c32a0..612f1cbe891 100644
--- a/docs/provider/hashicorp-vault.md
+++ b/docs/provider/hashicorp-vault.md
@@ -89,12 +89,13 @@ spec:
property: dev
---
-# will create a secret with:
-kind: Secret
-metadata:
- name: example-sync
-data:
- foobar: czNjcjN0
+# That will automatically create a Kubernetes Secret with:
+# apiVersion: v1
+# kind: Secret
+# metadata:
+# name: example-sync
+# data:
+# foobar: czNjcjN0
```
Keep in mind that fetching the labels with `metadataPolicy: Fetch` only works with KV sercrets engine version v2.
@@ -364,7 +365,7 @@ set of AWS Programmatic access credentials stored in a `Kind=Secret` and referen
### Mutual authentication (mTLS)
-Under specific compliance requirements, the Vault server can be set up to enforce mutual authentication from clients across all APIs by configuring the server with `tls_require_and_verify_client_cert = true`. This configuration differs fundamentally from the [TLS certificates auth method](#TLS-certificates-authentication). While the TLS certificates auth method allows the issuance of a Vault token through the `/v1/auth/cert/login` API, the mTLS configuration solely focuses on TLS transport layer authentication and lacks any authorization-related capabilities. It's important to note that the Vault token must still be included in the request, following any of the supported authentication methods mentioned earlier.
+Under specific compliance requirements, the Vault server can be set up to enforce mutual authentication from clients across all APIs by configuring the server with `tls_require_and_verify_client_cert = true`. This configuration differs fundamentally from the [TLS certificates auth method](#tls-certificates-authentication). While the TLS certificates auth method allows the issuance of a Vault token through the `/v1/auth/cert/login` API, the mTLS configuration solely focuses on TLS transport layer authentication and lacks any authorization-related capabilities. It's important to note that the Vault token must still be included in the request, following any of the supported authentication methods mentioned earlier.
```yaml
{% include 'vault-mtls-store.yaml' %}
diff --git a/docs/provider/infisical.md b/docs/provider/infisical.md
new file mode 100644
index 00000000000..617730bf906
--- /dev/null
+++ b/docs/provider/infisical.md
@@ -0,0 +1,68 @@
+![Infisical k8s Diagram](../pictures/external-secrets-operator.png)
+
+Sync secrets from [Infisical](https://www.infisical.com) to your Kubernetes cluster using External Secrets Operator.
+
+## Authentication
+In order for the operator to fetch secrets from Infisical, it needs to first authenticate with Infisical.
+
+To authenticate, you can use [Universal Auth](https://infisical.com/docs/documentation/platform/identities/universal-auth) from [Machine identities](https://infisical.com/docs/documentation/platform/identities/machine-identities).
+
+Follow the [guide here](https://infisical.com/docs/documentation/platform/identities/universal-auth) to learn how to create and obtain a pair of Client Secret and Client ID.
+
+## Storing Your Machine Identity Secrets
+
+Once you have generated a pair of `Client ID` and `Client Secret`, you will need to store these credentials in your cluster as a Kubernetes secret.
+
+!!! note inline end
+ Remember to replace with your own Machine Identity credentials.
+
+```yaml
+apiVersion: v1
+kind: Secret
+metadata:
+ name: universal-auth-credentials
+type: Opaque
+
+stringData:
+ clientId:
+ clientSecret:
+```
+
+### Secret Store
+
+You will then need to create a generic `SecretStore`. An sample `SecretStore` has been is shown below.
+
+!!! tip inline end
+ To get your project slug from Infisical, head over to the project settings and click the button `Copy Project Slug`.
+
+```yaml
+{% include 'infisical-generic-secret-store.yaml' %}
+```
+
+!!! Note
+ For `ClusterSecretStore`, be sure to set `namespace` in `universalAuthCredentials.clientId` and `universalAuthCredentials.clientSecret`.
+
+## Fetch Individual Secret(s)
+
+To sync one or more secrets individually, use the following YAML:
+
+```yaml
+{% include 'infisical-fetch-secret.yaml' %}
+```
+
+## Fetch All Secrets
+
+To sync all secrets from an Infisical , use the following YAML:
+
+``` yaml
+{% include 'infisical-fetch-all-secrets.yaml' %}
+```
+
+## Filter By Prefix/Name
+
+To filter secrets by `path` (path prefix) and `name` (regular expression).
+
+``` yaml
+{% include 'infisical-filtered-secrets.yaml' %}
+```
+
diff --git a/docs/provider/keeper-security.md b/docs/provider/keeper-security.md
index 0d6e8f26add..9fd112be483 100644
--- a/docs/provider/keeper-security.md
+++ b/docs/provider/keeper-security.md
@@ -80,7 +80,7 @@ There are some limitations using this provider.
## Push Secrets
-Push Secret will only work with a custom KeeperSecurity Record type `ExternalSecret`
+Push Secret will only work with a custom KeeperSecurity Record type `externalSecrets`
### Behavior
* `selector`:
diff --git a/docs/provider/kubernetes.md b/docs/provider/kubernetes.md
index 40d556659e2..b407ba73929 100644
--- a/docs/provider/kubernetes.md
+++ b/docs/provider/kubernetes.md
@@ -255,7 +255,7 @@ kind: PushSecret
metadata:
name: example
spec:
- refreshInterval: 10s
+ refreshInterval: 1h
secretStoreRefs:
- name: k8s-store-remote-ns
kind: SecretStore
@@ -298,6 +298,74 @@ rules:
- create
```
+#### PushSecret Metadata
+
+The Kubernetes provider is able to manage both `metadata.labels` and `metadata.annotations` of the secret on the target cluster.
+
+Users have different preferences on what metadata should be pushed. ESO by default pushes both labels and annotations to the target secret and merges them with the existing metadata.
+
+You can specify the metadata in the `spec.template.metadata` section if you want to decouple it from the existing secret.
+
+```yaml
+{% raw %}
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+metadata:
+ name: example
+spec:
+ # ...
+ template:
+ metadata:
+ labels:
+ app.kubernetes.io/part-of: argocd
+ data:
+ mysql_connection_string: "mysql://{{ .hostname }}:3306/{{ .database }}"
+ data:
+ - match:
+ secretKey: mysql_connection_string
+ remoteRef:
+ remoteKey: backend_secrets
+ property: mysql_connection_string
+{% endraw %}
+```
+
+Further, you can leverage the `.data[].metadata` section to fine-tine the behaviour of the metadata merge strategy. The metadata section is a versioned custom-resource _alike_ structure, the behaviour is detailed below.
+
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+metadata:
+ name: example
+spec:
+ # ...
+ data:
+ - match:
+ secretKey: example-1
+ remoteRef:
+ remoteKey: example-remote-secret
+ property: url
+
+ metadata:
+ apiVersion: kubernetes.external-secrets.io/v1alpha1
+ kind: PushSecretMetadata
+ spec:
+ sourceMergePolicy: Merge # or Replace
+ targetMergePolicy: Merge # or Replace / Ignore
+ labels:
+ color: red
+ annotations:
+ yes: please
+
+```
+
+
+| Field | Type | Description |
+| ----------------- | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| sourceMergePolicy | string: `Merge`, `Replace` | The sourceMergePolicy defines how the metadata of the source secret is merged. `Merge` will merge the metadata of the source secret with the metadata defined in `.data[].metadata`. With `Replace`, the metadata in `.data[].metadata` replaces the source metadata. |
+| targetMergePolicy | string: `Merge`, `Replace`, `Ignore` | The targetMergePolicy defines how ESO merges the metadata produced by the sourceMergePolicy with the target secret. With `Merge`, the source metadata is merged with the existing metadata from the target secret. `Replace` will replace the target metadata with the metadata defined in the source. `Ignore` leaves the target metadata as is. |
+| labels | `map[string]string` | The labels. |
+| annotations | `map[string]string` | The annotations. |
+
#### Implementation Considerations
When utilizing the PushSecret feature and configuring the permissions for the SecretStore, consider the following:
diff --git a/docs/provider/oracle-vault.md b/docs/provider/oracle-vault.md
index 243fc507421..d48fc912e56 100644
--- a/docs/provider/oracle-vault.md
+++ b/docs/provider/oracle-vault.md
@@ -1,64 +1,94 @@
## Oracle Vault
-External Secrets Operator integrates with [OCI API](https://github.com/oracle/oci-go-sdk) to sync secret on the Oracle Vault to secrets held on the Kubernetes cluster.
+External Secrets Operator integrates with the [Oracle Cloud Infrastructure (OCI) REST API](https://docs.oracle.com/en-us/iaas/api/) to manage secrets in Oracle Vault. All secret operations exposed by External Secrets Operator are supported by the Oracle provider.
-### Authentication
+For more information on managing OCI Vaults and OCI Vault Secrets, see the following documentation:
-Specify the authenticating principal with `principalType`, using `UserPrincipal`, `InstancePrincipal`, or `Workload` as values.
-If `principalType` or `auth` are not set, the operator defaults to instance principal for authentication.
+- [Managing Vaults](https://docs.oracle.com/en-us/iaas/Content/KeyManagement/Tasks/managingvaults.htm)
+- [Managing Vault Secrets](https://docs.oracle.com/en-us/iaas/Content/KeyManagement/Tasks/managingsecrets.htm)
-For user principal, userOCID, tenancyOCID, fingerprint and private key are required.
-The fingerprint and key file should be supplied in the secret with the rest being provided in the secret store.
+## Authentication
-See url for what region you you are accessing.
+External Secrets Operator may authenticate to OCI Vault using User Principal, [Instance Principal](https://blogs.oracle.com/developers/post/accessing-the-oracle-cloud-infrastructure-api-using-instance-principals), or [Workload Identity](https://blogs.oracle.com/cloud-infrastructure/post/oke-workload-identity-greater-control-access).
+
+To specify the authenticating principal in a secret store, set the `spec.provider.oracle.principalType` value. Note that the value of `principalType` defaults `InstancePrincipal` if not set.
+
+{% include 'oracle-principal-type.yaml' %}
+
+### User Principal Authentication
+
+For user principal authentication, region, user OCID, tenancy OCID, private key, and fingerprint are required.
+The private key and fingerprint must be supplied in a Kubernetes secret, while the user OCID, tenancy OCID, and region should be set in the secret store.
+
+To get your user principal information, find url for the OCI region you are accessing.
![userOCID-details](../pictures/screenshot_region.png)
-Select tenancy in the top right to see your user OCID as shown below.
+Select tenancy in the top right to see your tenancy OCID as shown below.
![tenancyOCID-details](../pictures/screenshot_tenancy_OCID.png)
Select your user in the top right to see your user OCID as shown below.
![region-details](../pictures/screenshot_user_OCID.png)
+Your fingerprint will be attatched to your API key, once it has been generated. Private keys can be created or uploaded on the same page as the your user OCID.
+![fingerprint-details](../pictures/screenshot_fingerprint.png)
-#### Service account key authentication
+Once you click "Add API Key" you will be shown the following, where you can download the key in the necessary PEM format for API requests. Creating a private key will automatically generate a fingerprint.
+![API-key-details](../pictures/screenshot_API_key.png)
-Create a secret containing your private key and fingerprint:
+Next, create a secret containing your private key and fingerprint:
```yaml
{% include 'oracle-credentials-secret.yaml' %}
```
-Your fingerprint will be attatched to your API key, once it has been generated. Found on the same page as the user OCID.
-![fingerprint-details](../pictures/screenshot_fingerprint.png)
-
-Once you click "Add API Key" you will be shown the following, where you can download the RSA key in the necessary PEM format for API requests.
-This will automatically generate a fingerprint.
-![API-key-details](../pictures/screenshot_API_key.png)
-
-### Update secret store
-Be sure the `oracle` provider is listed in the `Kind=SecretStore`.
+After creating the credentials secret, the secret store can be configured:
```yaml
{% include 'oracle-secret-store.yaml' %}
```
**NOTE:** In case of a `ClusterSecretStore`, Be sure to provide `namespace` in `privatekey` and `fingerprint` with the namespaces where the secrets reside.
-### Creating external secret
-To create a kubernetes secret from the Oracle Cloud Interface secret a`Kind=ExternalSecret` is needed.
+### Instance Principal Authentication (OCI)
+
+Instance Principal uses a pod's instance principal to authenticate to OCI Vault. Ensure your cluster instances have the appropriate policies to use [Instance Principal](https://blogs.oracle.com/developers/post/accessing-the-oracle-cloud-infrastructure-api-using-instance-principals).
+
+```yaml
+{% include 'oracle-instance-principal.yaml' %}
+```
+
+### Workload Identity Authentication (OCI/OKE)
+
+[Workload Identity](https://blogs.oracle.com/cloud-infrastructure/post/oke-workload-identity-greater-control-access) can be used to grant the External Secrets Operator pod policy driven access to OCI Vault when running on Oracle Container Engine for Kubernetes (OKE).
+
+Note that if a service account is not provided in the secret store, the Oracle provider will authenticate using the service account token of the External Secrets Operator.
+
+```yaml
+{% include 'oracle-workload-identity.yaml' %}
+```
+
+## Creating an External Secret
+
+To create a Kubernetes secret from an OCI Vault secret a `Kind=ExternalSecret` is needed. The External Secret will reference an OCI Vault instance containing secrets with either JSON or plaintext data.
+
+#### External Secret targeting JSON data
```yaml
{% include 'oracle-external-secret.yaml' %}
```
+#### External Secret targeting plaintext data
+```yaml
+{% include 'oracle-external-secret-plaintext.yaml' %}
+```
### Getting the Kubernetes secret
-The operator will fetch the project variable and inject it as a `Kind=Secret`.
+The operator will fetch the OCI Vault Secret and inject it as a `Kind=Secret`.
```
kubectl get secret oracle-secret-to-create -o jsonpath='{.data.dev-secret-test}' | base64 -d
```
-### PushSecrets and retrieving multiple secrets.
+## PushSecrets and retrieving multiple secrets.
When using [PushSecrets](https://external-secrets.io/latest/guides/pushsecrets/), the compartment OCID and encryption key OCID must be specified in the
Oracle SecretStore. You can find your compartment and encrpytion key OCIDs in the OCI console.
@@ -67,4 +97,3 @@ If [retrieving multiple secrets](https://external-secrets.io/latest/guides/getal
```yaml
{% include 'oracle-secret-store-pushsecret.yaml' %}
```
-
diff --git a/docs/provider/previder.md b/docs/provider/previder.md
new file mode 100644
index 00000000000..99c193c7a41
--- /dev/null
+++ b/docs/provider/previder.md
@@ -0,0 +1,64 @@
+
+![Previder Secret Vault](../pictures/previder-provider.png)
+
+## Previder Secret Vault Manager
+
+External Secrets Operator integrates with [Previder Secrets Vault](https://vault.previder.io) for secure secret management.
+
+### Authentication
+
+We support Access Token authentication using a Secrets Vault ReadWrite or ReadOnly token.
+
+This token can be created with the [vault-cli](https://github.com/previder/vault-cli) using an Environment token which can be acquired via the [Previder Portal](https://portal.previder.nl).
+
+#### Access Token authentication
+
+To use the access token, first create it as a regular Kubernetes Secret and then associate it with the Previder Secret Store.
+
+```yaml
+apiVersion: v1
+kind: Secret
+metadata:
+ name: previder-vault-sample-secret
+data:
+ previder-vault-token: cHJldmlkZXIgdmF1bHQgZXhhbXBsZQ==
+```
+
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+ name: previder-secretstore-sample
+spec:
+ provider:
+ previder:
+ auth:
+ secretRef:
+ accessToken:
+ name: previder-vault-sample-secret
+ key: previder-vault-token
+```
+
+
+### Creating external secret
+
+To create a kubernetes secret from the Previder Secret Vault, create an ExternalSecret with a reference to a Vault secret.
+
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: example
+spec:
+ refreshInterval: 1h
+ secretStoreRef:
+ name: previder-secretstore-sample
+ kind: SecretStore
+ target:
+ name: example-secret
+ creationPolicy: Owner
+ data:
+ - secretKey: local-secret-key
+ remoteRef:
+ key: token-name-or-id
+```
diff --git a/docs/provider/pulumi.md b/docs/provider/pulumi.md
index cf1162d6b02..7470d8fb5e8 100644
--- a/docs/provider/pulumi.md
+++ b/docs/provider/pulumi.md
@@ -2,13 +2,17 @@
Sync environments, configs and secrets from [Pulumi ESC](https://www.pulumi.com/product/esc/) to Kubernetes using the External Secrets Operator.
+![Pulumi ESC](../pictures/pulumi-esc.png)
+
+More information about setting up [Pulumi](https://www.pulumi.com/) ESC can be found in the [Pulumi ESC documentation](https://www.pulumi.com/docs/esc/).
+
### Authentication
Pulumi [Access Tokens](https://www.pulumi.com/docs/pulumi-cloud/access-management/access-tokens/) are recommended to access Pulumi ESC.
### Creating a SecretStore
-A Pulumi SecretStore can be created by specifying the `organization` and `environment` and referencing a Kubernetes secret containing the `accessToken`.
+A Pulumi `SecretStore` can be created by specifying the `organization`, `project` and `environment` and referencing a Kubernetes secret containing the `accessToken`.
```yaml
apiVersion: external-secrets.io/v1beta1
@@ -19,6 +23,7 @@ spec:
provider:
pulumi:
organization:
+ project:
environment:
accessToken:
secretRef:
@@ -26,7 +31,29 @@ spec:
key:
```
-If required, the API URL (`apiUrl`) can be customized as well. If not specified, the default value is `https://api.pulumi.com`.
+If required, the API URL (`apiUrl`) can be customized as well. If not specified, the default value is `https://api.pulumi.com/api/esc`.
+
+### Creating a ClusterSecretStore
+
+Similarly, a `ClusterSecretStore` can be created by specifying the `namespace` and referencing a Kubernetes secret containing the `accessToken`.
+
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: ClusterSecretStore
+metadata:
+ name: secret-store
+spec:
+ provider:
+ pulumi:
+ organization:
+ project:
+ environment:
+ accessToken:
+ secretRef:
+ name:
+ key:
+ namespace:
+```
### Referencing Secrets
@@ -38,7 +65,7 @@ kind: ExternalSecret
metadata:
name: secret
spec:
- refreshInterval: 5m
+ refreshInterval: 1h
secretStoreRef:
kind: SecretStore
name: secret-store
@@ -71,3 +98,61 @@ spec:
* root.array["*"].field
See [Pulumi's documentation](https://www.pulumi.com/docs/concepts/options/ignorechanges/) for more information.
+
+### PushSecrets
+
+With the latest release of Pulumi ESC, secrets can be pushed to the Pulumi service. This can be done by creating a `PushSecrets` object.
+
+Here is a basic example of how to define a `PushSecret` object:
+
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+metadata:
+ name: push-secret-example
+spec:
+ refreshInterval: 1h
+ selector:
+ secret:
+ name:
+ secretStoreRefs:
+ - kind: ClusterSecretStore
+ name: secret-store
+ data:
+ - match:
+ secretKey:
+ remoteRef:
+ remoteKey:
+```
+
+This will then push the secret to the Pulumi service. If the secret already exists, it will be updated.
+
+### Limitations
+
+Currently, the Pulumi provider only supports nested objects up to a depth of 1. Any nested objects beyond this depth will be stored as a string with the JSON representation.
+
+This Pulumi ESC example:
+
+```yaml
+values:
+ backstage:
+ my: test
+ test: hello
+ test22:
+ my: hello
+ test33:
+ world: true
+ x: true
+ num: 42
+```
+
+Will result in the following Kubernetes secret:
+
+```yaml
+my: test
+num: "42"
+test: hello
+test22: '{"my":{"trace":{"def":{"begin":{"byte":72,"column":11,"line":6},"end":{"byte":77,"column":16,"line":6},"environment":"tgif-demo"}},"value":"hello"}}'
+test33: '{"world":{"trace":{"def":{"begin":{"byte":103,"column":14,"line":8},"end":{"byte":107,"column":18,"line":8},"environment":"tgif-demo"}},"value":true}}'
+x: "true"
+```
diff --git a/docs/provider/scaleway.md b/docs/provider/scaleway.md
index d1cb00baf29..cef0e9e2f74 100644
--- a/docs/provider/scaleway.md
+++ b/docs/provider/scaleway.md
@@ -38,7 +38,7 @@ kind: ExternalSecret
metadata:
name: secret
spec:
- refreshInterval: 20s
+ refreshInterval: 1h
secretStoreRef:
kind: SecretStore
name: secret-store
diff --git a/docs/provider/secretserver.md b/docs/provider/secretserver.md
new file mode 100644
index 00000000000..2f389971fd5
--- /dev/null
+++ b/docs/provider/secretserver.md
@@ -0,0 +1,211 @@
+# Delinea Secret Server
+
+External Secrets Operator integration with [Delinea Secret Server](https://docs.delinea.com/online-help/secret-server/start.htm).
+
+### Creating a SecretStore
+
+You need a username, password and a fully qualified Secret Server tenant URL to authenticate
+i.e. `https://yourTenantName.secretservercloud.com`.
+
+Both username and password can be specified either directly in your `SecretStore` yaml config, or by referencing a kubernetes secret.
+
+To acquire a username and password, refer to the Secret Server [user management](https://docs.delinea.com/online-help/secret-server/users/creating-users/index.htm) documentation.
+
+Both `username` and `password` can either be specified directly via the `value` field (example below)
+>spec.provider.secretserver.username.value: "yourusername"
+spec.provider.secretserver.password.value: "yourpassword"
+
+Or you can reference a kubernetes secret (password example below).
+
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+ name: secret-server-store
+spec:
+ provider:
+ secretserver:
+ serverURL: "https://yourtenantname.secretservercloud.com"
+ username:
+ value: "yourusername"
+ password:
+ secretRef:
+ name:
+ key:
+```
+
+### Referencing Secrets
+
+Secrets may be referenced by secret ID or secret name.
+>Please note if using the secret name
+the name field must not contain spaces or control characters.
+If multiple secrets are found, *`only the first found secret will be returned`*.
+
+Please note: `Retrieving a specific version of a secret is not yet supported.`
+
+Note that because all Secret Server secrets are JSON objects, you must specify the `remoteRef.property`
+in your ExternalSecret configuration.
+You can access nested values or arrays using [gjson syntax](https://github.com/tidwall/gjson/blob/master/SYNTAX.md).
+
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: secret-server-external-secret
+spec:
+ refreshInterval: 1h
+ secretStoreRef:
+ kind: SecretStore
+ name: secret-server-store
+ data:
+ - secretKey: SecretServerValue #
+ remoteRef:
+ key: "52622" #
+ property: "array.0.value" # * an empty property will return the entire secret
+```
+
+### Preparing your secret
+You can either retrieve your entire secret or you can use a JSON formatted string
+stored in your secret located at Items[0].ItemValue to retrieve a specific value.
+See example JSON secret below.
+
+#### Examples
+Using the json formatted secret below:
+
+- Lookup a single top level property using secret ID.
+
+>spec.data.remoteRef.key = 52622 (id of the secret)
+spec.data.remoteRef.property = "user" (Items.0.ItemValue user attribute)
+returns: marktwain@hannibal.com
+
+- Lookup a nested property using secret name.
+
+>spec.data.remoteRef.key = "external-secret-testing" (name of the secret)
+spec.data.remoteRef.property = "books.1" (Items.0.ItemValue books.1 attribute)
+returns: huckleberryFinn
+
+- Lookup by secret ID (*secret name will work as well*) and return the entire secret.
+
+>spec.data.remoteRef.key = "52622" (id of the secret)
+spec.data.remoteRef.property = ""
+returns: The entire secret in JSON format as displayed below
+
+
+```JSON
+{
+ "Name": "external-secret-testing",
+ "FolderID": 73,
+ "ID": 52622,
+ "SiteID": 1,
+ "SecretTemplateID": 6098,
+ "SecretPolicyID": -1,
+ "PasswordTypeWebScriptID": -1,
+ "LauncherConnectAsSecretID": -1,
+ "CheckOutIntervalMinutes": -1,
+ "Active": true,
+ "CheckedOut": false,
+ "CheckOutEnabled": false,
+ "AutoChangeEnabled": false,
+ "CheckOutChangePasswordEnabled": false,
+ "DelayIndexing": false,
+ "EnableInheritPermissions": true,
+ "EnableInheritSecretPolicy": true,
+ "ProxyEnabled": false,
+ "RequiresComment": false,
+ "SessionRecordingEnabled": false,
+ "WebLauncherRequiresIncognitoMode": false,
+ "Items": [
+ {
+ "ItemID": 280265,
+ "FieldID": 439,
+ "FileAttachmentID": 0,
+ "FieldName": "Data",
+ "Slug": "data",
+ "FieldDescription": "json text field",
+ "Filename": "",
+ "ItemValue": "{ \"user\": \"marktwain@hannibal.com\", \"occupation\": \"author\",\"books\":[ \"tomSawyer\",\"huckleberryFinn\",\"Pudd'nhead Wilson\"] }",
+ "IsFile": false,
+ "IsNotes": false,
+ "IsPassword": false
+ }
+ ]
+}
+```
+
+### Referencing Secrets in multiple Items secrets
+
+If there is more then one Item in the secret, it supports to retrieve them (all Item.\*.ItemValue) looking up by Item.\*.FieldName or Item.\*.Slug, instead of the above behaviour to use gjson only on the first item Items.0.ItemValue only.
+
+#### Examples
+
+Using the json formatted secret below:
+
+- Lookup a single top level property using secret ID.
+
+>spec.data.remoteRef.key = 4000 (id of the secret)
+spec.data.remoteRef.property = "Username" (Items.0.FieldName)
+returns: usernamevalue
+
+- Lookup a nested property using secret name.
+
+>spec.data.remoteRef.key = "Secretname" (name of the secret)
+spec.data.remoteRef.property = "password" (Items.1.slug)
+returns: passwordvalue
+
+- Lookup by secret ID (*secret name will work as well*) and return the entire secret.
+
+>spec.data.remoteRef.key = "4000" (id of the secret)
+returns: The entire secret in JSON format as displayed below
+
+
+```JSON
+{
+ "Name": "Secretname",
+ "FolderID": 0,
+ "ID": 4000,
+ "SiteID": 0,
+ "SecretTemplateID": 0,
+ "LauncherConnectAsSecretID": 0,
+ "CheckOutIntervalMinutes": 0,
+ "Active": false,
+ "CheckedOut": false,
+ "CheckOutEnabled": false,
+ "AutoChangeEnabled": false,
+ "CheckOutChangePasswordEnabled": false,
+ "DelayIndexing": false,
+ "EnableInheritPermissions": false,
+ "EnableInheritSecretPolicy": false,
+ "ProxyEnabled": false,
+ "RequiresComment": false,
+ "SessionRecordingEnabled": false,
+ "WebLauncherRequiresIncognitoMode": false,
+ "Items": [
+ {
+ "ItemID": 0,
+ "FieldID": 0,
+ "FileAttachmentID": 0,
+ "FieldName": "Username",
+ "Slug": "username",
+ "FieldDescription": "",
+ "Filename": "",
+ "ItemValue": "usernamevalue",
+ "IsFile": false,
+ "IsNotes": false,
+ "IsPassword": false
+ },
+ {
+ "ItemID": 0,
+ "FieldID": 0,
+ "FileAttachmentID": 0,
+ "FieldName": "Password",
+ "Slug": "password",
+ "FieldDescription": "",
+ "Filename": "",
+ "ItemValue": "passwordvalue",
+ "IsFile": false,
+ "IsNotes": false,
+ "IsPassword": false
+ }
+ ]
+}
+```
diff --git a/docs/provider/webhook.md b/docs/provider/webhook.md
index 6c0d9b71162..fa7aec82f43 100644
--- a/docs/provider/webhook.md
+++ b/docs/provider/webhook.md
@@ -4,7 +4,7 @@ External Secrets Operator can integrate with simple web apis by specifying the e
### Example
-First, create a SecretStore with a webhook backend. We'll use a static user/password `root`:
+First, create a SecretStore with a webhook backend. We'll use a static user/password `test`:
```yaml
{% raw %}
@@ -124,4 +124,4 @@ spec:
```
### Webhook as generators
-You can also leverage webhooks as generators, following the same syntax. The only difference is that the webhook generator needs its source secrets to be labeled, as opposed to webhook secretstores. Please see the [generator-webhook](../api/generator/webhook.md) documentation for more information.
+You can also leverage webhooks as generators, following the same syntax. The only difference is that the webhook generator needs its source secrets to be labeled, as opposed to webhook secretstores. Please see the [generator-webhook](../api/generator/webhook.md) documentation for more information.
diff --git a/docs/snippets/1password-push-secret.yaml b/docs/snippets/1password-push-secret.yaml
new file mode 100644
index 00000000000..8097421d9ce
--- /dev/null
+++ b/docs/snippets/1password-push-secret.yaml
@@ -0,0 +1,32 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: source-secret
+stringData:
+ source-key: "my-secret"
+---
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+metadata:
+ name: pushsecret-example # Customisable
+spec:
+ deletionPolicy: Delete
+ refreshInterval: 1h
+ secretStoreRefs:
+ - name: 1password
+ kind: ClusterSecretStore
+ selector:
+ secret:
+ name: source-secret # Source Kubernetes secret
+ data:
+ - match:
+ secretKey: source-key # Source Kubernetes secret key to be pushed
+ remoteRef:
+ remoteKey: 1pw-secret-name # 1Password item/secret name
+ property: password # (Optional) 1Password field type, default password
+ metadata:
+ apiVersion: kubernetes.external-secrets.io/v1alpha1
+ kind: PushSecretMetadata
+ spec:
+ vault: staging # Optional the vault the secret is going to be pushed to, defaults to the first defined vault in the (Cluster)SecretStore
+ tags: ["tag1", "tag2"] # Optional metadata to be pushed with the secret
diff --git a/docs/snippets/1password-secret-store.yaml b/docs/snippets/1password-secret-store.yaml
index 3de4aadfd3a..64d35812e79 100644
--- a/docs/snippets/1password-secret-store.yaml
+++ b/docs/snippets/1password-secret-store.yaml
@@ -6,7 +6,7 @@ metadata:
spec:
provider:
onepassword:
- connectHost: https://onepassword-connect-staging
+ connectHost: https://onepassword-connect-staging:8080
vaults:
staging: 1 # look in this vault first
shared: 2 # next look in here. error if not found
diff --git a/docs/snippets/akeyless-push-secret.yaml b/docs/snippets/akeyless-push-secret.yaml
new file mode 100644
index 00000000000..f76467234c7
--- /dev/null
+++ b/docs/snippets/akeyless-push-secret.yaml
@@ -0,0 +1,18 @@
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+metadata:
+ name: push-secret
+spec:
+ refreshInterval: 1h
+ updatePolicy: Replace
+ deletionPolicy: Delete
+ secretStoreRefs:
+ - name: akeyless-secret-store
+ kind: SecretStore
+ selector:
+ secret:
+ name: k8s-created-secret
+ data:
+ - match:
+ remoteRef:
+ remoteKey: eso-created/my-secret
diff --git a/docs/snippets/aws-anchore-engine-access-credentials-external-secret.yaml b/docs/snippets/aws-anchore-engine-access-credentials-external-secret.yaml
index a2a0a95f67f..7f9e18d823f 100644
--- a/docs/snippets/aws-anchore-engine-access-credentials-external-secret.yaml
+++ b/docs/snippets/aws-anchore-engine-access-credentials-external-secret.yaml
@@ -5,7 +5,7 @@ metadata:
name: anchore-access-credentials
namespace: ci
spec:
- refreshInterval: 1m
+ refreshInterval: 1h
secretStoreRef:
name: cluster-secrets-store
kind: ClusterSecretStore
diff --git a/docs/snippets/aws-jenkins-credential-github-ssh-external-secret.yaml b/docs/snippets/aws-jenkins-credential-github-ssh-external-secret.yaml
index d52917eb3b0..f0e81828c5a 100644
--- a/docs/snippets/aws-jenkins-credential-github-ssh-external-secret.yaml
+++ b/docs/snippets/aws-jenkins-credential-github-ssh-external-secret.yaml
@@ -5,7 +5,7 @@ metadata:
name: github-ssh-access
namespace: ci
spec:
- refreshInterval: 1m
+ refreshInterval: 1h
secretStoreRef:
name: cluster-parameter-store
kind: ClusterSecretStore
diff --git a/docs/snippets/aws-jenkins-credential-sonarqube-api-token-external-secret.yaml b/docs/snippets/aws-jenkins-credential-sonarqube-api-token-external-secret.yaml
index c4404dc4f55..29024de0d62 100644
--- a/docs/snippets/aws-jenkins-credential-sonarqube-api-token-external-secret.yaml
+++ b/docs/snippets/aws-jenkins-credential-sonarqube-api-token-external-secret.yaml
@@ -5,7 +5,7 @@ metadata:
name: sonarqube-api-token
namespace: ci
spec:
- refreshInterval: 1m
+ refreshInterval: 1h
secretStoreRef:
name: cluster-secrets-store
kind: ClusterSecretStore
diff --git a/docs/snippets/aws-jenkins-credentials-harbor-chart-robot-external-secret.yaml b/docs/snippets/aws-jenkins-credentials-harbor-chart-robot-external-secret.yaml
index fa8c27167c0..16bdbc16d28 100644
--- a/docs/snippets/aws-jenkins-credentials-harbor-chart-robot-external-secret.yaml
+++ b/docs/snippets/aws-jenkins-credentials-harbor-chart-robot-external-secret.yaml
@@ -5,7 +5,7 @@ metadata:
name: harbor-chart-robot
namespace: ci
spec:
- refreshInterval: 1m
+ refreshInterval: 1h
secretStoreRef:
name: cluster-secrets-store
kind: ClusterSecretStore
diff --git a/docs/snippets/aws-pm-push-secret-with-metadata.yaml b/docs/snippets/aws-pm-push-secret-with-metadata.yaml
new file mode 100644
index 00000000000..b3298b1712c
--- /dev/null
+++ b/docs/snippets/aws-pm-push-secret-with-metadata.yaml
@@ -0,0 +1,46 @@
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+metadata:
+ name: pushsecret-example # Customisable
+ namespace: default # Same of the SecretStores
+spec:
+ deletionPolicy: Delete # the provider' secret will be deleted if the PushSecret is deleted
+ refreshInterval: 1h # Refresh interval for which push secret will reconcile
+ secretStoreRefs: # A list of secret stores to push secrets to
+ - name: aws-parameterstore
+ kind: SecretStore
+ selector:
+ secret:
+ name: pokedex-credentials # Source Kubernetes secret to be pushed
+ data:
+ - match:
+ remoteRef:
+ remoteKey: my-first-parameter # Remote reference (where the secret is going to be pushed)
+ metadata:
+ apiVersion: kubernetes.external-secrets.io/v1alpha1
+ kind: PushSecretMetadata
+ spec:
+ secretType: SecureString
+ kmsKeyID: bb123123-b2b0-4f60-ac3a-44a13f0e6b6c
+ tier:
+ type: Advanced # default is Standard
+ policies:
+ - type: "Expiration"
+ version: "1.0"
+ attributes:
+ timestamp: "2024-12-02T21:34:33.000Z"
+ - type: "ExpirationNotification"
+ version: "1.0"
+ attributes:
+ before: "2"
+ unit: "Days"
+ - type: "ExpirationNotification"
+ version: "1.0"
+ attributes:
+ before: "30"
+ unit: "Days"
+ - type: "NoChangeNotification"
+ version: "1.0"
+ attributes:
+ after: "30"
+ unit: "Days"
diff --git a/docs/snippets/aws-sm-external-secret.yaml b/docs/snippets/aws-sm-external-secret.yaml
index 479253c7c21..e9e6ea9ee7e 100644
--- a/docs/snippets/aws-sm-external-secret.yaml
+++ b/docs/snippets/aws-sm-external-secret.yaml
@@ -3,7 +3,7 @@ kind: ExternalSecret
metadata:
name: example
spec:
- refreshInterval: 1m
+ refreshInterval: 1h
secretStoreRef:
name: aws-secretsmanager
kind: SecretStore
diff --git a/docs/snippets/aws-sm-push-secret-with-metadata.yaml b/docs/snippets/aws-sm-push-secret-with-metadata.yaml
index b1622a042e6..6343262344e 100644
--- a/docs/snippets/aws-sm-push-secret-with-metadata.yaml
+++ b/docs/snippets/aws-sm-push-secret-with-metadata.yaml
@@ -5,7 +5,7 @@ metadata:
namespace: teamb # Same of the SecretStores
spec:
deletionPolicy: Delete
- refreshInterval: 10s # Refresh interval for which push secret will reconcile
+ refreshInterval: 1h # Refresh interval for which push secret will reconcile
secretStoreRefs: # A list of secret stores to push secrets to
- name: teamb-secret-store
kind: SecretStore
diff --git a/docs/snippets/azkv-pushsecret-certificate.yaml b/docs/snippets/azkv-pushsecret-certificate.yaml
index f4401959345..cb16fb255d3 100644
--- a/docs/snippets/azkv-pushsecret-certificate.yaml
+++ b/docs/snippets/azkv-pushsecret-certificate.yaml
@@ -21,7 +21,7 @@ metadata:
name: pushsecret-example
namespace: default
spec:
- refreshInterval: 10s # Refresh interval for which push secret will reconcile
+ refreshInterval: 1h # Refresh interval for which push secret will reconcile
deletionPolicy: Delete
secretStoreRefs: # A list of secret stores to push secrets to
- name: azure-store
diff --git a/docs/snippets/azkv-pushsecret-key.yaml b/docs/snippets/azkv-pushsecret-key.yaml
index ae954527cd2..e2530b1e330 100644
--- a/docs/snippets/azkv-pushsecret-key.yaml
+++ b/docs/snippets/azkv-pushsecret-key.yaml
@@ -11,7 +11,7 @@ metadata:
name: pushsecret-example
namespace: default
spec:
- refreshInterval: 10s # Refresh interval for which push secret will reconcile
+ refreshInterval: 1h # Refresh interval for which push secret will reconcile
deletionPolicy: Delete
secretStoreRefs: # A list of secret stores to push secrets to
- name: azure-store
diff --git a/docs/snippets/azkv-pushsecret-secret.yaml b/docs/snippets/azkv-pushsecret-secret.yaml
index b52f95386c3..16cc27cd499 100644
--- a/docs/snippets/azkv-pushsecret-secret.yaml
+++ b/docs/snippets/azkv-pushsecret-secret.yaml
@@ -11,7 +11,7 @@ metadata:
name: pushsecret-example
namespace: default
spec:
- refreshInterval: 10s # Refresh interval for which push secret will reconcile
+ refreshInterval: 1h # Refresh interval for which push secret will reconcile
deletionPolicy: Delete
secretStoreRefs: # A list of secret stores to push secrets to
- name: azure-store
@@ -23,4 +23,9 @@ spec:
- match:
secretKey: source-key # Source Kubernetes secret key containing the secret
remoteRef:
- remoteKey: my-azkv-secret-name
\ No newline at end of file
+ remoteKey: my-azkv-secret-name
+ metadata:
+ apiVersion: kubernetes.external-secrets.io/v1alpha1
+ kind: PushSecretMetadata
+ spec:
+ expirationDate: "2024-12-31T23:59:59Z" # Expiration date for the secret in Azure Key Vault
\ No newline at end of file
diff --git a/docs/snippets/beyondtrust-external-secret.yaml b/docs/snippets/beyondtrust-external-secret.yaml
new file mode 100644
index 00000000000..41b8e9e9958
--- /dev/null
+++ b/docs/snippets/beyondtrust-external-secret.yaml
@@ -0,0 +1,16 @@
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: beyondtrust-external-secret
+spec:
+ refreshInterval: 1h
+ secretStoreRef:
+ kind: SecretStore
+ name: secretstore-beyondtrust
+ target:
+ name: my-beyondtrust-secret # name of secret to create in k8s secrets (etcd)
+ creationPolicy: Owner
+ data:
+ - secretKey: secretKey
+ remoteRef:
+ key: system01/managed_account01
\ No newline at end of file
diff --git a/docs/snippets/beyondtrust-secret-store.yaml b/docs/snippets/beyondtrust-secret-store.yaml
new file mode 100644
index 00000000000..ea163a3ea52
--- /dev/null
+++ b/docs/snippets/beyondtrust-secret-store.yaml
@@ -0,0 +1,33 @@
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+ name: secretstore-beyondtrust
+spec:
+ provider:
+ beyondtrust:
+ auth:
+ certificate:
+ secretRef:
+ name: bt-certificate
+ key: ClientCertificate
+ certificateKey:
+ secretRef:
+ name: bt-certificatekey
+ key: ClientCertificateKey
+ clientSecret:
+ secretRef:
+ name: bt-secret
+ key: ClientSecret
+ clientId:
+ secretRef:
+ name: bt-id
+ key: ClientId
+ apiKey:
+ secretRef:
+ name: bt-apikey
+ key: ApiKey
+ server:
+ retrievalType: MANAGED_ACCOUNT
+ verifyCA: true
+ clientTimeOutSeconds: 45
+ apiUrl: https://example.ps-dev.beyondtrustcloud.com:443/BeyondTrust/api/public/v3/
\ No newline at end of file
diff --git a/docs/snippets/bitwarden-cli-deployment.yaml b/docs/snippets/bitwarden-cli-deployment.yaml
index 17259c6eed6..53c129a237b 100644
--- a/docs/snippets/bitwarden-cli-deployment.yaml
+++ b/docs/snippets/bitwarden-cli-deployment.yaml
@@ -54,7 +54,7 @@ spec:
- --post-data=''
initialDelaySeconds: 20
failureThreshold: 3
- timeoutSeconds: 1
+ timeoutSeconds: 10
periodSeconds: 120
readinessProbe:
tcpSocket:
diff --git a/docs/snippets/bitwarden-secret-store.yaml b/docs/snippets/bitwarden-secret-store.yaml
index 05201c1ce96..d398e976fb7 100644
--- a/docs/snippets/bitwarden-secret-store.yaml
+++ b/docs/snippets/bitwarden-secret-store.yaml
@@ -34,4 +34,14 @@ spec:
url: "http://bitwarden-cli:8087/object/item/{{ .remoteRef.key }}"
result:
jsonPath: "$.data.notes"
+---
+apiVersion: external-secrets.io/v1beta1
+kind: ClusterSecretStore
+metadata:
+ name: bitwarden-attachments
+spec:
+ provider:
+ webhook:
+ url: "http://bitwarden-cli:8087/object/attachment/{{ .remoteRef.property }}?itemid={{ .remoteRef.key }}"
+ result: {}
{% endraw %}
diff --git a/docs/snippets/bitwarden-secret.yaml b/docs/snippets/bitwarden-secret.yaml
index 81e279a2327..d91d67ccae3 100644
--- a/docs/snippets/bitwarden-secret.yaml
+++ b/docs/snippets/bitwarden-secret.yaml
@@ -2,11 +2,11 @@
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
- name: my-db-secrets
+ name: my-secrets
namespace: default
spec:
target:
- name: my-db-secrets
+ name: my-secrets
deletionPolicy: Delete
template:
type: Opaque
@@ -23,6 +23,8 @@ spec:
postgresql://{{ .username }}:{{ .password }}@my-postgresql:5432/mydb
service_account_key: |-
{{ .service_account_key }}
+ ssh_pub_key: |-
+ {{ .ssh_pub_key }}
data:
- secretKey: username
sourceRef:
@@ -63,4 +65,12 @@ spec:
kind: ClusterSecretStore # or SecretStore
remoteRef:
key: service_account_key
+ - secretKey: ssh_pub_key
+ sourceRef:
+ storeRef:
+ name: bitwarden-attachments
+ kind: ClusterSecretStore # or SecretStore
+ remoteRef:
+ key: aaaabbbb-cccc-dddd-eeee-000011112222
+ property: id_rsa.pub
{% endraw %}
diff --git a/docs/snippets/bitwarden-secrets-manager-secret-store.yaml b/docs/snippets/bitwarden-secrets-manager-secret-store.yaml
new file mode 100644
index 00000000000..91a01b641ba
--- /dev/null
+++ b/docs/snippets/bitwarden-secrets-manager-secret-store.yaml
@@ -0,0 +1,18 @@
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+ name: bitwarden-secretsmanager
+spec:
+ provider:
+ bitwardensecretsmanager:
+ apiURL: https://api.bitwarden.com
+ identityURL: https://identity.bitwarden.com
+ auth:
+ secretRef:
+ credentials:
+ key: token
+ name: bitwarden-access-token
+ bitwardenServerSDKURL: https://bitwarden-sdk-server.default.svc.cluster.local:9998
+ caBundle: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQ5akNDQXQ2Z0F3SUJBZ0lRS08vM1J1dXR4YWdOeThCdUcyUTJYREFOQmdrcWhraUc5dzBCQVFzRkFEQkQKTVJ3d0dnWURWUVFLRXhObGVIUmxjbTVoYkMxelpXTnlaWFJ6TG1sdk1TTXdJUVlEVlFRREV4cGpaWEowTFcxaApibUZuWlhJdFltbDBkMkZ5WkdWdUxYUnNjekFlRncweU5EQTJNVGt4TXpJd01EUmFGdzB5TkRBNU1UY3hNekl3Ck1EUmFNRU14SERBYUJnTlZCQW9URTJWNGRHVnlibUZzTFhObFkzSmxkSE11YVc4eEl6QWhCZ05WQkFNVEdtTmwKY25RdGJXRnVZV2RsY2kxaWFYUjNZWEprWlc0dGRHeHpNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QQpNSUlCQ2dLQ0FRRUExdlFxaTNCL0NVU01FaUx1b3NkTVdZV25QcWJmQ20xbnZsMWhoUWxjOW1ocDFnSmxDbndjCmE0MmxuTkx0TjNTUmdrZWFNYXppV1RyaDQ5SGdUeTNVQ2xoNDh5RXFvTmJDRUlaL2xxOHNoVzRMd2g0RTdNT08KOVJJMDY2a3JCYllYakZuam1ETjdJV1NLOVVwZjIrOUpLTi9PM3ZWTktLMGZhOERxRkppL3h3VUsyOGRNc05tZAo2NnkreW52TzRFRU51Wm9IRUFieWdrOTQ2cm9yNnNmUkxHZ3ZVYXg5cmd4dEh5TkZqcGkrbjhCUDRlQkRZeGI4CkVsQy93Q0Rza2NBNFF3TXphU3NFbDBwL3gwQm9nTS9nbWJWelNVemhBL2NGdXpMRVJmV0tuanJrbmpoenNFWncKRWlzUmZ6K3MyVnUvcm5YK3pabTBoWTFvSDZYY29mVkhOUUlEQVFBQm80SGxNSUhpTUE0R0ExVWREd0VCL3dRRQpBd0lDcERBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJTeUplK1lnUWZQbWFFOEZKSHowbzZ0CjQzeGh4VENCbndZRFZSMFJCSUdYTUlHVWdqOWxlSFJsY201aGJDMXpaV055WlhSekxXSnBkSGRoY21SbGJpMXoKWkdzdGMyVnlkbVZ5TG1SbFptRjFiSFF1YzNaakxtTnNkWE4wWlhJdWJHOWpZV3lDTG1KcGRIZGhjbVJsYmkxegpaR3N0YzJWeWRtVnlMbVJsWm1GMWJIUXVjM1pqTG1Oc2RYTjBaWEl1Ykc5allXeUNDV3h2WTJGc2FHOXpkSWNFCmZ3QUFBWWNRQUFBQUFBQUFBQUFBQUFBQUFBQUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBdllYUW5ETEgKczc3N3NJN3cwN2NWMVIrTmZvbGRYblp5ejVtQVpDZkc4T2djZDRGdjRYV3lrRG94MzlkUWo0dTJnOWlVcUNwawp2QzJsbUR1UjNrS2kzbjgySTYyQ1BDN1JmZFd3M2hqaFJOV1NKbVBGeGF6NHkrbnMvMDZ3RFBlMmZwRXpPMXIzCmwxTFdZMHBySVlMME1EYTI1c3BUdlZPdWxyeWlnUnJRRGNEbS9hZ3krSEs4RHB3dWlTTEpsdFM0Q1JVa25mb3kKS00rL213VTd4RzNrSnN5ekR0T2dOZDhZeG1lRU44Q05WSk9JalltRk9OWTJrYU51S2ZnMU1aaXArcllPTEFqUgpJdUNxOFhSSTVST2gxOFJKdVlXcVZ6MUkwbXE4aVgwYlo2WG5WRjliZ0ViQ2d3bXZOWkZha3Z4RVhkWmR2N3VmCkYvRm9PTUFlNTY3L0RBPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQ==
+ organizationID: 7c0d21ec-10d9-4972-bdf8-ec52df99cc86
+ projectID: 9c713cd6-728c-437a-a783-252b0773a0bb
diff --git a/docs/snippets/chef-external-secret.yaml b/docs/snippets/chef-external-secret.yaml
index 704a6bb9f48..5069a6076b6 100644
--- a/docs/snippets/chef-external-secret.yaml
+++ b/docs/snippets/chef-external-secret.yaml
@@ -10,7 +10,7 @@ metadata:
labels:
app.kubernetes.io/name: external-secrets
spec:
- refreshInterval: 15m
+ refreshInterval: 1h
secretStoreRef:
name: vivid-clustersecretstore # name of ClusterSecretStore
kind: ClusterSecretStore
diff --git a/docs/snippets/conjur-external-secret-find.yaml b/docs/snippets/conjur-external-secret-find.yaml
index 829a9b838b1..1e64d30ef2f 100644
--- a/docs/snippets/conjur-external-secret-find.yaml
+++ b/docs/snippets/conjur-external-secret-find.yaml
@@ -3,7 +3,7 @@ kind: ExternalSecret
metadata:
name: conjur-find-by-name
spec:
- refreshInterval: 10s
+ refreshInterval: 1h
secretStoreRef:
# This name must match the metadata.name in the `SecretStore`
name: conjur
diff --git a/docs/snippets/conjur-external-secret.yaml b/docs/snippets/conjur-external-secret.yaml
index b4a34585823..5519df2cab7 100644
--- a/docs/snippets/conjur-external-secret.yaml
+++ b/docs/snippets/conjur-external-secret.yaml
@@ -3,7 +3,7 @@ kind: ExternalSecret
metadata:
name: conjur
spec:
- refreshInterval: 10s
+ refreshInterval: 1h
secretStoreRef:
# This name must match the metadata.name in the `SecretStore`
name: conjur
diff --git a/docs/snippets/device42-external-secret.yaml b/docs/snippets/device42-external-secret.yaml
new file mode 100644
index 00000000000..7b717ff9f8e
--- /dev/null
+++ b/docs/snippets/device42-external-secret.yaml
@@ -0,0 +1,16 @@
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: device42-find-by-id
+spec:
+ refreshInterval: 1h
+ secretStoreRef:
+ # This name must match the metadata.name in the `SecretStore`
+ name: device42
+ kind: SecretStore
+ target:
+ name: k8s-secret-to-be-created
+ data:
+ - secretKey: K8S_PASSWORD
+ remoteRef:
+ key: "12345"
diff --git a/docs/snippets/esoctl-tool-push-secret-snippet.yaml b/docs/snippets/esoctl-tool-push-secret-snippet.yaml
new file mode 100644
index 00000000000..ebff6a8f28a
--- /dev/null
+++ b/docs/snippets/esoctl-tool-push-secret-snippet.yaml
@@ -0,0 +1,22 @@
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+metadata:
+ name: example-push-secret-with-template
+spec:
+ refreshInterval: 10s
+ secretStoreRefs:
+ - name: secret-store-name
+ kind: SecretStore
+ selector:
+ secret:
+ name: git-sync-secret
+ template:
+ engineVersion: v2
+ data:
+ token: "{{ .token | toString | upper }} was templated"
+ data:
+ - match:
+ secretKey: token
+ remoteRef:
+ remoteKey: git-sync-secret-copy-templated
+ property: token
\ No newline at end of file
diff --git a/docs/snippets/filtercertchain-template-v2-external-secret.yaml b/docs/snippets/filtercertchain-template-v2-external-secret.yaml
new file mode 100644
index 00000000000..c754822d073
--- /dev/null
+++ b/docs/snippets/filtercertchain-template-v2-external-secret.yaml
@@ -0,0 +1,17 @@
+{% raw %}
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: template
+spec:
+ # ...
+ target:
+ template:
+ type: kubernetes.io/tls
+ engineVersion: v2
+ data:
+ ca.crt: "{{ .mysecret | filterPEM "CERTIFICATE" | filterCertChain "intermediate" }}"
+ tls.crt: "{{ .mysecret | filterPEM "CERTIFICATE" | filterCertChain "leaf" }}"
+ tls.key: "{{ .mysecret | filterPEM "PRIVATE KEY" }}"
+
+{% endraw %}
diff --git a/docs/snippets/full-cluster-external-secret.yaml b/docs/snippets/full-cluster-external-secret.yaml
index 94a6ea1f2da..caef33679b5 100644
--- a/docs/snippets/full-cluster-external-secret.yaml
+++ b/docs/snippets/full-cluster-external-secret.yaml
@@ -9,8 +9,17 @@ spec:
# This is a basic label selector to select the namespaces to deploy ExternalSecrets to.
# you can read more about them here https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#resources-that-support-set-based-requirements
- namespaceSelector:
- matchLabels:
+ # Deprecated: Use namespaceSelectors instead.
+ # namespaceSelector:
+ # matchLabels:
+ # cool: label
+
+ # This is a list of basic label selector to select the namespaces to deploy ExternalSecrets to.
+ # you can read more about them here https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#resources-that-support-set-based-requirements
+ # The list is OR'd together, so if any of the namespaceSelectors match the namespace,
+ # the ExternalSecret will be deployed to that namespace.
+ namespaceSelectors:
+ - matchLabels:
cool: label
# How often the ClusterExternalSecret should reconcile itself
diff --git a/docs/snippets/full-cluster-secret-store.yaml b/docs/snippets/full-cluster-secret-store.yaml
index 9743212bbd4..81a416aabe0 100644
--- a/docs/snippets/full-cluster-secret-store.yaml
+++ b/docs/snippets/full-cluster-secret-store.yaml
@@ -68,11 +68,16 @@ spec:
# AppRole auth: https://www.vaultproject.io/docs/auth/approle
appRole:
path: "approle"
- roleId: "db02de05-fa39-4855-059b-67221c5c2f63"
+ # Instead of referencing the AppRole's ID from the secret, you can also specify it directly
+ # roleId: "db02de05-fa39-4855-059b-67221c5c2f63"
+ roleRef:
+ name: "my-secret"
+ namespace: "secret-admin"
+ key: "vault-role-id"
secretRef:
name: "my-secret"
namespace: "secret-admin"
- key: "vault-token"
+ key: "vault-role-secret"
# Kubernetes auth: https://www.vaultproject.io/docs/auth/kubernetes
kubernetes:
@@ -142,7 +147,7 @@ spec:
# Conditions about namespaces in which the ClusterSecretStore is usable for ExternalSecrets
conditions:
- # Options are namespaceSelector, or namespaces
+ # Options are namespaceSelector, namespaces or namespacesRegex
- namespaceSelector:
matchLabels:
my.namespace.io/some-label: "value" # Only namespaces with that label will work
@@ -151,6 +156,11 @@ spec:
- "namespace-a"
- "namespace-b"
+ # Namespace regexes are useful for policy management or when external tools auto-generate namespaces with prefixes/suffixes
+ - namespaceRegexes:
+ - "namespace-a-.*" # All namespaces prefixed by namespace-a- will work
+ - "namespace-b-.*" # All namespaces prefixed by namespace-b- will work
+
# conditions needs only one of the conditions to meet for the CSS to be usable in the namespace.
status:
diff --git a/docs/snippets/full-external-secret.yaml b/docs/snippets/full-external-secret.yaml
index cd43d2f52c6..c017bc90608 100644
--- a/docs/snippets/full-external-secret.yaml
+++ b/docs/snippets/full-external-secret.yaml
@@ -32,17 +32,18 @@ spec:
# It is immutable
name: application-config
- # Enum with values: 'Owner', 'Merge', or 'None'
- # Default value of 'Owner'
- # Owner creates the secret and sets .metadata.ownerReferences of the resource
- # Merge does not create the secret, but merges in the data fields to the secret
- # None does not create a secret (future use with injector)
- creationPolicy: 'Merge'
-
- # DeletionPolicy defines how/when to delete the Secret in Kubernetes
- # if the provider secret gets deleted.
- # Valid values are Delete, Merge, Retain
- deletionPolicy: "Retain"
+ # Specifies the ExternalSecret ownership details in the created Secret. Options:
+ # - Owner: (default) Creates the Secret and sets .metadata.ownerReferences. If the ExternalSecret is deleted, the Secret will also be deleted.
+ # - Merge: Does not create the Secret but merges data fields into the existing Secret (expects the Secret to already exist).
+ # - Orphan: Creates the Secret but does not set .metadata.ownerReferences. If the Secret already exists, it will be updated.
+ # - None: Does not create or update the Secret (reserved for future use with injector).
+ creationPolicy: Merge
+
+ # Specifies what happens to the Secret when data fields are deleted from the provider (e.g., Vault, AWS Parameter Store). Options:
+ # - Retain: (default) Retains the Secret if all Secret data fields have been deleted from the provider.
+ # - Delete: Removes the Secret if all Secret data fields from the provider are deleted.
+ # - Merge: Removes keys from the Secret but not the Secret itself.
+ deletionPolicy: Retain
# Specify a blueprint for the resulting Kind=Secret
template:
@@ -108,8 +109,6 @@ spec:
target: "rewriting-${1}-with-groups"
- find:
path: path-to-filter
- source: "exp-(.*?)-ression"
- target: "rewriting-${1}-with-groups"
name:
regexp: ".*foobar.*"
tags:
diff --git a/docs/snippets/full-pushsecret-no-key-no-property.yaml b/docs/snippets/full-pushsecret-no-key-no-property.yaml
index 71a811ae5a2..314fbbd48d0 100644
--- a/docs/snippets/full-pushsecret-no-key-no-property.yaml
+++ b/docs/snippets/full-pushsecret-no-key-no-property.yaml
@@ -5,7 +5,7 @@ metadata:
namespace: default # Same of the SecretStores
spec:
deletionPolicy: Delete # the provider' secret will be deleted if the PushSecret is deleted
- refreshInterval: 10s # Refresh interval for which push secret will reconcile
+ refreshInterval: 1h # Refresh interval for which push secret will reconcile
secretStoreRefs: # A list of secret stores to push secrets to
- name: aws-parameterstore
kind: SecretStore
diff --git a/docs/snippets/full-pushsecret-no-key-with-property.yaml b/docs/snippets/full-pushsecret-no-key-with-property.yaml
index 3ed813cccdc..821f977a343 100644
--- a/docs/snippets/full-pushsecret-no-key-with-property.yaml
+++ b/docs/snippets/full-pushsecret-no-key-with-property.yaml
@@ -5,7 +5,7 @@ metadata:
namespace: default # Same of the SecretStores
spec:
deletionPolicy: Delete # the provider' secret will be deleted if the PushSecret is deleted
- refreshInterval: 10s # Refresh interval for which push secret will reconcile
+ refreshInterval: 1h # Refresh interval for which push secret will reconcile
secretStoreRefs: # A list of secret stores to push secrets to
- name: aws-parameterstore
kind: SecretStore
diff --git a/docs/snippets/full-pushsecret.yaml b/docs/snippets/full-pushsecret.yaml
index 7b5a3c87c11..3a678c0e633 100644
--- a/docs/snippets/full-pushsecret.yaml
+++ b/docs/snippets/full-pushsecret.yaml
@@ -7,13 +7,18 @@ metadata:
spec:
updatePolicy: Replace # Policy to overwrite existing secrets in the provider on sync
deletionPolicy: Delete # the provider' secret will be deleted if the PushSecret is deleted
- refreshInterval: 10s # Refresh interval for which push secret will reconcile
+ refreshInterval: 1h # Refresh interval for which push secret will reconcile
secretStoreRefs: # A list of secret stores to push secrets to
- name: aws-parameterstore
kind: SecretStore
selector:
secret:
name: pokedex-credentials # Source Kubernetes secret to be pushed
+ # Alternatively, you can point to a generator that produces values to be pushed
+ generatorRef:
+ apiVersion: external-secrets.io/v1alpha1
+ kind: ECRAuthorizationToken
+ name: prod-registry-credentials
template:
metadata:
annotations: { }
diff --git a/docs/snippets/full-secret-store.yaml b/docs/snippets/full-secret-store.yaml
index f6bd9db2995..4c27343280e 100644
--- a/docs/snippets/full-secret-store.yaml
+++ b/docs/snippets/full-secret-store.yaml
@@ -64,11 +64,11 @@ spec:
key: "cert-key"
# client side related TLS communication, when the Vault server requires mutual authentication
tls:
- clientCert:
+ certSecretRef:
namespace: ...
name: "my-cert-secret"
key: "tls.crt"
- secretRef:
+ keySecretRef:
namespace: ...
name: "my-cert-secret"
key: "tls.key"
@@ -82,10 +82,14 @@ spec:
# AppRole auth: https://www.vaultproject.io/docs/auth/approle
appRole:
path: "approle"
- roleId: "db02de05-fa39-4855-059b-67221c5c2f63"
+ # Instead of referencing the AppRole's ID from the secret, you can also specify it directly
+ # roleId: "db02de05-fa39-4855-059b-67221c5c2f63"
+ roleRef:
+ name: "my-secret"
+ key: "vault-role-id"
secretRef:
name: "my-secret"
- key: "vault-token"
+ key: "vault-role-secret"
# Kubernetes auth: https://www.vaultproject.io/docs/auth/kubernetes
kubernetes:
diff --git a/docs/snippets/gcpsm-tls-externalsecret.yaml b/docs/snippets/gcpsm-tls-externalsecret.yaml
index 69dd2b20f29..ca213c1511f 100644
--- a/docs/snippets/gcpsm-tls-externalsecret.yaml
+++ b/docs/snippets/gcpsm-tls-externalsecret.yaml
@@ -14,8 +14,8 @@ spec:
template:
type: kubernetes.io/tls
data:
- tls.crt: "{{ .mysecret | pkcs12cert | pemCertificate }}"
- tls.key: "{{ .mysecret | pkcs12key | pemPrivateKey }}"
+ tls.crt: "{{ .mysecret | pkcs12cert }}"
+ tls.key: "{{ .mysecret | pkcs12key }}"
data:
# this is a pkcs12 archive that contains
diff --git a/docs/snippets/generator-acr-argocd-helm-repo.yaml b/docs/snippets/generator-acr-argocd-helm-repo.yaml
new file mode 100644
index 00000000000..9fc7b9b4d02
--- /dev/null
+++ b/docs/snippets/generator-acr-argocd-helm-repo.yaml
@@ -0,0 +1,38 @@
+{% raw %}
+apiVersion: generators.external-secrets.io/v1alpha1
+kind: ACRAccessToken
+metadata:
+ name: azurecr
+spec:
+ tenantId: 11111111-2222-3333-4444-111111111111
+ registry: example.azurecr.io
+ auth:
+ managedIdentity:
+ identityId: 11111111-2222-3333-4444-111111111111
+---
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: azurecr-credentials
+spec:
+ dataFrom:
+ - sourceRef:
+ generatorRef:
+ apiVersion: generators.external-secrets.io/v1alpha1
+ kind: ACRAccessToken
+ name: azurecr
+ refreshInterval: 3h
+ target:
+ name: azurecr-credentials
+ template:
+ metadata:
+ labels:
+ argocd.argoproj.io/secret-type: repository
+ data:
+ name: "example.azurecr.io"
+ url: "example.azurecr.io"
+ username: "{{ .username }}"
+ password: "{{ .password }}"
+ enableOCI: "true"
+ type: "helm"
+{% endraw %}
diff --git a/docs/snippets/generator-acr-example.yaml b/docs/snippets/generator-acr-example.yaml
index ba9a2ed9868..06ad89fbaaa 100644
--- a/docs/snippets/generator-acr-example.yaml
+++ b/docs/snippets/generator-acr-example.yaml
@@ -11,7 +11,7 @@ spec:
apiVersion: generators.external-secrets.io/v1alpha1
kind: ACRAccessToken
name: my-azurecr
- refreshInterval: 12h
+ refreshInterval: 3h
target:
name: azurecr-credentials
template:
@@ -22,7 +22,7 @@ spec:
"auths": {
"myregistry.azurecr.io": {
"username": "{{ .username }}",
- "identitytoken": "{{ .password }}"
+ "password": "{{ .password }}"
}
}
}
diff --git a/docs/snippets/generator-acr.yaml b/docs/snippets/generator-acr.yaml
index 49093480a45..13468e1e7f6 100644
--- a/docs/snippets/generator-acr.yaml
+++ b/docs/snippets/generator-acr.yaml
@@ -28,13 +28,13 @@ spec:
name: az-secret
key: clientid
- # option 2:
+ # option 2: use a managed identity Client ID
managedIdentity:
- identityId: "xxxxx"
+ identityId: 11111111-2222-3333-4444-111111111111
# option 3:
workloadIdentity:
# note: you can reference service accounts across namespaces.
serviceAccountRef:
name: "my-service-account"
- audiences: []
\ No newline at end of file
+ audiences: []
diff --git a/docs/snippets/generator-cluster-example.yaml b/docs/snippets/generator-cluster-example.yaml
new file mode 100644
index 00000000000..7262db80bb6
--- /dev/null
+++ b/docs/snippets/generator-cluster-example.yaml
@@ -0,0 +1,14 @@
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: "cluster-secret"
+spec:
+ refreshInterval: "1h"
+ target:
+ name: cluster-secret
+ dataFrom:
+ - sourceRef:
+ generatorRef:
+ apiVersion: generators.external-secrets.io/v1alpha1
+ kind: ClusterGenerator
+ name: "cluster-gen"
diff --git a/docs/snippets/generator-cluster.yaml b/docs/snippets/generator-cluster.yaml
new file mode 100644
index 00000000000..cf3344bbd90
--- /dev/null
+++ b/docs/snippets/generator-cluster.yaml
@@ -0,0 +1,24 @@
+apiVersion: generators.external-secrets.io/v1alpha1
+kind: ClusterGenerator
+metadata:
+ name: cluster-gen
+spec:
+ kind: Password
+ generator:
+# Further specs are available:
+# acrAccessTokenSpec:
+# ecrRAuthorizationTokenSpec:
+# fakeSpec:
+# gcrAccessTokenSpec:
+# githubAccessTokenSpec:
+# stsSessionTokenSpec:
+# uuidSpec:
+# vaultDynamicSecretSpec:
+# webhookSpec:
+ passwordSpec:
+ length: 42
+ digits: 5
+ symbols: 5
+ symbolCharacters: "-_$@"
+ noUpper: false
+ allowRepeat: true
diff --git a/docs/snippets/generator-github-example-basicauth.yaml b/docs/snippets/generator-github-example-basicauth.yaml
new file mode 100644
index 00000000000..f109478c8b1
--- /dev/null
+++ b/docs/snippets/generator-github-example-basicauth.yaml
@@ -0,0 +1,26 @@
+{% raw %}
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: github-auth-template
+spec:
+ dataFrom:
+ - sourceRef:
+ generatorRef:
+ apiVersion: generators.external-secrets.io/v1alpha1
+ kind: GithubAccessToken
+ name: github-auth-token
+ refreshInterval: "15m"
+ target:
+ template:
+ metadata:
+ annotations:
+ tekton.dev/git-0: "https://github.com"
+ type: kubernetes.io/basic-auth
+ engineVersion: v2
+ data:
+ username: "token"
+ password: "{{ .token }}"
+ name: github-auth-template
+
+{% endraw %}
diff --git a/docs/snippets/generator-github-example.yaml b/docs/snippets/generator-github-example.yaml
index d409ea6bc50..d9bd6e20903 100644
--- a/docs/snippets/generator-github-example.yaml
+++ b/docs/snippets/generator-github-example.yaml
@@ -1,3 +1,4 @@
+---
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
diff --git a/docs/snippets/generator-github.yaml b/docs/snippets/generator-github.yaml
index 4a1f00c00fd..478d931bb7b 100644
--- a/docs/snippets/generator-github.yaml
+++ b/docs/snippets/generator-github.yaml
@@ -13,8 +13,12 @@ spec:
appID: "0000000" # (1)
installID: "00000000" # (5)
url: "" # (Default https://api.github.com.)
+ repositories: # Optional
+ - "Hello-World"
+ permissions: # Optional
+ contents: read
auth:
- privatKey:
+ privateKey:
secretRef:
name: github-app-pem # (2)
key: key
diff --git a/docs/snippets/generator-quay-example.yaml b/docs/snippets/generator-quay-example.yaml
new file mode 100644
index 00000000000..2d62d9b4e0a
--- /dev/null
+++ b/docs/snippets/generator-quay-example.yaml
@@ -0,0 +1,29 @@
+{% raw %}
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: quay-credentials
+ namespace: default
+spec:
+ dataFrom:
+ - sourceRef:
+ generatorRef:
+ apiVersion: generators.external-secrets.io/v1alpha1
+ kind: QuayAccessToken
+ name: my-quay-token
+ refreshInterval: 55m # Tokens are good for 1 hour
+ target:
+ name: quay-credentials
+ template:
+ type: kubernetes.io/dockerconfigjson
+ data:
+ .dockerconfigjson: |
+ {
+ "auths": {
+ "{{ .registry }}": {
+ "auth": "{{ .auth }}"
+ }
+ }
+ }
+
+{% endraw %}
diff --git a/docs/snippets/generator-quay.yaml b/docs/snippets/generator-quay.yaml
new file mode 100644
index 00000000000..abc74bc6d51
--- /dev/null
+++ b/docs/snippets/generator-quay.yaml
@@ -0,0 +1,11 @@
+apiVersion: generators.external-secrets.io/v1alpha1
+kind: QuayAccessToken
+metadata:
+ name: my-quay-token
+ namespace: default
+spec:
+ url: "quay.io"
+ robotAccount: "quay_user_or_org+robot_account_name"
+ serviceAccountRef:
+ name: "default"
+ namespace: "default"
diff --git a/docs/snippets/generator-sts-example.yaml b/docs/snippets/generator-sts-example.yaml
new file mode 100644
index 00000000000..36069adff2f
--- /dev/null
+++ b/docs/snippets/generator-sts-example.yaml
@@ -0,0 +1,14 @@
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: "sts-secret"
+spec:
+ refreshInterval: "1h"
+ target:
+ name: sts-secret
+ dataFrom:
+ - sourceRef:
+ generatorRef:
+ apiVersion: generators.external-secrets.io/v1alpha1
+ kind: STSSessionToken
+ name: "sts-gen"
diff --git a/docs/snippets/generator-sts.yaml b/docs/snippets/generator-sts.yaml
new file mode 100644
index 00000000000..7a0813774a6
--- /dev/null
+++ b/docs/snippets/generator-sts.yaml
@@ -0,0 +1,40 @@
+apiVersion: generators.external-secrets.io/v1alpha1
+kind: STSSessionToken
+metadata:
+ name: sts-gen
+spec:
+
+ # specify aws region (mandatory)
+ region: eu-west-1
+
+ # assume role with the given authentication credentials
+ role: "my-role"
+
+ # choose an authentication strategy
+ # if no auth strategy is defined it falls back to using
+ # credentials from the environment of the controller.
+ auth:
+
+ # 1: static credentials
+ # point to a secret that contains static credentials
+ # like AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY
+ secretRef:
+ accessKeyIDSecretRef:
+ name: "my-aws-creds"
+ key: "key-id"
+ secretAccessKeySecretRef:
+ name: "my-aws-creds"
+ key: "access-secret"
+
+ # option 2: IAM Roles for Service Accounts
+ # point to a service account that should be used
+ # that is configured for IAM Roles for Service Accounts (IRSA)
+ jwt:
+ serviceAccountRef:
+ name: "oci-token-sync"
+
+ # optional request parameters for further fine-tuning the Token generation.
+ requestParameters:
+ serialNumber: arn:aws:iam::123456789012:mfa/user
+ sessionDuration: 900
+ tokenCode: "123456"
diff --git a/docs/snippets/generator-uuid-example.yaml b/docs/snippets/generator-uuid-example.yaml
new file mode 100644
index 00000000000..a5cfa07e2af
--- /dev/null
+++ b/docs/snippets/generator-uuid-example.yaml
@@ -0,0 +1,14 @@
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: "uuid"
+spec:
+ refreshInterval: "30m"
+ target:
+ name: uuid-secret
+ dataFrom:
+ - sourceRef:
+ generatorRef:
+ apiVersion: generators.external-secrets.io/v1alpha1
+ kind: UUID
+ name: "my-uuid"
diff --git a/docs/snippets/generator-uuid.yaml b/docs/snippets/generator-uuid.yaml
new file mode 100644
index 00000000000..98d384511f2
--- /dev/null
+++ b/docs/snippets/generator-uuid.yaml
@@ -0,0 +1,5 @@
+apiVersion: generators.external-secrets.io/v1alpha1
+kind: UUID
+metadata:
+ name: my-uuid
+spec: {}
diff --git a/docs/snippets/generator-webhook.yaml b/docs/snippets/generator-webhook.yaml
index eb8352de7fe..ede098a36fc 100644
--- a/docs/snippets/generator-webhook.yaml
+++ b/docs/snippets/generator-webhook.yaml
@@ -20,7 +20,7 @@ kind: Secret
metadata:
name: webhook-credentials
labels:
- generators.external-secrets.io/type: webhook #Needed to allow webhook to use this secret
+ external-secrets.io/type: webhook #Needed to allow webhook to use this secret
data:
username: dGVzdA== # "test"
password: dGVzdA== # "test"
diff --git a/docs/snippets/gitops/deployment-crds.yaml b/docs/snippets/gitops/deployment-crds.yaml
index ace826d1acc..5377a2f3359 100644
--- a/docs/snippets/gitops/deployment-crds.yaml
+++ b/docs/snippets/gitops/deployment-crds.yaml
@@ -1,5 +1,5 @@
---
-apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
+apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: external-secrets-crds
diff --git a/docs/snippets/gitops/deployment-crs.yaml b/docs/snippets/gitops/deployment-crs.yaml
index 2e971b3ed36..7ba7f5dbd77 100644
--- a/docs/snippets/gitops/deployment-crs.yaml
+++ b/docs/snippets/gitops/deployment-crs.yaml
@@ -1,5 +1,5 @@
---
-apiVersion: kustomize.toolkit.fluxcd.io/v1beta2
+apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: external-secrets-crs
diff --git a/docs/snippets/gitops/deployment.yaml b/docs/snippets/gitops/deployment.yaml
index 6caeeb8970a..30aea415ea9 100644
--- a/docs/snippets/gitops/deployment.yaml
+++ b/docs/snippets/gitops/deployment.yaml
@@ -1,21 +1,21 @@
# How to manage values files. Ref: https://fluxcd.io/docs/guides/helmreleases/#refer-to-values-inside-the-chart
# How to inject values: https://fluxcd.io/docs/guides/helmreleases/#cloud-storage
---
-apiVersion: helm.toolkit.fluxcd.io/v2beta1
+apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: external-secrets
namespace: flux-system
spec:
# Override Release name to avoid the pattern Namespace-Release
- # Ref: https://fluxcd.io/docs/components/helm/api/#helm.toolkit.fluxcd.io/v2beta1.HelmRelease
+ # Ref: https://fluxcd.io/flux/components/helm/api/v2/#helm.toolkit.fluxcd.io/v2.HelmRelease
releaseName: external-secrets
targetNamespace: external-secrets
interval: 10m
chart:
spec:
chart: external-secrets
- version: 0.9.4
+ version: 0.10.3
sourceRef:
kind: HelmRepository
name: external-secrets
@@ -23,6 +23,6 @@ spec:
values:
installCRDs: false
- # Ref: https://fluxcd.io/docs/components/helm/api/#helm.toolkit.fluxcd.io/v2beta1.Install
+ # Ref: https://fluxcd.io/flux/components/helm/api/v2/#helm.toolkit.fluxcd.io/v2.Install
install:
createNamespace: true
diff --git a/docs/snippets/gitops/repositories.yaml b/docs/snippets/gitops/repositories.yaml
index 845ee5d3f0a..6daa65b0ac9 100644
--- a/docs/snippets/gitops/repositories.yaml
+++ b/docs/snippets/gitops/repositories.yaml
@@ -1,5 +1,5 @@
# Reference to Helm repository
-apiVersion: source.toolkit.fluxcd.io/v1beta1
+apiVersion: source.toolkit.fluxcd.io/v1
kind: HelmRepository
metadata:
name: external-secrets
@@ -8,7 +8,7 @@ spec:
interval: 10m
url: https://charts.external-secrets.io
---
-apiVersion: source.toolkit.fluxcd.io/v1beta1
+apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: external-secrets
@@ -16,5 +16,5 @@ metadata:
spec:
interval: 10m
ref:
- branch: main
- url: http://github.com/external-secrets/external-secrets
+ tag: v0.10.3
+ url: https://github.com/external-secrets/external-secrets
diff --git a/docs/snippets/ibm-external-secret-by-name.yaml b/docs/snippets/ibm-external-secret-by-name.yaml
index 3cf445a1e1f..863c49a8464 100644
--- a/docs/snippets/ibm-external-secret-by-name.yaml
+++ b/docs/snippets/ibm-external-secret-by-name.yaml
@@ -3,7 +3,7 @@ kind: ExternalSecret
metadata:
name: database-credentials
spec:
- refreshInterval: 60m
+ refreshInterval: 1h
secretStoreRef:
name: ibm-store
kind: SecretStore
diff --git a/docs/snippets/ibm-external-secret.yaml b/docs/snippets/ibm-external-secret.yaml
index ca2c92b1e5a..7912f1f93d9 100644
--- a/docs/snippets/ibm-external-secret.yaml
+++ b/docs/snippets/ibm-external-secret.yaml
@@ -3,7 +3,7 @@ kind: ExternalSecret
metadata:
name: database-credentials
spec:
- refreshInterval: 60m
+ refreshInterval: 1h
secretStoreRef:
name: ibm-store
kind: SecretStore
diff --git a/docs/snippets/infisical-fetch-all-secrets.yaml b/docs/snippets/infisical-fetch-all-secrets.yaml
new file mode 100644
index 00000000000..e0ff9a5c9fc
--- /dev/null
+++ b/docs/snippets/infisical-fetch-all-secrets.yaml
@@ -0,0 +1,16 @@
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: infisical-managed-secrets
+spec:
+ secretStoreRef:
+ kind: SecretStore
+ name: infisical
+
+ target:
+ name: auth-api
+
+ dataFrom:
+ - find:
+ name:
+ regexp: .*
diff --git a/docs/snippets/infisical-fetch-secret.yaml b/docs/snippets/infisical-fetch-secret.yaml
new file mode 100644
index 00000000000..eeb8153e394
--- /dev/null
+++ b/docs/snippets/infisical-fetch-secret.yaml
@@ -0,0 +1,16 @@
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: infisical-managed-secrets
+spec:
+ secretStoreRef:
+ kind: SecretStore
+ name: infisical
+
+ target:
+ name: auth-api
+
+ data:
+ - secretKey: API_KEY
+ remoteRef:
+ key: API_KEY
diff --git a/docs/snippets/infisical-filtered-secrets.yaml b/docs/snippets/infisical-filtered-secrets.yaml
new file mode 100644
index 00000000000..51f7d498505
--- /dev/null
+++ b/docs/snippets/infisical-filtered-secrets.yaml
@@ -0,0 +1,15 @@
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: infisical-managed-secrets
+spec:
+ secretStoreRef:
+ kind: SecretStore
+ name: infisical
+
+ target:
+ name: auth-api
+
+ dataFrom:
+ - find:
+ path: DB_
diff --git a/docs/snippets/infisical-generic-secret-store.yaml b/docs/snippets/infisical-generic-secret-store.yaml
new file mode 100644
index 00000000000..b728b87d6b4
--- /dev/null
+++ b/docs/snippets/infisical-generic-secret-store.yaml
@@ -0,0 +1,27 @@
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+ name: infisical
+spec:
+ provider:
+ infisical:
+ auth:
+ universalAuthCredentials:
+ clientId:
+ key: clientId
+ namespace: default
+ name: universal-auth-credentials
+ clientSecret:
+ key: clientSecret
+ namespace: default
+ name: universal-auth-credentials
+ # Details to pull secrets from
+ secretsScope:
+ projectSlug: first-project-fujo
+ environmentSlug: dev # "dev", "staging", "prod", etc..
+ # optional
+ secretsPath: / # Root is "/"
+ # optional
+ recursive: true # Default is false
+ # optional
+ hostAPI: https://app.infisical.com
diff --git a/docs/snippets/keepersecurity-external-secret.yaml b/docs/snippets/keepersecurity-external-secret.yaml
index fe458477f8d..097e0c4d6d7 100644
--- a/docs/snippets/keepersecurity-external-secret.yaml
+++ b/docs/snippets/keepersecurity-external-secret.yaml
@@ -21,7 +21,7 @@ metadata:
name: regcred
namespace: external-secrets
spec:
- refreshInterval: 1m
+ refreshInterval: 1h
secretStoreRef:
name: keeper
kind: ClusterSecretStore
@@ -49,7 +49,7 @@ metadata:
name: config
namespace: external-secrets
spec:
- refreshInterval: 1m
+ refreshInterval: 1h
secretStoreRef:
name: keeper
kind: ClusterSecretStore
diff --git a/docs/snippets/onboardbase-fetch-all-secrets.yaml b/docs/snippets/onboardbase-fetch-all-secrets.yaml
index 0411c671c12..ffb0fb66524 100644
--- a/docs/snippets/onboardbase-fetch-all-secrets.yaml
+++ b/docs/snippets/onboardbase-fetch-all-secrets.yaml
@@ -3,7 +3,7 @@ kind: ExternalSecret
metadata:
name: service-name-secrets
spec:
- refreshInterval: 10m
+ refreshInterval: 1h
secretStoreRef:
name: onboardbase-external-secret-store
kind: SecretStore
diff --git a/docs/snippets/onboardbase-fetch-secret.yaml b/docs/snippets/onboardbase-fetch-secret.yaml
index 2bb8ca50b25..2fddb0f9043 100644
--- a/docs/snippets/onboardbase-fetch-secret.yaml
+++ b/docs/snippets/onboardbase-fetch-secret.yaml
@@ -3,7 +3,7 @@ kind: ExternalSecret
metadata:
name: service-name-secrets
spec:
- refreshInterval: 10m
+ refreshInterval: 1h
secretStoreRef:
name: onboardbase-external-secret-store
kind: SecretStore
diff --git a/docs/snippets/onboardbase-filtered-secrets.yaml b/docs/snippets/onboardbase-filtered-secrets.yaml
index 0f75a8dca51..5db463ca6e5 100644
--- a/docs/snippets/onboardbase-filtered-secrets.yaml
+++ b/docs/snippets/onboardbase-filtered-secrets.yaml
@@ -3,7 +3,7 @@ kind: ExternalSecret
metadata:
name: service-name-secrets
spec:
- refreshInterval: 10m
+ refreshInterval: 1h
secretStoreRef:
name: onboardbase-external-secret-store
kind: SecretStore
diff --git a/docs/snippets/oracle-external-secret-plaintext.yaml b/docs/snippets/oracle-external-secret-plaintext.yaml
new file mode 100644
index 00000000000..eabe6cb6c93
--- /dev/null
+++ b/docs/snippets/oracle-external-secret-plaintext.yaml
@@ -0,0 +1,16 @@
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+ name: example
+spec:
+ refreshInterval: 0.03m
+ secretStoreRef:
+ kind: SecretStore
+ name: example # Must match SecretStore on the cluster
+ target:
+ name: secret-to-be-created # Name for the secret on the cluster
+ creationPolicy: Owner
+ data:
+ - secretKey: key
+ remoteRef:
+ key: my-eso-secret
diff --git a/docs/snippets/oracle-instance-principal.yaml b/docs/snippets/oracle-instance-principal.yaml
new file mode 100644
index 00000000000..0e2679f5d81
--- /dev/null
+++ b/docs/snippets/oracle-instance-principal.yaml
@@ -0,0 +1,9 @@
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+ name: my-secret-store
+spec:
+ provider:
+ oracle:
+ vault: # The vault OCID
+ principalType: InstancePrincipal
diff --git a/docs/snippets/oracle-principal-type.yaml b/docs/snippets/oracle-principal-type.yaml
new file mode 100644
index 00000000000..386432313d5
--- /dev/null
+++ b/docs/snippets/oracle-principal-type.yaml
@@ -0,0 +1,9 @@
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+ name: my-secret-store
+spec:
+ provider:
+ oracle:
+ # May be UserPrincipal, InstancePrincipal, or Workload
+ principalType:
diff --git a/docs/snippets/oracle-secret-store.yaml b/docs/snippets/oracle-secret-store.yaml
index 8fc9d44b950..94f68a48733 100644
--- a/docs/snippets/oracle-secret-store.yaml
+++ b/docs/snippets/oracle-secret-store.yaml
@@ -1,29 +1,3 @@
-apiVersion: external-secrets.io/v1beta1
-kind: SecretStore
-metadata:
- name: example-instance-principal
-spec:
- provider:
- oracle:
- vault: # The vault OCID
- region: # The vault region
- principalType: InstancePrincipal
-
----
-
-apiVersion: external-secrets.io/v1beta1
-kind: SecretStore
-metadata:
- name: example-workload-identity
-spec:
- provider:
- oracle:
- vault: # The vault OCID
- region: # The vault region
- principalType: Workload
-
----
-
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
diff --git a/docs/snippets/oracle-workload-identity.yaml b/docs/snippets/oracle-workload-identity.yaml
new file mode 100644
index 00000000000..4dc3d0967ef
--- /dev/null
+++ b/docs/snippets/oracle-workload-identity.yaml
@@ -0,0 +1,14 @@
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+ name: my-secret-store
+spec:
+ provider:
+ oracle:
+ vault: # The vault OCID
+ principalType: Workload
+ # If serviceAccountRef is not specified, the Oracle provider will authenticate using the service account token of the External Secrets Operator.
+ serviceAccountRef:
+ # If using a namespaced secret store, this service account must exist in the same namespace as the secret store.
+ # namespace: service account namespace. Required if using ClusterSecretStore, otherwise cannot be specified.
+ name: # The service account name to use for authentication.
diff --git a/docs/snippets/pkcs12-template-v1-external-secret.yaml b/docs/snippets/pkcs12-template-v1-external-secret.yaml
index 04dbf2fa539..7d45400cff1 100644
--- a/docs/snippets/pkcs12-template-v1-external-secret.yaml
+++ b/docs/snippets/pkcs12-template-v1-external-secret.yaml
@@ -13,6 +13,7 @@ spec:
# this is how the Kind=Secret will look like
template:
type: kubernetes.io/tls
+ engineVersion: v1
data:
tls.crt: "{{ .mysecret | pkcs12cert | pemCertificate }}"
tls.key: "{{ .mysecret | pkcs12key | pemPrivateKey }}"
diff --git a/docs/snippets/pushsecret-generator-rotation-example.yaml b/docs/snippets/pushsecret-generator-rotation-example.yaml
new file mode 100644
index 00000000000..8bf62c75f5b
--- /dev/null
+++ b/docs/snippets/pushsecret-generator-rotation-example.yaml
@@ -0,0 +1,33 @@
+{% raw %}
+apiVersion: generators.external-secrets.io/v1alpha1
+kind: Password
+metadata:
+ name: strong-password
+spec:
+ length: 128
+ digits: 5
+ symbols: 5
+ symbolCharacters: "-_$@"
+ noUpper: false
+ allowRepeat: true
+---
+apiVersion: external-secrets.io/v1alpha1
+kind: PushSecret
+metadata:
+ name: pushsecret-example
+spec:
+ refreshInterval: 6h
+ secretStoreRefs:
+ - name: aws-parameter-store
+ kind: SecretStore
+ selector:
+ generatorRef:
+ apiVersion: generators.external-secrets.io/v1alpha1
+ kind: Password
+ name: strong-password
+ data:
+ - match:
+ secretKey: password # property in the generator output
+ remoteRef:
+ remoteKey: prod/myql/password
+{% endraw %}
diff --git a/docs/snippets/vault-anchore-engine-access-credentials-external-secret.yaml b/docs/snippets/vault-anchore-engine-access-credentials-external-secret.yaml
index 613c2410ceb..435ff59a3ef 100644
--- a/docs/snippets/vault-anchore-engine-access-credentials-external-secret.yaml
+++ b/docs/snippets/vault-anchore-engine-access-credentials-external-secret.yaml
@@ -5,7 +5,7 @@ metadata:
name: anchore-access-credentials
namespace: security
spec:
- refreshInterval: 1m
+ refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
diff --git a/docs/snippets/vault-jenkins-credential-github-ssh-access-external-secret.yaml b/docs/snippets/vault-jenkins-credential-github-ssh-access-external-secret.yaml
index 9b5d3e36806..1784bd2a23b 100644
--- a/docs/snippets/vault-jenkins-credential-github-ssh-access-external-secret.yaml
+++ b/docs/snippets/vault-jenkins-credential-github-ssh-access-external-secret.yaml
@@ -5,7 +5,7 @@ metadata:
name: github-ssh-access
namespace: ci
spec:
- refreshInterval: 1m
+ refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
diff --git a/docs/snippets/vault-jenkins-credential-harbor-chart-robot-external-secret.yaml b/docs/snippets/vault-jenkins-credential-harbor-chart-robot-external-secret.yaml
index 4622e2ba8b9..cca29c407b7 100644
--- a/docs/snippets/vault-jenkins-credential-harbor-chart-robot-external-secret.yaml
+++ b/docs/snippets/vault-jenkins-credential-harbor-chart-robot-external-secret.yaml
@@ -5,7 +5,7 @@ metadata:
name: harbor-chart-robot
namespace: ci
spec:
- refreshInterval: 1m
+ refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
diff --git a/docs/snippets/vault-jenkins-credential-sonarqube-api-token-external-secret.yaml b/docs/snippets/vault-jenkins-credential-sonarqube-api-token-external-secret.yaml
index 1915a98c733..ca53fc2d0e4 100644
--- a/docs/snippets/vault-jenkins-credential-sonarqube-api-token-external-secret.yaml
+++ b/docs/snippets/vault-jenkins-credential-sonarqube-api-token-external-secret.yaml
@@ -5,7 +5,7 @@ metadata:
name: sonarqube-api-token
namespace: ci
spec:
- refreshInterval: 1m
+ refreshInterval: 1h
secretStoreRef:
name: vault-backend
kind: ClusterSecretStore
diff --git a/docs/snippets/vault-pushsecret.yaml b/docs/snippets/vault-pushsecret.yaml
index f7801ceb554..483f1e86918 100644
--- a/docs/snippets/vault-pushsecret.yaml
+++ b/docs/snippets/vault-pushsecret.yaml
@@ -13,7 +13,7 @@ metadata:
name: pushsecret-example
namespace: default
spec:
- refreshInterval: 10s
+ refreshInterval: 1h
secretStoreRefs:
- name: vault-secretstore
kind: SecretStore
diff --git a/docs/spec.md b/docs/spec.md
deleted file mode 100644
index 08a0ce49598..00000000000
--- a/docs/spec.md
+++ /dev/null
@@ -1,6721 +0,0 @@
-Packages:
-
-external-secrets.io/v1beta1
-
-
Package v1beta1 contains resources for external-secrets
-
-Resource Types:
-
-AWSAuth
-
-
-(Appears on:
-AWSProvider )
-
-
-
AWSAuth tells the controller how to do authentication with aws.
-Only one of secretRef or jwt can be specified.
-if none is specified the controller will load credentials using the aws sdk defaults.
-
-
-AWSAuthSecretRef
-
-
-(Appears on:
-AWSAuth )
-
-
-
AWSAuthSecretRef holds secret references for AWS credentials
-both AccessKeyID and SecretAccessKey must be defined in order to properly authenticate.
-
-
-
-
-Field
-Description
-
-
-
-
-
-accessKeyIDSecretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-The AccessKeyID is used for authentication
-
-
-
-
-secretAccessKeySecretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-The SecretAccessKey is used for authentication
-
-
-
-
-sessionTokenSecretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-The SessionToken used for authentication
-This must be defined if AccessKeyID and SecretAccessKey are temporary credentials
-see: https://docs.aws.amazon.com/IAM/latest/UserGuide/id_credentials_temp_use-resources.html
-
-
-
-
-AWSJWTAuth
-
-
-(Appears on:
-AWSAuth )
-
-
-
Authenticate against AWS using service account tokens.
-
-
-
-
-Field
-Description
-
-
-
-
-
-serviceAccountRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.ServiceAccountSelector
-
-
-
-
-
-
-
-AWSProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
AWSProvider configures a store to sync secrets with AWS.
-
-
-AWSServiceType
-(string
alias)
-
-(Appears on:
-AWSProvider )
-
-
-
AWSServiceType is a enum that defines the service/API that is used to fetch the secrets.
-
-
-AlibabaAuth
-
-
-(Appears on:
-AlibabaProvider )
-
-
-
AlibabaAuth contains a secretRef for credentials.
-
-
-AlibabaAuthSecretRef
-
-
-(Appears on:
-AlibabaAuth )
-
-
-
AlibabaAuthSecretRef holds secret references for Alibaba credentials.
-
-
-
-
-Field
-Description
-
-
-
-
-
-accessKeyIDSecretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-The AccessKeyID is used for authentication
-
-
-
-
-accessKeySecretSecretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-The AccessKeySecret is used for authentication
-
-
-
-
-AlibabaProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
AlibabaProvider configures a store to sync secrets using the Alibaba Secret Manager provider.
-
-
-
-
-Field
-Description
-
-
-
-
-
-auth
-
-
-AlibabaAuth
-
-
-
-
-
-
-
-
-endpoint
-
-string
-
-
-
-(Optional)
-
-
-
-
-regionID
-
-string
-
-
-
-Alibaba Region to be used for the provider
-
-
-
-
-AzureKVAuth
-
-
-(Appears on:
-AkeylessProvider )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-secretRef
-
-
-AkeylessAuthSecretRef
-
-
-
-
-(Optional)
-Reference to a Secret that contains the details
-to authenticate with Akeyless.
-
-
-
-
-kubernetesAuth
-
-
-AkeylessKubernetesAuth
-
-
-
-
-(Optional)
-Kubernetes authenticates with Akeyless by passing the ServiceAccount
-token stored in the named Secret resource.
-
-
-
-
-AkeylessAuthSecretRef
-
-
-(Appears on:
-AkeylessAuth )
-
-
-
AkeylessAuthSecretRef
-AKEYLESS_ACCESS_TYPE_PARAM: AZURE_OBJ_ID OR GCP_AUDIENCE OR ACCESS_KEY OR KUB_CONFIG_NAME.
-
-
-
-
-Field
-Description
-
-
-
-
-
-accessID
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-The SecretAccessID is used for authentication
-
-
-
-
-accessType
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-
-
-
-
-accessTypeParam
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-
-
-
-
-CAProvider
-
-
-(Appears on:
-VaultProvider )
-
-
-
Defines a location to fetch the cert for the vault provider from.
-
-
-
-
-Field
-Description
-
-
-
-
-
-type
-
-
-CAProviderType
-
-
-
-
-The type of provider to use such as “Secret”, or “ConfigMap”.
-
-
-
-
-name
-
-string
-
-
-
-The name of the object located at the provider type.
-
-
-
-
-key
-
-string
-
-
-
-The key the value inside of the provider type to use, only used with “Secret” type
-
-
-
-
-namespace
-
-string
-
-
-
-The namespace the Provider type is in.
-
-
-
-
-CAProviderType
-(string
alias)
-
-(Appears on:
-CAProvider )
-
-
-
-
-
-
-Value
-Description
-
-
-"ConfigMap"
-
-"Secret"
-
-
-
-ClusterSecretStore
-
-
-(Appears on:
-AkeylessAuth )
-
-
-
Authenticate with Kubernetes ServiceAccount token stored.
-
-
-
-
-Field
-Description
-
-
-
-
-
-accessID
-
-string
-
-
-
-the Akeyless Kubernetes auth-method access-id
-
-
-
-
-k8sConfName
-
-string
-
-
-
-Kubernetes-auth configuration name in Akeyless-Gateway
-
-
-
-
-serviceAccountRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.ServiceAccountSelector
-
-
-
-(Optional)
-Optional service account field containing the name of a kubernetes ServiceAccount.
-If the service account is specified, the service account secret token JWT will be used
-for authenticating with Akeyless. If the service account selector is not supplied,
-the secretRef will be used instead.
-
-
-
-
-secretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-(Optional)
-Optional secret field containing a Kubernetes ServiceAccount JWT used
-for authenticating with Akeyless. If a name is specified without a key,
-token
is the default. If one is not specified, the one bound to
-the controller will be used.
-
-
-
-
-AkeylessProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
AkeylessProvider Configures an store to sync secrets using Akeyless KV.
-
-
-
-
-Field
-Description
-
-
-
-
-
-akeylessGWApiURL
-
-string
-
-
-
-Akeyless GW API Url from which the secrets to be fetched from.
-
-
-
-
-authSecretRef
-
-
-AkeylessAuth
-
-
-
-
-Auth configures how the operator authenticates with Akeyless.
-
-
-
-
-AlibabaAuth
-
-
-(Appears on:
-AlibabaProvider )
-
-
-
AlibabaAuth contains a secretRef for credentials.
-
-
-AlibabaAuthSecretRef
-
-
-(Appears on:
-AlibabaAuth )
-
-
-
AlibabaAuthSecretRef holds secret references for Alibaba credentials.
-
-
-
-
-Field
-Description
-
-
-
-
-
-accessKeyIDSecretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-The AccessKeyID is used for authentication
-
-
-
-
-accessKeySecretSecretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-The AccessKeySecret is used for authentication
-
-
-
-
-AlibabaProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
AlibabaProvider configures a store to sync secrets using the Alibaba Secret Manager provider.
-
-
-
-
-Field
-Description
-
-
-
-
-
-auth
-
-
-AlibabaAuth
-
-
-
-
-
-
-
-
-endpoint
-
-string
-
-
-
-(Optional)
-
-
-
-
-regionID
-
-string
-
-
-
-Alibaba Region to be used for the provider
-
-
-
-
-AzureAuthType
-(string
alias)
-
-(Appears on:
-AzureKVProvider )
-
-
-
AuthType describes how to authenticate to the Azure Keyvault
-Only one of the following auth types may be specified.
-If none of the following auth type is specified, the default one
-is ServicePrincipal.
-
-
-
-
-Value
-Description
-
-
-"ManagedIdentity"
-Using Managed Identity to authenticate. Used with aad-pod-identity installed in the cluster.
-
-"ServicePrincipal"
-Using service principal to authenticate, which needs a tenantId, a clientId and a clientSecret.
-
-"WorkloadIdentity"
-Using Workload Identity service accounts to authenticate.
-
-
-
-AzureEnvironmentType
-(string
alias)
-
-(Appears on:
-AzureKVProvider )
-
-
-
AzureEnvironmentType specifies the Azure cloud environment endpoints to use for
-connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint.
-The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152
-PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud
-
-
-
-
-Value
-Description
-
-
-"ChinaCloud"
-
-"GermanCloud"
-
-"PublicCloud"
-
-"USGovernmentCloud"
-
-
-
-AzureKVAuth
-
-
-(Appears on:
-AzureKVProvider )
-
-
-
Configuration used to authenticate with Azure.
-
-
-
-
-Field
-Description
-
-
-
-
-
-clientId
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-(Optional)
-The Azure clientId of the service principle used for authentication.
-
-
-
-
-clientSecret
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-(Optional)
-The Azure ClientSecret of the service principle used for authentication.
-
-
-
-
-AzureKVProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
Configures an store to sync secrets using Azure KV.
-
-
-
-
-Field
-Description
-
-
-
-
-
-authType
-
-
-AzureAuthType
-
-
-
-
-(Optional)
-Auth type defines how to authenticate to the keyvault service.
-Valid values are:
-- “ServicePrincipal” (default): Using a service principal (tenantId, clientId, clientSecret)
-- “ManagedIdentity”: Using Managed Identity assigned to the pod (see aad-pod-identity)
-
-
-
-
-vaultUrl
-
-string
-
-
-
-Vault Url from which the secrets to be fetched from.
-
-
-
-
-tenantId
-
-string
-
-
-
-(Optional)
-TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type.
-
-
-
-
-environmentType
-
-
-AzureEnvironmentType
-
-
-
-
-EnvironmentType specifies the Azure cloud environment endpoints to use for
-connecting and authenticating with Azure. By default it points to the public cloud AAD endpoint.
-The following endpoints are available, also see here: https://github.com/Azure/go-autorest/blob/main/autorest/azure/environments.go#L152
-PublicCloud, USGovernmentCloud, ChinaCloud, GermanCloud
-
-
-
-
-authSecretRef
-
-
-AzureKVAuth
-
-
-
-
-(Optional)
-Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type.
-
-
-
-
-serviceAccountRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.ServiceAccountSelector
-
-
-
-(Optional)
-ServiceAccountRef specified the service account
-that should be used when authenticating with WorkloadIdentity.
-
-
-
-
-identityId
-
-string
-
-
-
-(Optional)
-If multiple Managed Identity is assigned to the pod, you can select the one to be used
-
-
-
-
-CAProvider
-
-
-(Appears on:
-KubernetesServer ,
-VaultProvider )
-
-
-
Used to provide custom certificate authority (CA) certificates
-for a secret store. The CAProvider points to a Secret or ConfigMap resource
-that contains a PEM-encoded certificate.
-
-
-
-
-Field
-Description
-
-
-
-
-
-type
-
-
-CAProviderType
-
-
-
-
-The type of provider to use such as “Secret”, or “ConfigMap”.
-
-
-
-
-name
-
-string
-
-
-
-The name of the object located at the provider type.
-
-
-
-
-key
-
-string
-
-
-
-The key where the CA certificate can be found in the Secret or ConfigMap.
-
-
-
-
-namespace
-
-string
-
-
-
-(Optional)
-The namespace the Provider type is in.
-Can only be defined when used in a ClusterSecretStore.
-
-
-
-
-CAProviderType
-(string
alias)
-
-(Appears on:
-CAProvider )
-
-
-
-
-
-
-Value
-Description
-
-
-"ConfigMap"
-
-"Secret"
-
-
-
-CertAuth
-
-
-(Appears on:
-KubernetesAuth )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-clientCert
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-
-
-
-
-clientKey
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-
-
-
-
-ClusterExternalSecret
-
-
-
ClusterExternalSecret is the Schema for the clusterexternalsecrets API.
-
-
-ClusterExternalSecretConditionType
-(string
alias)
-
-(Appears on:
-ClusterExternalSecretStatusCondition )
-
-
-
-
-
-
-Value
-Description
-
-
-"NotReady"
-
-"PartiallyReady"
-
-"Ready"
-
-
-
-ClusterExternalSecretNamespaceFailure
-
-
-(Appears on:
-ClusterExternalSecretStatus )
-
-
-
ClusterExternalSecretNamespaceFailure represents a failed namespace deployment and it’s reason.
-
-
-
-
-Field
-Description
-
-
-
-
-
-namespace
-
-string
-
-
-
-Namespace is the namespace that failed when trying to apply an ExternalSecret
-
-
-
-
-reason
-
-string
-
-
-
-(Optional)
-Reason is why the ExternalSecret failed to apply to the namespace
-
-
-
-
-immutable
-
-bool
-
-
-
-(Optional)
-Immutable defines if the final secret will be immutable
-
-
-
-
-ClusterExternalSecretSpec
-
-
-(Appears on:
-ClusterExternalSecret )
-
-
-
ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret.
-
-
-
-
-Field
-Description
-
-
-
-
-
-externalSecretSpec
-
-
-ExternalSecretSpec
-
-
-
-
-The spec for the ExternalSecrets to be created
-
-
-
-
-externalSecretName
-
-string
-
-
-
-(Optional)
-The name of the external secrets to be created defaults to the name of the ClusterExternalSecret
-
-
-
-
-namespaceSelector
-
-
-Kubernetes meta/v1.LabelSelector
-
-
-
-
-The labels to select by to find the Namespaces to create the ExternalSecrets in.
-
-
-
-
-refreshTime
-
-
-Kubernetes meta/v1.Duration
-
-
-
-
-The time in which the controller should reconcile it’s objects and recheck namespaces for labels.
-
-
-
-
-ClusterExternalSecretStatus
-
-
-(Appears on:
-ClusterExternalSecret )
-
-
-
ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret.
-
-
-
-
-Field
-Description
-
-
-
-
-
-failedNamespaces
-
-
-[]ClusterExternalSecretNamespaceFailure
-
-
-
-
-(Optional)
-Failed namespaces are the namespaces that failed to apply an ExternalSecret
-
-
-
-
-provisionedNamespaces
-
-[]string
-
-
-
-(Optional)
-ProvisionedNamespaces are the namespaces where the ClusterExternalSecret has secrets
-
-
-
-
-conditions
-
-
-[]ClusterExternalSecretStatusCondition
-
-
-
-
-(Optional)
-
-
-
-
-ClusterExternalSecretStatusCondition
-
-
-(Appears on:
-ClusterExternalSecretStatus )
-
-
-
-
-ClusterSecretStore
-
-
-
ClusterSecretStore represents a secure external location for storing secrets, which can be referenced as part of storeRef
fields.
-
-
-
-
-Field
-Description
-
-
-
-
-
-metadata
-
-
-Kubernetes meta/v1.ObjectMeta
-
-
-
-
-Refer to the Kubernetes API documentation for the fields of the
-metadata
field.
-
-
-
-
-spec
-
-
-SecretStoreSpec
-
-
-
-
-
-
-
-
-
-controller
-
-string
-
-
-
-(Optional)
-Used to select the correct KES controller (think: ingress.ingressClassName)
-The KES controller is instantiated with a specific controller name and filters ES based on this property
-
-
-
-
-provider
-
-
-SecretStoreProvider
-
-
-
-
-Used to configure the provider. Only one provider may be set
-
-
-
-
-retrySettings
-
-
-SecretStoreRetrySettings
-
-
-
-
-(Optional)
-Used to configure http retries if failed
-
-
-
-
-refreshInterval
-
-int
-
-
-
-(Optional)
-Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config.
-
-
-
-
-conditions
-
-
-[]ClusterSecretStoreCondition
-
-
-
-
-(Optional)
-Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore
-
-
-
-
-
-
-
-status
-
-
-SecretStoreStatus
-
-
-
-
-
-
-
-
-ClusterSecretStoreCondition
-
-
-(Appears on:
-SecretStoreSpec )
-
-
-
ClusterSecretStoreCondition describes a condition by which to choose namespaces to process ExternalSecrets in
-for a ClusterSecretStore instance.
-
-
-
-
-Field
-Description
-
-
-
-
-
-namespaceSelector
-
-
-Kubernetes meta/v1.LabelSelector
-
-
-
-
-(Optional)
-Choose namespace using a labelSelector
-
-
-
-
-namespaces
-
-[]string
-
-
-
-Choose namespaces by name
-
-
-
-
-DopplerAuth
-
-
-(Appears on:
-DopplerProvider )
-
-
-
-
-DopplerAuthSecretRef
-
-
-(Appears on:
-DopplerAuth )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-dopplerToken
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-The DopplerToken is used for authentication.
-See https://docs.doppler.com/reference/api#authentication for auth token types.
-The Key attribute defaults to dopplerToken if not specified.
-
-
-
-
-DopplerProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
DopplerProvider configures a store to sync secrets using the Doppler provider.
-Project and Config are required if not using a Service Token.
-
-
-
-
-Field
-Description
-
-
-
-
-
-auth
-
-
-DopplerAuth
-
-
-
-
-Auth configures how the Operator authenticates with the Doppler API
-
-
-
-
-project
-
-string
-
-
-
-(Optional)
-Doppler project (required if not using a Service Token)
-
-
-
-
-config
-
-string
-
-
-
-(Optional)
-Doppler config (required if not using a Service Token)
-
-
-
-
-nameTransformer
-
-string
-
-
-
-(Optional)
-Environment variable compatible name transforms that change secret names to a different format
-
-
-
-
-format
-
-string
-
-
-
-(Optional)
-Format enables the downloading of secrets as a file (string)
-
-
-
-
-OracleAuth
-
-
-(Appears on:
-OracleProvider )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-secretRef
-
-
-OracleSecretRef
-
-
-
-
-SecretRef to pass through sensitive information.
-
-
-
-
-OracleProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
Configures an store to sync secrets using a Oracle Vault
-backend.
-
-
-
-
-Field
-Description
-
-
-
-
-
-auth
-
-
-OracleAuth
-
-
-
-
-Auth configures how secret-manager authenticates with the Oracle Vault.
-
-
-
-
-user
-
-string
-
-
-
-User is an access OCID specific to the account.
-
-
-
-
-tenancy
-
-string
-
-
-
-projectID is an access token specific to the secret.
-
-
-
-
-region
-
-string
-
-
-
-projectID is an access token specific to the secret.
-
-
-
-
-OracleSecretRef
-
-
-(Appears on:
-OracleAuth )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-privatekey
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-The Access Token is used for authentication
-
-
-
-
-fingerprint
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-projectID is an access token specific to the secret.
-
-
-
-
-PasswordDepotAuth
-
-
-(Appears on:
-PasswordDepotProvider )
-
-
-
-
-PasswordDepotProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
Configures a store to sync secrets with a Password Depot instance.
-
-
-
-
-Field
-Description
-
-
-
-
-
-host
-
-string
-
-
-
-URL configures the Password Depot instance URL.
-
-
-
-
-database
-
-string
-
-
-
-Database to use as source
-
-
-
-
-auth
-
-
-PasswordDepotAuth
-
-
-
-
-Auth configures how secret-manager authenticates with a Password Depot instance.
-
-
-
-
-PasswordDepotSecretRef
-
-
-(Appears on:
-PasswordDepotAuth )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-credentials
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-Username / Password is used for authentication.
-
-
-
-
-SecretStore
-
-
-
ExternalSecret is the Schema for the external-secrets API.
-
-
-
-
-Field
-Description
-
-
-
-
-
-metadata
-
-
-Kubernetes meta/v1.ObjectMeta
-
-
-
-
-Refer to the Kubernetes API documentation for the fields of the
-metadata
field.
-
-
-
-
-spec
-
-
-ExternalSecretSpec
-
-
-
-
-
-
-
-
-
-secretStoreRef
-
-
-SecretStoreRef
-
-
-
-
-(Optional)
-
-
-
-
-target
-
-
-ExternalSecretTarget
-
-
-
-
-(Optional)
-
-
-
-
-refreshInterval
-
-
-Kubernetes meta/v1.Duration
-
-
-
-
-RefreshInterval is the amount of time before the values are read again from the SecretStore provider
-Valid time units are “ns”, “us” (or “µs”), “ms”, “s”, “m”, “h”
-May be set to zero to fetch and create it once. Defaults to 1h.
-
-
-
-
-data
-
-
-[]ExternalSecretData
-
-
-
-
-(Optional)
-Data defines the connection between the Kubernetes Secret keys and the Provider data
-
-
-
-
-dataFrom
-
-
-[]ExternalSecretDataFromRemoteRef
-
-
-
-
-(Optional)
-DataFrom is used to fetch all properties from a specific Provider data
-If multiple entries are specified, the Secret keys are merged in the specified order
-
-
-
-
-
-
-
-status
-
-
-ExternalSecretStatus
-
-
-
-
-
-
-
-
-ExternalSecretConditionType
-(string
alias)
-
-(Appears on:
-ExternalSecretStatusCondition )
-
-
-
-
-
-
-Value
-Description
-
-
-"Deleted"
-
-"Ready"
-
-
-
-ExternalSecretConversionStrategy
-(string
alias)
-
-(Appears on:
-ExternalSecretDataRemoteRef ,
-ExternalSecretFind )
-
-
-
-
-
-
-Value
-Description
-
-
-"Default"
-
-"Unicode"
-
-
-
-ExternalSecretCreationPolicy
-(string
alias)
-
-(Appears on:
-ExternalSecretTarget )
-
-
-
ExternalSecretCreationPolicy defines rules on how to create the resulting Secret.
-
-
-
-
-Value
-Description
-
-
-"Merge"
-Merge does not create the Secret, but merges the data fields to the Secret.
-
-"None"
-None does not create a Secret (future use with injector).
-
-"Orphan"
-Orphan creates the Secret and does not set the ownerReference.
-I.e. it will be orphaned after the deletion of the ExternalSecret.
-
-"Owner"
-Owner creates the Secret and sets .metadata.ownerReferences to the ExternalSecret resource.
-
-
-
-ExternalSecretData
-
-
-(Appears on:
-ExternalSecretSpec )
-
-
-
ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.) and the Provider data.
-
-
-
-
-Field
-Description
-
-
-
-
-
-secretKey
-
-string
-
-
-
-SecretKey defines the key in which the controller stores
-the value. This is the key in the Kind=Secret
-
-
-
-
-remoteRef
-
-
-ExternalSecretDataRemoteRef
-
-
-
-
-RemoteRef points to the remote secret and defines
-which secret (version/property/..) to fetch.
-
-
-
-
-sourceRef
-
-
-SourceRef
-
-
-
-
-SourceRef allows you to override the source
-from which the value will pulled from.
-
-
-
-
-ExternalSecretDataFromRemoteRef
-
-
-(Appears on:
-ExternalSecretSpec )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-extract
-
-
-ExternalSecretDataRemoteRef
-
-
-
-
-(Optional)
-Used to extract multiple key/value pairs from one secret
-Note: Extract does not support sourceRef.Generator or sourceRef.GeneratorRef.
-
-
-
-
-find
-
-
-ExternalSecretFind
-
-
-
-
-(Optional)
-Used to find secrets based on tags or regular expressions
-Note: Find does not support sourceRef.Generator or sourceRef.GeneratorRef.
-
-
-
-
-rewrite
-
-
-[]ExternalSecretRewrite
-
-
-
-
-(Optional)
-Used to rewrite secret Keys after getting them from the secret Provider
-Multiple Rewrite operations can be provided. They are applied in a layered order (first to last)
-
-
-
-
-sourceRef
-
-
-SourceRef
-
-
-
-
-SourceRef points to a store or generator
-which contains secret values ready to use.
-Use this in combination with Extract or Find pull values out of
-a specific SecretStore.
-When sourceRef points to a generator Extract or Find is not supported.
-The generator returns a static map of values
-
-
-
-
-ExternalSecretDataRemoteRef
-
-
-(Appears on:
-ExternalSecretData ,
-ExternalSecretDataFromRemoteRef )
-
-
-
ExternalSecretDataRemoteRef defines Provider data location.
-
-
-
-
-Field
-Description
-
-
-
-
-
-key
-
-string
-
-
-
-Key is the key used in the Provider, mandatory
-
-
-
-
-metadataPolicy
-
-
-ExternalSecretMetadataPolicy
-
-
-
-
-(Optional)
-Policy for fetching tags/labels from provider secrets, possible options are Fetch, None. Defaults to None
-
-
-
-
-property
-
-string
-
-
-
-(Optional)
-Used to select a specific property of the Provider value (if a map), if supported
-
-
-
-
-version
-
-string
-
-
-
-(Optional)
-Used to select a specific version of the Provider value, if supported
-
-
-
-
-conversionStrategy
-
-
-ExternalSecretConversionStrategy
-
-
-
-
-(Optional)
-Used to define a conversion Strategy
-
-
-
-
-decodingStrategy
-
-
-ExternalSecretDecodingStrategy
-
-
-
-
-(Optional)
-Used to define a decoding Strategy
-
-
-
-
-ExternalSecretDecodingStrategy
-(string
alias)
-
-(Appears on:
-ExternalSecretDataRemoteRef ,
-ExternalSecretFind )
-
-
-
-
-
-
-Value
-Description
-
-
-"Auto"
-
-"Base64"
-
-"Base64URL"
-
-"None"
-
-
-
-ExternalSecretDeletionPolicy
-(string
alias)
-
-(Appears on:
-ExternalSecretTarget )
-
-
-
ExternalSecretDeletionPolicy defines rules on how to delete the resulting Secret.
-
-
-
-
-Value
-Description
-
-
-"Delete"
-Delete deletes the secret if all provider secrets are deleted.
-If a secret gets deleted on the provider side and is not accessible
-anymore this is not considered an error and the ExternalSecret
-does not go into SecretSyncedError status.
-
-"Merge"
-Merge removes keys in the secret, but not the secret itself.
-If a secret gets deleted on the provider side and is not accessible
-anymore this is not considered an error and the ExternalSecret
-does not go into SecretSyncedError status.
-
-"Retain"
-Retain will retain the secret if all provider secrets have been deleted.
-If a provider secret does not exist the ExternalSecret gets into the
-SecretSyncedError status.
-
-
-
-ExternalSecretFind
-
-
-(Appears on:
-ExternalSecretDataFromRemoteRef )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-path
-
-string
-
-
-
-(Optional)
-A root path to start the find operations.
-
-
-
-
-name
-
-
-FindName
-
-
-
-
-(Optional)
-Finds secrets based on the name.
-
-
-
-
-tags
-
-map[string]string
-
-
-
-(Optional)
-Find secrets based on tags.
-
-
-
-
-conversionStrategy
-
-
-ExternalSecretConversionStrategy
-
-
-
-
-(Optional)
-Used to define a conversion Strategy
-
-
-
-
-decodingStrategy
-
-
-ExternalSecretDecodingStrategy
-
-
-
-
-(Optional)
-Used to define a decoding Strategy
-
-
-
-
-
-
-(Appears on:
-ExternalSecretDataRemoteRef )
-
-
-
-
-
-
-Value
-Description
-
-
-"Fetch"
-
-"None"
-
-
-
-ExternalSecretRewrite
-
-
-(Appears on:
-ExternalSecretDataFromRemoteRef )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-regexp
-
-
-ExternalSecretRewriteRegexp
-
-
-
-
-(Optional)
-Used to rewrite with regular expressions.
-The resulting key will be the output of a regexp.ReplaceAll operation.
-
-
-
-
-ExternalSecretRewriteRegexp
-
-
-(Appears on:
-ExternalSecretRewrite )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-source
-
-string
-
-
-
-Used to define the regular expression of a re.Compiler.
-
-
-
-
-target
-
-string
-
-
-
-Used to define the target pattern of a ReplaceAll operation.
-
-
-
-
-ExternalSecretSpec
-
-
-(Appears on:
-ClusterExternalSecretSpec ,
-ExternalSecret )
-
-
-
ExternalSecretSpec defines the desired state of ExternalSecret.
-
-
-
-
-Field
-Description
-
-
-
-
-
-secretStoreRef
-
-
-SecretStoreRef
-
-
-
-
-(Optional)
-
-
-
-
-target
-
-
-ExternalSecretTarget
-
-
-
-
-(Optional)
-
-
-
-
-refreshInterval
-
-
-Kubernetes meta/v1.Duration
-
-
-
-
-RefreshInterval is the amount of time before the values are read again from the SecretStore provider
-Valid time units are “ns”, “us” (or “µs”), “ms”, “s”, “m”, “h”
-May be set to zero to fetch and create it once. Defaults to 1h.
-
-
-
-
-data
-
-
-[]ExternalSecretData
-
-
-
-
-(Optional)
-Data defines the connection between the Kubernetes Secret keys and the Provider data
-
-
-
-
-dataFrom
-
-
-[]ExternalSecretDataFromRemoteRef
-
-
-
-
-(Optional)
-DataFrom is used to fetch all properties from a specific Provider data
-If multiple entries are specified, the Secret keys are merged in the specified order
-
-
-
-
-ExternalSecretStatus
-
-
-(Appears on:
-ExternalSecret )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-refreshTime
-
-
-Kubernetes meta/v1.Time
-
-
-
-
-refreshTime is the time and date the external secret was fetched and
-the target secret updated
-
-
-
-
-syncedResourceVersion
-
-string
-
-
-
-SyncedResourceVersion keeps track of the last synced version
-
-
-
-
-conditions
-
-
-[]ExternalSecretStatusCondition
-
-
-
-
-(Optional)
-
-
-
-
-ExternalSecretStatusCondition
-
-
-(Appears on:
-ExternalSecretStatus )
-
-
-
-
-ExternalSecretTarget
-
-
-(Appears on:
-ExternalSecretSpec )
-
-
-
ExternalSecretTarget defines the Kubernetes Secret to be created
-There can be only one target per ExternalSecret.
-
-
-
-
-Field
-Description
-
-
-
-
-
-name
-
-string
-
-
-
-(Optional)
-Name defines the name of the Secret resource to be managed
-This field is immutable
-Defaults to the .metadata.name of the ExternalSecret resource
-
-
-
-
-creationPolicy
-
-
-ExternalSecretCreationPolicy
-
-
-
-
-(Optional)
-CreationPolicy defines rules on how to create the resulting Secret
-Defaults to ‘Owner’
-
-
-
-
-deletionPolicy
-
-
-ExternalSecretDeletionPolicy
-
-
-
-
-(Optional)
-DeletionPolicy defines rules on how to delete the resulting Secret
-Defaults to ‘Retain’
-
-
-
-
-template
-
-
-ExternalSecretTemplate
-
-
-
-
-(Optional)
-Template defines a blueprint for the created Secret resource.
-
-
-
-
-immutable
-
-bool
-
-
-
-(Optional)
-Immutable defines if the final secret will be immutable
-
-
-
-
-ExternalSecretTemplate
-
-
-(Appears on:
-ExternalSecretTarget )
-
-
-
ExternalSecretTemplate defines a blueprint for the created Secret resource.
-we can not use native corev1.Secret, it will have empty ObjectMeta values: https://github.com/kubernetes-sigs/controller-tools/issues/448
-
-
-
-
-(Appears on:
-ExternalSecretTemplate )
-
-
-
ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint.
-
-
-
-
-Field
-Description
-
-
-
-
-
-annotations
-
-map[string]string
-
-
-
-(Optional)
-
-
-
-
-labels
-
-map[string]string
-
-
-
-(Optional)
-
-
-
-
-ExternalSecretValidator
-
-
-
-FakeProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
FakeProvider configures a fake provider that returns static values.
-
-
-FakeProviderData
-
-
-(Appears on:
-FakeProvider )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-key
-
-string
-
-
-
-
-
-
-
-value
-
-string
-
-
-
-
-
-
-
-valueMap
-
-map[string]string
-
-
-
-
-
-
-
-version
-
-string
-
-
-
-
-
-
-
-FindName
-
-
-(Appears on:
-ExternalSecretFind )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-regexp
-
-string
-
-
-
-(Optional)
-Finds secrets base
-
-
-
-
-GCPSMAuth
-
-
-(Appears on:
-GCPSMProvider )
-
-
-
-
-GCPSMAuthSecretRef
-
-
-(Appears on:
-GCPSMAuth )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-secretAccessKeySecretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-(Optional)
-The SecretAccessKey is used for authentication
-
-
-
-
-GCPSMProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
GCPSMProvider Configures a store to sync secrets using the GCP Secret Manager provider.
-
-
-
-
-Field
-Description
-
-
-
-
-
-auth
-
-
-GCPSMAuth
-
-
-
-
-(Optional)
-Auth defines the information necessary to authenticate against GCP
-
-
-
-
-projectID
-
-string
-
-
-
-ProjectID project where secret is located
-
-
-
-
-GCPWorkloadIdentity
-
-
-(Appears on:
-GCPSMAuth )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-serviceAccountRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.ServiceAccountSelector
-
-
-
-
-
-
-
-clusterLocation
-
-string
-
-
-
-
-
-
-
-clusterName
-
-string
-
-
-
-
-
-
-
-clusterProjectID
-
-string
-
-
-
-
-
-
-
-GeneratorRef
-
-
-(Appears on:
-SourceRef )
-
-
-
GeneratorRef points to a generator custom resource.
-
-
-
-
-Field
-Description
-
-
-
-
-
-apiVersion
-
-string
-
-
-
-Specify the apiVersion of the generator resource
-
-
-
-
-kind
-
-string
-
-
-
-Specify the Kind of the resource, e.g. Password, ACRAccessToken etc.
-
-
-
-
-name
-
-string
-
-
-
-Specify the name of the generator resource
-
-
-
-
-GenericStore
-
-
-
GenericStore is a common interface for interacting with ClusterSecretStore
-or a namespaced SecretStore.
-
-GenericStoreValidator
-
-
-
-GitlabAuth
-
-
-(Appears on:
-GitlabProvider )
-
-
-
-
-GitlabProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
Configures a store to sync secrets with a GitLab instance.
-
-
-
-
-Field
-Description
-
-
-
-
-
-url
-
-string
-
-
-
-URL configures the GitLab instance URL. Defaults to https://gitlab.com/ .
-
-
-
-
-auth
-
-
-GitlabAuth
-
-
-
-
-Auth configures how secret-manager authenticates with a GitLab instance.
-
-
-
-
-projectID
-
-string
-
-
-
-ProjectID specifies a project where secrets are located.
-
-
-
-
-inheritFromGroups
-
-bool
-
-
-
-InheritFromGroups specifies whether parent groups should be discovered and checked for secrets.
-
-
-
-
-groupIDs
-
-[]string
-
-
-
-GroupIDs specify, which gitlab groups to pull secrets from. Group secrets are read from left to right followed by the project variables.
-
-
-
-
-environment
-
-string
-
-
-
-Environment environment_scope of gitlab CI/CD variables (Please see https://docs.gitlab.com/ee/ci/environments/#create-a-static-environment on how to create environments)
-
-
-
-
-GitlabSecretRef
-
-
-(Appears on:
-GitlabAuth )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-accessToken
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-AccessToken is used for authentication.
-
-
-
-
-IBMAuth
-
-
-(Appears on:
-IBMProvider )
-
-
-
-
-IBMAuthContainerAuth
-
-
-(Appears on:
-IBMAuth )
-
-
-
IBM Container-based auth with IAM Trusted Profile.
-
-
-
-
-Field
-Description
-
-
-
-
-
-profile
-
-string
-
-
-
-the IBM Trusted Profile
-
-
-
-
-tokenLocation
-
-string
-
-
-
-Location the token is mounted on the pod
-
-
-
-
-iamEndpoint
-
-string
-
-
-
-
-
-
-
-IBMAuthSecretRef
-
-
-(Appears on:
-IBMAuth )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-secretApiKeySecretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-The SecretAccessKey is used for authentication
-
-
-
-
-IBMProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
Configures an store to sync secrets using a IBM Cloud Secrets Manager
-backend.
-
-
-
-
-Field
-Description
-
-
-
-
-
-auth
-
-
-IBMAuth
-
-
-
-
-Auth configures how secret-manager authenticates with the IBM secrets manager.
-
-
-
-
-serviceUrl
-
-string
-
-
-
-ServiceURL is the Endpoint URL that is specific to the Secrets Manager service instance
-
-
-
-
-KubernetesAuth
-
-
-(Appears on:
-KubernetesProvider )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-cert
-
-
-CertAuth
-
-
-
-
-(Optional)
-has both clientCert and clientKey as secretKeySelector
-
-
-
-
-token
-
-
-TokenAuth
-
-
-
-
-(Optional)
-use static token to authenticate with
-
-
-
-
-serviceAccount
-
-github.com/external-secrets/external-secrets/apis/meta/v1.ServiceAccountSelector
-
-
-
-(Optional)
-points to a service account that should be used for authentication
-
-
-
-
-KubernetesProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
Configures a store to sync secrets with a Kubernetes instance.
-
-
-
-
-Field
-Description
-
-
-
-
-
-server
-
-
-KubernetesServer
-
-
-
-
-configures the Kubernetes server Address.
-
-
-
-
-auth
-
-
-KubernetesAuth
-
-
-
-
-Auth configures how secret-manager authenticates with a Kubernetes instance.
-
-
-
-
-remoteNamespace
-
-string
-
-
-
-(Optional)
-Remote namespace to fetch the secrets from
-
-
-
-
-KubernetesServer
-
-
-(Appears on:
-KubernetesProvider )
-
-
-
-
-NoSecretError
-
-
-
NoSecretError shall be returned when a GetSecret can not find the
-desired secret. This is used for deletionPolicy.
-
-OnePasswordAuth
-
-
-(Appears on:
-OnePasswordProvider )
-
-
-
OnePasswordAuth contains a secretRef for credentials.
-
-
-OnePasswordAuthSecretRef
-
-
-(Appears on:
-OnePasswordAuth )
-
-
-
OnePasswordAuthSecretRef holds secret references for 1Password credentials.
-
-
-
-
-Field
-Description
-
-
-
-
-
-connectTokenSecretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-The ConnectToken is used for authentication to a 1Password Connect Server.
-
-
-
-
-OnePasswordProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
OnePasswordProvider configures a store to sync secrets using the 1Password Secret Manager provider.
-
-
-
-
-Field
-Description
-
-
-
-
-
-auth
-
-
-OnePasswordAuth
-
-
-
-
-Auth defines the information necessary to authenticate against OnePassword Connect Server
-
-
-
-
-connectHost
-
-string
-
-
-
-ConnectHost defines the OnePassword Connect Server to connect to
-
-
-
-
-vaults
-
-map[string]int
-
-
-
-Vaults defines which OnePassword vaults to search in which order
-
-
-
-
-OracleAuth
-
-
-(Appears on:
-OracleProvider )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-tenancy
-
-string
-
-
-
-Tenancy is the tenancy OCID where user is located.
-
-
-
-
-user
-
-string
-
-
-
-User is an access OCID specific to the account.
-
-
-
-
-secretRef
-
-
-OracleSecretRef
-
-
-
-
-SecretRef to pass through sensitive information.
-
-
-
-
-OracleProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
Configures an store to sync secrets using a Oracle Vault
-backend.
-
-
-
-
-Field
-Description
-
-
-
-
-
-region
-
-string
-
-
-
-Region is the region where vault is located.
-
-
-
-
-vault
-
-string
-
-
-
-Vault is the vault’s OCID of the specific vault where secret is located.
-
-
-
-
-auth
-
-
-OracleAuth
-
-
-
-
-(Optional)
-Auth configures how secret-manager authenticates with the Oracle Vault.
-If empty, use the instance principal, otherwise the user credentials specified in Auth.
-
-
-
-
-OracleSecretRef
-
-
-(Appears on:
-OracleAuth )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-privatekey
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-PrivateKey is the user’s API Signing Key in PEM format, used for authentication.
-
-
-
-
-fingerprint
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-Fingerprint is the fingerprint of the API private key.
-
-
-
-
-Provider
-
-
-
Provider is a common interface for interacting with secret backends.
-
-SecretStore
-
-
-
SecretStore represents a secure external location for storing secrets, which can be referenced as part of storeRef
fields.
-
-
-
-
-Field
-Description
-
-
-
-
-
-metadata
-
-
-Kubernetes meta/v1.ObjectMeta
-
-
-
-
-Refer to the Kubernetes API documentation for the fields of the
-metadata
field.
-
-
-
-
-spec
-
-
-SecretStoreSpec
-
-
-
-
-
-
-
-
-
-controller
-
-string
-
-
-
-(Optional)
-Used to select the correct KES controller (think: ingress.ingressClassName)
-The KES controller is instantiated with a specific controller name and filters ES based on this property
-
-
-
-
-provider
-
-
-SecretStoreProvider
-
-
-
-
-Used to configure the provider. Only one provider may be set
-
-
-
-
-retrySettings
-
-
-SecretStoreRetrySettings
-
-
-
-
-(Optional)
-Used to configure http retries if failed
-
-
-
-
-refreshInterval
-
-int
-
-
-
-(Optional)
-Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config.
-
-
-
-
-conditions
-
-
-[]ClusterSecretStoreCondition
-
-
-
-
-(Optional)
-Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore
-
-
-
-
-
-
-
-status
-
-
-SecretStoreStatus
-
-
-
-
-
-
-
-
-SecretStoreConditionType
-(string
alias)
-
-(Appears on:
-SecretStoreStatusCondition )
-
-
-
-
-
-
-Value
-Description
-
-
-"Ready"
-
-
-
-SecretStoreProvider
-
-
-(Appears on:
-SecretStoreSpec )
-
-
-
SecretStoreProvider contains the provider-specific configuration.
-
-
-
-
-Field
-Description
-
-
-
-
-
-aws
-
-
-AWSProvider
-
-
-
-
-(Optional)
-AWS configures this store to sync secrets using AWS Secret Manager provider
-
-
-
-
-azurekv
-
-
-AzureKVProvider
-
-
-
-
-(Optional)
-AzureKV configures this store to sync secrets using Azure Key Vault provider
-
-
-
-
-akeyless
-
-
-AkeylessProvider
-
-
-
-
-(Optional)
-Akeyless configures this store to sync secrets using Akeyless Vault provider
-
-
-
-
-vault
-
-
-VaultProvider
-
-
-
-
-(Optional)
-Vault configures this store to sync secrets using Hashi provider
-
-
-
-
-gcpsm
-
-
-GCPSMProvider
-
-
-
-
-(Optional)
-GCPSM configures this store to sync secrets using Google Cloud Platform Secret Manager provider
-
-
-
-
-oracle
-
-
-OracleProvider
-
-
-
-
-(Optional)
-Oracle configures this store to sync secrets using Oracle Vault provider
-
-
-
-
-ibm
-
-
-IBMProvider
-
-
-
-
-(Optional)
-IBM configures this store to sync secrets using IBM Cloud provider
-
-
-
-
-yandexcertificatemanager
-
-
-YandexCertificateManagerProvider
-
-
-
-
-(Optional)
-YandexCertificateManager configures this store to sync secrets using Yandex Certificate Manager provider
-
-
-
-
-yandexlockbox
-
-
-YandexLockboxProvider
-
-
-
-
-(Optional)
-YandexLockbox configures this store to sync secrets using Yandex Lockbox provider
-
-
-
-
-gitlab
-
-
-GitlabProvider
-
-
-
-
-(Optional)
-GitLab configures this store to sync secrets using GitLab Variables provider
-
-
-
-
-alibaba
-
-
-AlibabaProvider
-
-
-
-
-(Optional)
-Alibaba configures this store to sync secrets using Alibaba Cloud provider
-
-
-
-
-onepassword
-
-
-OnePasswordProvider
-
-
-
-
-(Optional)
-OnePassword configures this store to sync secrets using the 1Password Cloud provider
-
-
-
-
-webhook
-
-
-WebhookProvider
-
-
-
-
-(Optional)
-Webhook configures this store to sync secrets using a generic templated webhook
-
-
-
-
-kubernetes
-
-
-KubernetesProvider
-
-
-
-
-(Optional)
-Kubernetes configures this store to sync secrets using a Kubernetes cluster provider
-
-
-
-
-fake
-
-
-FakeProvider
-
-
-
-
-(Optional)
-Fake configures a store with static key/value pairs
-
-
-
-
-senhasegura
-
-
-SenhaseguraProvider
-
-
-
-
-(Optional)
-Senhasegura configures this store to sync secrets using senhasegura provider
-
-
-
-
-doppler
-
-
-DopplerProvider
-
-
-
-
-(Optional)
-Doppler configures this store to sync secrets using the Doppler provider
-
-
-
-
-SecretStoreRef
-
-
-(Appears on:
-ExternalSecretSpec ,
-SourceRef )
-
-
-
SecretStoreRef defines which SecretStore to fetch the ExternalSecret data.
-
-
-
-
-Field
-Description
-
-
-
-
-
-name
-
-string
-
-
-
-Name of the SecretStore resource
-
-
-
-
-kind
-
-string
-
-
-
-(Optional)
-Kind of the SecretStore resource (SecretStore or ClusterSecretStore)
-Defaults to SecretStore
-
-
-
-
-SecretStoreRetrySettings
-
-
-(Appears on:
-SecretStoreSpec )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-maxRetries
-
-int32
-
-
-
-
-
-
-
-retryInterval
-
-string
-
-
-
-
-
-
-
-SecretStoreSpec
-
-
-(Appears on:
-ClusterSecretStore ,
-SecretStore )
-
-
-
SecretStoreSpec defines the desired state of SecretStore.
-
-
-
-
-Field
-Description
-
-
-
-
-
-controller
-
-string
-
-
-
-(Optional)
-Used to select the correct KES controller (think: ingress.ingressClassName)
-The KES controller is instantiated with a specific controller name and filters ES based on this property
-
-
-
-
-provider
-
-
-SecretStoreProvider
-
-
-
-
-Used to configure the provider. Only one provider may be set
-
-
-
-
-retrySettings
-
-
-SecretStoreRetrySettings
-
-
-
-
-(Optional)
-Used to configure http retries if failed
-
-
-
-
-refreshInterval
-
-int
-
-
-
-(Optional)
-Used to configure store refresh interval in seconds. Empty or 0 will default to the controller config.
-
-
-
-
-conditions
-
-
-[]ClusterSecretStoreCondition
-
-
-
-
-(Optional)
-Used to constraint a ClusterSecretStore to specific namespaces. Relevant only to ClusterSecretStore
-
-
-
-
-SecretStoreStatus
-
-
-(Appears on:
-ClusterSecretStore ,
-SecretStore )
-
-
-
SecretStoreStatus defines the observed state of the SecretStore.
-
-
-SecretStoreStatusCondition
-
-
-(Appears on:
-SecretStoreStatus )
-
-
-
-
-SecretsClient
-
-
-
SecretsClient provides access to secrets.
-
-SenhaseguraAuth
-
-
-(Appears on:
-SenhaseguraProvider )
-
-
-
SenhaseguraAuth tells the controller how to do auth in senhasegura.
-
-
-
-
-Field
-Description
-
-
-
-
-
-clientId
-
-string
-
-
-
-
-
-
-
-clientSecretSecretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-
-
-
-
-SenhaseguraModuleType
-(string
alias)
-
-(Appears on:
-SenhaseguraProvider )
-
-
-
SenhaseguraModuleType enum defines senhasegura target module to fetch secrets
-
-
-
-
-Value
-Description
-
-
-"DSM"
- SenhaseguraModuleDSM is the senhasegura DevOps Secrets Management module
-see: https://senhasegura.com/devops
-
-
-
-
-SenhaseguraProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
SenhaseguraProvider setup a store to sync secrets with senhasegura.
-
-
-
-
-Field
-Description
-
-
-
-
-
-url
-
-string
-
-
-
-URL of senhasegura
-
-
-
-
-module
-
-
-SenhaseguraModuleType
-
-
-
-
-Module defines which senhasegura module should be used to get secrets
-
-
-
-
-auth
-
-
-SenhaseguraAuth
-
-
-
-
-Auth defines parameters to authenticate in senhasegura
-
-
-
-
-ignoreSslCertificate
-
-bool
-
-
-
-IgnoreSslCertificate defines if SSL certificate must be ignored
-
-
-
-
-SourceRef
-
-
-(Appears on:
-ExternalSecretData ,
-ExternalSecretDataFromRemoteRef )
-
-
-
SourceRef allows you to override the source
-from which the secret will be pulled from.
-You can define at maximum one property.
-
-
-
-
-Field
-Description
-
-
-
-
-
-storeRef
-
-
-SecretStoreRef
-
-
-
-
-(Optional)
-
-
-
-
-generatorRef
-
-
-GeneratorRef
-
-
-
-
-(Optional)
-GeneratorRef points to a generator custom resource in
-
-
-
-
-TemplateEngineVersion
-(string
alias)
-
-(Appears on:
-ExternalSecretTemplate )
-
-
-
-
-
-
-Value
-Description
-
-
-"v1"
-
-"v2"
-
-
-
-TemplateFrom
-
-
-(Appears on:
-ExternalSecretTemplate )
-
-
-
-
-TemplateRef
-
-
-(Appears on:
-TemplateFrom )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-name
-
-string
-
-
-
-
-
-
-
-items
-
-
-[]TemplateRefItem
-
-
-
-
-
-
-
-
-TemplateRefItem
-
-
-(Appears on:
-TemplateRef )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-key
-
-string
-
-
-
-
-
-
-
-TemplateScope
-(string
alias)
-
-(Appears on:
-TemplateFrom )
-
-
-
-
-
-
-Value
-Description
-
-
-"KeysAndValues"
-
-"Values"
-
-
-
-TemplateTarget
-(string
alias)
-
-(Appears on:
-TemplateFrom )
-
-
-
-
-
-
-Value
-Description
-
-
-"Annotations"
-
-"Data"
-
-"Labels"
-
-"StringData"
-
-
-
-TokenAuth
-
-
-(Appears on:
-KubernetesAuth )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-bearerToken
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-
-
-
-
-ValidationResult
-(byte
alias)
-
-
-
-
-
-Value
-Description
-
-
-2
-Error indicates that there is a misconfiguration.
-
-0
-Ready indicates that the client is configured correctly
-and can be used.
-
-1
-Unknown indicates that the client can be used
-but information is missing and it can not be validated.
-
-
-
-VaultAppRole
-
-
-(Appears on:
-VaultAuth )
-
-
-
VaultAppRole authenticates with Vault using the App Role auth mechanism,
-with the role and secret stored in a Kubernetes Secret resource.
-
-
-
-
-Field
-Description
-
-
-
-
-
-path
-
-string
-
-
-
-Path where the App Role authentication backend is mounted
-in Vault, e.g: “approle”
-
-
-
-
-roleId
-
-string
-
-
-
-RoleID configured in the App Role authentication backend when setting
-up the authentication backend in Vault.
-
-
-
-
-secretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-Reference to a key in a Secret that contains the App Role secret used
-to authenticate with Vault.
-The key
field must be specified and denotes which entry within the Secret
-resource is used as the app role secret.
-
-
-
-
-VaultAuth
-
-
-(Appears on:
-VaultProvider )
-
-
-
VaultAuth is the configuration used to authenticate with a Vault server.
-Only one of tokenSecretRef
, appRole
, kubernetes
, ldap
, userPass
, jwt
or cert
-can be specified.
-
-
-
-
-Field
-Description
-
-
-
-
-
-tokenSecretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-(Optional)
-TokenSecretRef authenticates with Vault by presenting a token.
-
-
-
-
-appRole
-
-
-VaultAppRole
-
-
-
-
-(Optional)
-AppRole authenticates with Vault using the App Role auth mechanism,
-with the role and secret stored in a Kubernetes Secret resource.
-
-
-
-
-kubernetes
-
-
-VaultKubernetesAuth
-
-
-
-
-(Optional)
-Kubernetes authenticates with Vault by passing the ServiceAccount
-token stored in the named Secret resource to the Vault server.
-
-
-
-
-ldap
-
-
-VaultLdapAuth
-
-
-
-
-(Optional)
-Ldap authenticates with Vault by passing username/password pair using
-the LDAP authentication method
-
-
-
-
-userPass
-
-
-VaultUserPassAuth
-
-
-
-
-(Optional)
-UserPass authenticates with Vault by passing username/password pair using
-the userPass authentication method
-
-
-
-
-jwt
-
-
-VaultJwtAuth
-
-
-
-
-(Optional)
-Jwt authenticates with Vault by passing role and JWT token using the
-JWT/OIDC authentication method
-
-
-
-
-cert
-
-
-VaultCertAuth
-
-
-
-
-(Optional)
-Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate
-Cert authentication method
-
-
-
-
-VaultCertAuth
-
-
-(Appears on:
-VaultAuth )
-
-
-
VaultJwtAuth authenticates with Vault using the JWT/OIDC authentication
-method, with the role name and token stored in a Kubernetes Secret resource.
-
-
-
-
-Field
-Description
-
-
-
-
-
-clientCert
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-(Optional)
-ClientCert is a certificate to authenticate using the Cert Vault
-authentication method
-
-
-
-
-secretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-SecretRef to a key in a Secret resource containing client private key to
-authenticate with Vault using the Cert authentication method
-
-
-
-
-VaultJwtAuth
-
-
-(Appears on:
-VaultAuth )
-
-
-
VaultJwtAuth authenticates with Vault using the JWT/OIDC authentication
-method, with the role name and a token stored in a Kubernetes Secret resource or
-a Kubernetes service account token retrieved via TokenRequest
.
-
-
-
-
-Field
-Description
-
-
-
-
-
-path
-
-string
-
-
-
-Path where the JWT authentication backend is mounted
-in Vault, e.g: “jwt”
-
-
-
-
-role
-
-string
-
-
-
-(Optional)
-Role is a JWT role to authenticate using the JWT/OIDC Vault
-authentication method
-
-
-
-
-secretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-(Optional)
-Optional SecretRef that refers to a key in a Secret resource containing JWT token to
-authenticate with Vault using the JWT/OIDC authentication method.
-
-
-
-
-kubernetesServiceAccountToken
-
-
-VaultKubernetesServiceAccountTokenAuth
-
-
-
-
-(Optional)
-Optional ServiceAccountToken specifies the Kubernetes service account for which to request
-a token for with the TokenRequest
API.
-
-
-
-
-VaultKVStoreVersion
-(string
alias)
-
-(Appears on:
-VaultProvider )
-
-
-
-
-
-
-Value
-Description
-
-
-"v1"
-
-"v2"
-
-
-
-VaultKubernetesAuth
-
-
-(Appears on:
-VaultAuth )
-
-
-
Authenticate against Vault using a Kubernetes ServiceAccount token stored in
-a Secret.
-
-
-
-
-Field
-Description
-
-
-
-
-
-mountPath
-
-string
-
-
-
-Path where the Kubernetes authentication backend is mounted in Vault, e.g:
-“kubernetes”
-
-
-
-
-serviceAccountRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.ServiceAccountSelector
-
-
-
-(Optional)
-Optional service account field containing the name of a kubernetes ServiceAccount.
-If the service account is specified, the service account secret token JWT will be used
-for authenticating with Vault. If the service account selector is not supplied,
-the secretRef will be used instead.
-
-
-
-
-secretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-(Optional)
-Optional secret field containing a Kubernetes ServiceAccount JWT used
-for authenticating with Vault. If a name is specified without a key,
-token
is the default. If one is not specified, the one bound to
-the controller will be used.
-
-
-
-
-role
-
-string
-
-
-
-A required field containing the Vault Role to assume. A Role binds a
-Kubernetes ServiceAccount with a set of Vault policies.
-
-
-
-
-VaultKubernetesServiceAccountTokenAuth
-
-
-(Appears on:
-VaultJwtAuth )
-
-
-
VaultKubernetesServiceAccountTokenAuth authenticates with Vault using a temporary
-Kubernetes service account token retrieved by the TokenRequest
API.
-
-
-
-
-Field
-Description
-
-
-
-
-
-oracle
-
-
-OracleProvider
-
-
-
-
-(Optional)
-Oracle configures this store to sync secrets using Oracle Vault provider
-
-
-
-
-ibm
-
-github.com/external-secrets/external-secrets/apis/meta/v1.ServiceAccountSelector
-
-
-
-Service account field containing the name of a kubernetes ServiceAccount.
-
-
-
-
-audiences
-
-[]string
-
-
-
-(Optional)
-Optional audiences field that will be used to request a temporary Kubernetes service
-account token for the service account referenced by serviceAccountRef
.
-Defaults to a single audience vault
it not specified.
-Deprecated: use serviceAccountRef.Audiences instead
-
-
-
-
-expirationSeconds
-
-int64
-
-
-
-(Optional)
-Optional expiration time in seconds that will be used to request a temporary
-Kubernetes service account token for the service account referenced by
-serviceAccountRef
.
-Deprecated: this will be removed in the future.
-Defaults to 10 minutes.
-
-
-
-
-alibaba
-
-
-AlibabaProvider
-
-
-
-
-(Optional)
-Alibaba configures this store to sync secrets using Alibaba Cloud provider
-
-
-
-
-passworddepot
-
-
-PasswordDepotProvider
-
-
-
-
-(Optional)
-PasswordDepot configures this store to sync secrets using PasswordDepot provider
-
-
-
-
-VaultLdapAuth
-
-
-(Appears on:
-VaultAuth )
-
-
-
VaultLdapAuth authenticates with Vault using the LDAP authentication method,
-with the username and password stored in a Kubernetes Secret resource.
-
-
-
-
-Field
-Description
-
-
-
-
-
-path
-
-string
-
-
-
-Path where the LDAP authentication backend is mounted
-in Vault, e.g: “ldap”
-
-
-
-
-username
-
-string
-
-
-
-Username is a LDAP user name used to authenticate using the LDAP Vault
-authentication method
-
-
-
-
-secretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-SecretRef to a key in a Secret resource containing password for the LDAP
-user used to authenticate with Vault using the LDAP authentication
-method
-
-
-
-
-VaultUserPassAuth
-
-
-(Appears on:
-VaultAuth )
-
-
-
VaultUserPassAuth authenticates with Vault using the UserPass authentication method,
-with the username and password stored in a Kubernetes Secret resource.
-
-
-
-
-Field
-Description
-
-
-
-
-
-path
-
-string
-
-
-
-Path where the UserPass authentication backend is mounted
-in Vault, e.g: “userpass”
-
-
-
-
-username
-
-string
-
-
-
-Username is a user name used to authenticate using the UserPass Vault
-authentication method
-
-
-
-
-secretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-SecretRef to a key in a Secret resource containing password for the
-user used to authenticate with Vault using the UserPass authentication
-method
-
-
-
-
-VaultProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
Configures an store to sync secrets using a HashiCorp Vault
-KV backend.
-
-
-
-
-Field
-Description
-
-
-
-
-
-auth
-
-
-VaultAuth
-
-
-
-
-Auth configures how secret-manager authenticates with the Vault server.
-
-
-
-
-server
-
-string
-
-
-
-Server is the connection address for the Vault server, e.g: “https://vault.example.com:8200” .
-
-
-
-
-path
-
-string
-
-
-
-(Optional)
-Path is the mount path of the Vault KV backend endpoint, e.g:
-“secret”. The v2 KV secret engine version specific “/data” path suffix
-for fetching secrets from Vault is optional and will be appended
-if not present in specified path.
-
-
-
-
-version
-
-
-VaultKVStoreVersion
-
-
-
-
-Version is the Vault KV secret engine version. This can be either “v1” or
-“v2”. Version defaults to “v2”.
-
-
-
-
-namespace
-
-string
-
-
-
-(Optional)
-Name of the vault namespace. Namespaces is a set of features within Vault Enterprise that allows
-Vault environments to support Secure Multi-tenancy. e.g: “ns1”.
-More about namespaces can be found here https://www.vaultproject.io/docs/enterprise/namespaces
-
-
-
-
-caBundle
-
-[]byte
-
-
-
-(Optional)
-PEM encoded CA bundle used to validate Vault server certificate. Only used
-if the Server URL is using HTTPS protocol. This parameter is ignored for
-plain HTTP protocol connection. If not set the system root certificates
-are used to validate the TLS connection.
-
-
-
-
-caProvider
-
-
-CAProvider
-
-
-
-
-(Optional)
-The provider for the CA bundle to use to validate Vault server certificate.
-
-
-
-
-readYourWrites
-
-bool
-
-
-
-(Optional)
-ReadYourWrites ensures isolated read-after-write semantics by
-providing discovered cluster replication states in each request.
-More information about eventual consistency in Vault can be found here
-https://www.vaultproject.io/docs/enterprise/consistency
-
-
-
-
-forwardInconsistent
-
-bool
-
-
-
-(Optional)
-ForwardInconsistent tells Vault to forward read-after-write requests to the Vault
-leader instead of simply retrying within a loop. This can increase performance if
-the option is enabled serverside.
-https://www.vaultproject.io/docs/configuration/replication#allow_forwarding_via_header
-
-
-
-
-WebhookCAProvider
-
-
-(Appears on:
-WebhookProvider )
-
-
-
Defines a location to fetch the cert for the webhook provider from.
-
-
-
-
-Field
-Description
-
-
-
-
-
-type
-
-
-WebhookCAProviderType
-
-
-
-
-The type of provider to use such as “Secret”, or “ConfigMap”.
-
-
-
-
-name
-
-string
-
-
-
-The name of the object located at the provider type.
-
-
-
-
-key
-
-string
-
-
-
-The key the value inside of the provider type to use, only used with “Secret” type
-
-
-
-
-namespace
-
-string
-
-
-
-(Optional)
-The namespace the Provider type is in.
-
-
-
-
-WebhookCAProviderType
-(string
alias)
-
-(Appears on:
-WebhookCAProvider )
-
-
-
-
-
-
-Value
-Description
-
-
-"ConfigMap"
-
-"Secret"
-
-
-
-WebhookProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
AkeylessProvider Configures an store to sync secrets using Akeyless KV.
-
-
-
-
-Field
-Description
-
-
-
-
-
-method
-
-string
-
-
-
-Webhook Method
-
-
-
-
-url
-
-string
-
-
-
-Webhook url to call
-
-
-
-
-headers
-
-map[string]string
-
-
-
-(Optional)
-Headers
-
-
-
-
-body
-
-string
-
-
-
-(Optional)
-Body
-
-
-
-
-timeout
-
-
-Kubernetes meta/v1.Duration
-
-
-
-
-(Optional)
-Timeout
-
-
-
-
-result
-
-
-WebhookResult
-
-
-
-
-Result formatting
-
-
-
-
-secrets
-
-
-[]WebhookSecret
-
-
-
-
-(Optional)
-Secrets to fill in templates
-These secrets will be passed to the templating function as key value pairs under the given name
-
-
-
-
-caBundle
-
-[]byte
-
-
-
-(Optional)
-PEM encoded CA bundle used to validate webhook server certificate. Only used
-if the Server URL is using HTTPS protocol. This parameter is ignored for
-plain HTTP protocol connection. If not set the system root certificates
-are used to validate the TLS connection.
-
-
-
-
-caProvider
-
-
-WebhookCAProvider
-
-
-
-
-(Optional)
-The provider for the CA bundle to use to validate webhook server certificate.
-
-
-
-
-WebhookResult
-
-
-(Appears on:
-WebhookProvider )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-jsonPath
-
-string
-
-
-
-(Optional)
-Json path of return value
-
-
-
-
-WebhookSecret
-
-
-(Appears on:
-WebhookProvider )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-name
-
-string
-
-
-
-Name of this secret in templates
-
-
-
-
-secretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-Secret ref to fill in credentials
-
-
-
-
-YandexCertificateManagerAuth
-
-
-(Appears on:
-YandexCertificateManagerProvider )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-authorizedKeySecretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-(Optional)
-The authorized key used for authentication
-
-
-
-
-YandexCertificateManagerCAProvider
-
-
-(Appears on:
-YandexCertificateManagerProvider )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-certSecretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-
-
-
-
-YandexCertificateManagerProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
YandexCertificateManagerProvider Configures a store to sync secrets using the Yandex Certificate Manager provider.
-
-
-
-
-Field
-Description
-
-
-
-
-
-apiEndpoint
-
-string
-
-
-
-(Optional)
-Yandex.Cloud API endpoint (e.g. ‘api.cloud.yandex.net:443’)
-
-
-
-
-auth
-
-
-YandexCertificateManagerAuth
-
-
-
-
-Auth defines the information necessary to authenticate against Yandex Certificate Manager
-
-
-
-
-caProvider
-
-
-YandexCertificateManagerCAProvider
-
-
-
-
-(Optional)
-The provider for the CA bundle to use to validate Yandex.Cloud server certificate.
-
-
-
-
-YandexLockboxAuth
-
-
-(Appears on:
-YandexLockboxProvider )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-authorizedKeySecretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-(Optional)
-The authorized key used for authentication
-
-
-
-
-caProvider
-
-
-CAProvider
-
-
-
-
-(Optional)
-The provider for the CA bundle to use to validate Vault server certificate.
-
-
-
-
-YandexLockboxCAProvider
-
-
-(Appears on:
-YandexLockboxProvider )
-
-
-
-
-
-
-Field
-Description
-
-
-
-
-
-certSecretRef
-
-github.com/external-secrets/external-secrets/apis/meta/v1.SecretKeySelector
-
-
-
-
-
-
-
-YandexLockboxProvider
-
-
-(Appears on:
-SecretStoreProvider )
-
-
-
YandexLockboxProvider Configures a store to sync secrets using the Yandex Lockbox provider.
-
-
-
-
-Field
-Description
-
-
-
-
-
-apiEndpoint
-
-string
-
-
-
-(Optional)
-Yandex.Cloud API endpoint (e.g. ‘api.cloud.yandex.net:443’)
-
-
-
-
-auth
-
-
-YandexLockboxAuth
-
-
-
-
-Auth defines the information necessary to authenticate against Yandex Lockbox
-
-
-
-
-caProvider
-
-
-YandexLockboxCAProvider
-
-
-
-
-(Optional)
-The provider for the CA bundle to use to validate Yandex.Cloud server certificate.
-
-
-
-
-
-
-Generated with gen-crd-api-reference-docs
.
-
diff --git a/e2e/Dockerfile b/e2e/Dockerfile
index d8a43828948..b14d9e14903 100644
--- a/e2e/Dockerfile
+++ b/e2e/Dockerfile
@@ -1,12 +1,12 @@
-FROM golang:1.22.2-bookworm@sha256:b03f3ba515751657c75475b20941fef47341fccb3341c3c0b64283ff15d3fb46 as builder
+FROM golang:1.23.5-bookworm@sha256:3149bc5043fa58cf127fd8db1fdd4e533b6aed5a40d663d4f4ae43d20386665f AS builder
ENV KUBECTL_VERSION="v1.28.3"
ENV HELM_VERSION="v3.13.1"
RUN go install github.com/onsi/ginkgo/v2/ginkgo@v2.1.6
-RUN wget -q https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl -O /usr/local/bin/kubectl && \
+RUN wget --max-redirect=0 -q https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl -O /usr/local/bin/kubectl && \
chmod +x /usr/local/bin/kubectl && \
- wget -q https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz -O - | tar -xzO linux-amd64/helm > /usr/local/bin/helm && \
+ wget --max-redirect=0 -q https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz -O - | tar -xzO linux-amd64/helm > /usr/local/bin/helm && \
chmod +x /usr/local/bin/helm
WORKDIR /usr/src/app
@@ -16,7 +16,7 @@ COPY . .
WORKDIR /usr/src/app/e2e
RUN make e2e-bin
-FROM alpine:3.19.1@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b
+FROM alpine:3.21.2@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099
RUN apk add -U --no-cache \
ca-certificates \
bash \
diff --git a/e2e/Makefile b/e2e/Makefile
index f6b6028f848..c3d3692fe5e 100644
--- a/e2e/Makefile
+++ b/e2e/Makefile
@@ -9,6 +9,8 @@ export E2E_IMAGE_NAME ?= ghcr.io/external-secrets/external-secrets-e2e
export GINKGO_LABELS ?= !managed
export TEST_SUITES ?= provider generator flux argocd
+export OCI_IMAGE_NAME = oci.external-secrets.io/external-secrets/external-secrets
+
start-kind: ## Start kind cluster
kind create cluster \
--name external-secrets \
@@ -22,7 +24,13 @@ test: e2e-image ## Run e2e tests against current kube context
VERSION=$(VERSION) \
ARCH=amd64 \
DOCKER_BUILD_ARGS="${DOCKER_BUILD_ARGS} --build-arg TARGETARCH=amd64 --build-arg TARGETOS=linux"
+ $(MAKE) -C ../ docker.build \
+ IMAGE_NAME=$(OCI_IMAGE_NAME) \
+ VERSION=$(VERSION) \
+ ARCH=amd64 \
+ DOCKER_BUILD_ARGS="${DOCKER_BUILD_ARGS} --build-arg TARGETARCH=amd64 --build-arg TARGETOS=linux"
kind load docker-image --name="external-secrets" $(IMAGE_NAME):$(VERSION)
+ kind load docker-image --name="external-secrets" $(OCI_IMAGE_NAME):$(VERSION)
kind load docker-image --name="external-secrets" $(E2E_IMAGE_NAME):$(VERSION)
./run.sh
@@ -31,7 +39,15 @@ test.managed: e2e-image ## Run e2e tests against current kube context
VERSION=$(VERSION) \
ARCH=amd64 \
DOCKER_BUILD_ARGS="${DOCKER_BUILD_ARGS} --build-arg TARGETARCH=amd64 --build-arg TARGETOS=linux"
+ $(MAKE) -C ../ docker.build \
+ IMAGE_NAME=$(OCI_IMAGE_NAME) \
+ VERSION=$(VERSION) \
+ ARCH=amd64 \
+ DOCKER_BUILD_ARGS="${DOCKER_BUILD_ARGS} --build-arg TARGETARCH=amd64 --build-arg TARGETOS=linux"
+ $(MAKE) -C ../ docker.push \
+ VERSION=$(VERSION)
$(MAKE) -C ../ docker.push \
+ IMAGE_NAME=$(OCI_IMAGE_NAME) \
VERSION=$(VERSION)
$(MAKE) -C ../ docker.push \
IMAGE_NAME=$(E2E_IMAGE_NAME) \
diff --git a/e2e/framework/addon/chart.go b/e2e/framework/addon/chart.go
index c692b31d24d..07d7e86eb5d 100644
--- a/e2e/framework/addon/chart.go
+++ b/e2e/framework/addon/chart.go
@@ -66,6 +66,7 @@ func (c *HelmChart) Install() error {
}
args := []string{"install", c.ReleaseName, c.Chart,
+ "--dependency-update",
"--debug",
"--wait",
"--timeout", "600s",
diff --git a/e2e/framework/addon/conjur.go b/e2e/framework/addon/conjur.go
index 8714a95a6be..e22a47bb0ba 100644
--- a/e2e/framework/addon/conjur.go
+++ b/e2e/framework/addon/conjur.go
@@ -11,6 +11,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
+
package addon
import (
@@ -127,7 +128,7 @@ func (l *Conjur) initConjur() error {
return fmt.Errorf("error fetching admin API key: %w", err)
}
- // TODO: ExecCmdWithContainer includes the StdErr output with a warning about config directory.
+ // Note: ExecCmdWithContainer includes the StdErr output with a warning about config directory.
// Therefore we need to split the output and only use the first line.
l.AdminApiKey = strings.Split(apiKey, "\n")[0]
diff --git a/e2e/framework/addon/eso.go b/e2e/framework/addon/eso.go
index 48e6fe83646..56e9330edfc 100644
--- a/e2e/framework/addon/eso.go
+++ b/e2e/framework/addon/eso.go
@@ -25,7 +25,10 @@ type ESO struct {
*HelmChart
}
-const installCRDsVar = "installCRDs"
+const (
+ installCRDsVar = "installCRDs"
+ esoImage = "ghcr.io/external-secrets/external-secrets"
+)
func NewESO(mutators ...MutationFunc) *ESO {
eso := &ESO{
@@ -42,14 +45,26 @@ func NewESO(mutators ...MutationFunc) *ESO {
Key: "webhook.image.tag",
Value: os.Getenv("VERSION"),
},
+ {
+ Key: "webhook.image.repository",
+ Value: esoImage,
+ },
{
Key: "certController.image.tag",
Value: os.Getenv("VERSION"),
},
+ {
+ Key: "certController.image.repository",
+ Value: esoImage,
+ },
{
Key: "image.tag",
Value: os.Getenv("VERSION"),
},
+ {
+ Key: "image.repository",
+ Value: esoImage,
+ },
{
Key: "extraArgs.loglevel",
Value: "debug",
diff --git a/e2e/framework/addon/vault.go b/e2e/framework/addon/vault.go
index a46984b891a..8d5748bdc7c 100644
--- a/e2e/framework/addon/vault.go
+++ b/e2e/framework/addon/vault.go
@@ -22,14 +22,16 @@ import (
"crypto/x509/pkix"
"encoding/json"
"encoding/pem"
+ "errors"
"fmt"
- "k8s.io/apimachinery/pkg/types"
"math/big"
"net"
"net/http"
"os"
"time"
+ "k8s.io/apimachinery/pkg/types"
+
"github.com/golang-jwt/jwt/v4"
vault "github.com/hashicorp/vault/api"
@@ -320,7 +322,7 @@ func genVaultCertificates(namespace string) ([]byte, []byte, []byte, []byte, []b
"vault-" + namespace,
fmt.Sprintf("vault-%s.%s.svc.cluster.local", namespace, namespace)})
if err != nil {
- return nil, nil, nil, nil, nil, nil, fmt.Errorf("unable to generate vault server cert")
+ return nil, nil, nil, nil, nil, nil, errors.New("unable to generate vault server cert")
}
serverKeyPem := pem.EncodeToMemory(&pem.Block{
Type: privatePemType,
@@ -333,7 +335,7 @@ func genVaultCertificates(namespace string) ([]byte, []byte, []byte, []byte, []b
}
clientPem, clientKey, err := genPeerCert(clientRootCert, clientRootKey, "vault-client", nil)
if err != nil {
- return nil, nil, nil, nil, nil, nil, fmt.Errorf("unable to generate vault server cert")
+ return nil, nil, nil, nil, nil, nil, errors.New("unable to generate vault server cert")
}
clientKeyPem := pem.EncodeToMemory(&pem.Block{
Type: privatePemType,
diff --git a/e2e/framework/eso.go b/e2e/framework/eso.go
index 3003f72a3e6..baa50c5cbc6 100644
--- a/e2e/framework/eso.go
+++ b/e2e/framework/eso.go
@@ -105,8 +105,9 @@ func equalSecrets(exp, ts *v1.Secret) bool {
return false
}
- // secret contains label owner which must be ignored
+ // secret contains labels which must be ignored
delete(ts.ObjectMeta.Labels, esv1beta1.LabelOwner)
+ delete(ts.ObjectMeta.Labels, esv1beta1.LabelManaged)
if len(ts.ObjectMeta.Labels) == 0 {
ts.ObjectMeta.Labels = nil
}
diff --git a/e2e/framework/util/util.go b/e2e/framework/util/util.go
index a8464ab5a8b..43cece0bd9f 100644
--- a/e2e/framework/util/util.go
+++ b/e2e/framework/util/util.go
@@ -24,37 +24,43 @@ import (
fluxhelm "github.com/fluxcd/helm-controller/api/v2beta1"
fluxsrc "github.com/fluxcd/source-controller/api/v1beta2"
-
- // nolint
- . "github.com/onsi/ginkgo/v2"
v1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/client-go/kubernetes"
- "k8s.io/client-go/kubernetes/scheme"
+ clientgoscheme "k8s.io/client-go/kubernetes/scheme"
restclient "k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/remotecommand"
crclient "sigs.k8s.io/controller-runtime/pkg/client"
+ // nolint
+ . "github.com/onsi/ginkgo/v2"
+
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
)
-var Scheme = runtime.NewScheme()
+var scheme = runtime.NewScheme()
func init() {
- _ = scheme.AddToScheme(Scheme)
- _ = esv1beta1.AddToScheme(Scheme)
- _ = esv1alpha1.AddToScheme(Scheme)
- _ = genv1alpha1.AddToScheme(Scheme)
- _ = fluxhelm.AddToScheme(Scheme)
- _ = fluxsrc.AddToScheme(Scheme)
- _ = apiextensionsv1.AddToScheme(Scheme)
+ // kubernetes schemes
+ utilruntime.Must(clientgoscheme.AddToScheme(scheme))
+ utilruntime.Must(apiextensionsv1.AddToScheme(scheme))
+
+ // external-secrets schemes
+ utilruntime.Must(esv1beta1.AddToScheme(scheme))
+ utilruntime.Must(esv1alpha1.AddToScheme(scheme))
+ utilruntime.Must(genv1alpha1.AddToScheme(scheme))
+
+ // other schemes
+ utilruntime.Must(fluxhelm.AddToScheme(scheme))
+ utilruntime.Must(fluxsrc.AddToScheme(scheme))
}
const (
@@ -129,7 +135,7 @@ func execCmd(client kubernetes.Interface, config *restclient.Config, podName, co
}
req.VersionedParams(
option,
- scheme.ParameterCodec,
+ clientgoscheme.ParameterCodec,
)
exec, err := remotecommand.NewSPDYExecutor(config, "POST", req.URL())
if err != nil {
@@ -290,7 +296,7 @@ func NewConfig() (*restclient.Config, *kubernetes.Clientset, crclient.Client) {
Fail(err.Error())
}
- CRClient, err := crclient.New(kubeConfig, crclient.Options{Scheme: Scheme})
+ CRClient, err := crclient.New(kubeConfig, crclient.Options{Scheme: scheme})
if err != nil {
Fail(err.Error())
}
diff --git a/e2e/go.mod b/e2e/go.mod
index 5141c51a290..367acefb7ed 100644
--- a/e2e/go.mod
+++ b/e2e/go.mod
@@ -1,209 +1,219 @@
module github.com/external-secrets/external-secrets-e2e
-go 1.22.1
+go 1.23.4
-replace github.com/external-secrets/external-secrets => ../
+replace (
+ github.com/Masterminds/sprig/v3 => github.com/external-secrets/sprig/v3 v3.3.0
+ github.com/external-secrets/external-secrets => ../
+)
replace (
github.com/external-secrets/external-secrets v0.0.0 => ../
github.com/go-check/check => github.com/go-check/check v0.0.0-20180628173108-788fd7840127
- k8s.io/api => k8s.io/api v0.28.1
- k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.28.1
- k8s.io/apimachinery => k8s.io/apimachinery v0.28.1
- k8s.io/apiserver => k8s.io/apiserver v0.28.1
- k8s.io/cli-runtime => k8s.io/cli-runtime v0.28.1
- k8s.io/client-go => k8s.io/client-go v0.28.1
- k8s.io/cloud-provider => k8s.io/cloud-provider v0.28.1
- k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.28.1
- k8s.io/code-generator => k8s.io/code-generator v0.28.1
- k8s.io/component-base => k8s.io/component-base v0.28.1
- k8s.io/component-helpers => k8s.io/component-helpers v0.28.1
- k8s.io/controller-manager => k8s.io/controller-manager v0.28.1
- k8s.io/cri-api => k8s.io/cri-api v0.28.1
- k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.28.1
- k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.28.1
- k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.28.1
- k8s.io/kube-proxy => k8s.io/kube-proxy v0.28.1
- k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.28.1
- k8s.io/kubectl => k8s.io/kubectl v0.28.1
- k8s.io/kubelet => k8s.io/kubelet v0.28.1
- k8s.io/kubernetes => k8s.io/kubernetes v1.27.1
- k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.28.1
- k8s.io/metrics => k8s.io/metrics v0.28.1
- k8s.io/mount-utils => k8s.io/mount-utils v0.28.1
- k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.28.1
- k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.28.1
+ k8s.io/api => k8s.io/api v0.31.0
+ k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.31.0
+ k8s.io/apimachinery => k8s.io/apimachinery v0.31.0
+ k8s.io/apiserver => k8s.io/apiserver v0.31.0
+ k8s.io/cli-runtime => k8s.io/cli-runtime v0.31.0
+ k8s.io/client-go => k8s.io/client-go v0.31.0
+ k8s.io/cloud-provider => k8s.io/cloud-provider v0.31.0
+ k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.31.0
+ k8s.io/code-generator => k8s.io/code-generator v0.31.0
+ k8s.io/component-base => k8s.io/component-base v0.31.0
+ k8s.io/component-helpers => k8s.io/component-helpers v0.31.0
+ k8s.io/controller-manager => k8s.io/controller-manager v0.31.0
+ k8s.io/cri-api => k8s.io/cri-api v0.31.0
+ k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.31.0
+ k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.31.0
+ k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.31.0
+ k8s.io/kube-proxy => k8s.io/kube-proxy v0.31.0
+ k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.31.0
+ k8s.io/kubectl => k8s.io/kubectl v0.31.0
+ k8s.io/kubelet => k8s.io/kubelet v0.31.0
+ k8s.io/kubernetes => k8s.io/kubernetes v1.30.0
+ k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.31.0
+ k8s.io/metrics => k8s.io/metrics v0.31.0
+ k8s.io/mount-utils => k8s.io/mount-utils v0.31.0
+ k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.31.0
+ k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.31.0
)
require (
- cloud.google.com/go/secretmanager v1.12.0
+ cloud.google.com/go/secretmanager v1.14.3
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible
github.com/Azure/go-autorest/autorest v0.11.29
- github.com/Azure/go-autorest/autorest/azure/auth v0.5.12
- github.com/DelineaXPM/dsv-sdk-go/v2 v2.1.2
+ github.com/Azure/go-autorest/autorest/azure/auth v0.5.13
+ github.com/DelineaXPM/dsv-sdk-go/v2 v2.2.0
+ github.com/DelineaXPM/tss-sdk-go/v2 v2.0.3
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.5
github.com/akeylesslabs/akeyless-go/v3 v3.6.3
github.com/aliyun/alibaba-cloud-sdk-go v1.62.271
- github.com/aws/aws-sdk-go v1.51.21
- github.com/cyberark/conjur-api-go v0.11.1
+ github.com/aws/aws-sdk-go v1.55.6
+ github.com/cyberark/conjur-api-go v0.12.10
github.com/external-secrets/external-secrets v0.0.0
github.com/fluxcd/helm-controller/api v0.37.2
github.com/fluxcd/pkg/apis/meta v1.2.0
github.com/fluxcd/source-controller/api v1.2.3
- github.com/golang-jwt/jwt/v4 v4.5.0
- github.com/hashicorp/vault/api v1.12.2
- github.com/onsi/ginkgo/v2 v2.17.1
- github.com/onsi/gomega v1.30.0
- github.com/oracle/oci-go-sdk/v65 v65.63.1
- github.com/scaleway/scaleway-sdk-go v1.0.0-beta.26
- github.com/xanzy/go-gitlab v0.102.0
- golang.org/x/oauth2 v0.19.0
- google.golang.org/api v0.172.0
- k8s.io/api v0.29.3
- k8s.io/apiextensions-apiserver v0.29.3
- k8s.io/apimachinery v0.29.3
+ github.com/golang-jwt/jwt/v4 v4.5.1
+ github.com/hashicorp/vault/api v1.15.0
+ github.com/onsi/ginkgo/v2 v2.22.2
+ github.com/onsi/gomega v1.36.2
+ github.com/oracle/oci-go-sdk/v65 v65.81.3
+ github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30
+ gitlab.com/gitlab-org/api/client-go v0.120.0
+ golang.org/x/oauth2 v0.25.0
+ google.golang.org/api v0.218.0
+ k8s.io/api v0.32.1
+ k8s.io/apiextensions-apiserver v0.32.1
+ k8s.io/apimachinery v0.32.1
k8s.io/client-go v1.5.2
- k8s.io/utils v0.0.0-20240310230437-4693a0247e57
- sigs.k8s.io/controller-runtime v0.17.3
+ k8s.io/utils v0.0.0-20241210054802-24370beab758
+ sigs.k8s.io/controller-runtime v0.20.1
sigs.k8s.io/yaml v1.4.0
- software.sslmate.com/src/go-pkcs12 v0.4.0
+ software.sslmate.com/src/go-pkcs12 v0.5.0
)
require (
- cloud.google.com/go/compute v1.25.1 // indirect
- cloud.google.com/go/compute/metadata v0.2.3 // indirect
- cloud.google.com/go/iam v1.1.7 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect
+ al.essio.dev/pkg/shellescape v1.5.1 // indirect
+ cloud.google.com/go/auth v0.14.0 // indirect
+ cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
+ cloud.google.com/go/compute/metadata v0.6.0 // indirect
+ cloud.google.com/go/iam v1.3.1 // indirect
+ dario.cat/mergo v1.0.1 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
- github.com/Azure/go-autorest/autorest/adal v0.9.23 // indirect
+ github.com/Azure/go-autorest/autorest/adal v0.9.24 // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
- github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
+ github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
- github.com/Masterminds/semver/v3 v3.2.1 // indirect
- github.com/Masterminds/sprig/v3 v3.2.3 // indirect
- github.com/alessio/shellescape v1.4.2 // indirect
+ github.com/Masterminds/semver/v3 v3.3.1 // indirect
+ github.com/Masterminds/sprig/v3 v3.3.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
- github.com/cenkalti/backoff/v3 v3.2.2 // indirect
+ github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
- github.com/danieljoos/wincred v1.2.1 // indirect
- github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/danieljoos/wincred v1.2.2 // indirect
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
- github.com/emicklei/go-restful/v3 v3.12.0 // indirect
+ github.com/emicklei/go-restful/v3 v3.12.1 // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fluxcd/pkg/apis/acl v0.1.0 // indirect
github.com/fluxcd/pkg/apis/kustomize v1.2.0 // indirect
- github.com/fsnotify/fsnotify v1.7.0 // indirect
- github.com/go-jose/go-jose/v3 v3.0.3 // indirect
- github.com/go-logr/logr v1.4.1 // indirect
+ github.com/fsnotify/fsnotify v1.8.0 // indirect
+ github.com/fxamacker/cbor/v2 v2.7.0 // indirect
+ github.com/go-jose/go-jose/v4 v4.0.4 // indirect
+ github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/jsonreference v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
- github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
- github.com/goccy/go-json v0.10.2 // indirect
+ github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
+ github.com/goccy/go-json v0.10.4 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
- github.com/gofrs/flock v0.8.1 // indirect
+ github.com/gofrs/flock v0.12.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
- github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
+ github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang/protobuf v1.5.4 // indirect
- github.com/google/gnostic-models v0.6.8 // indirect
+ github.com/google/btree v1.1.3 // indirect
+ github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
- github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect
- github.com/google/s2a-go v0.1.7 // indirect
+ github.com/google/pprof v0.0.0-20250125003558-7fdb3d7e6fa0 // indirect
+ github.com/google/s2a-go v0.1.9 // indirect
github.com/google/uuid v1.6.0 // indirect
- github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
- github.com/googleapis/gax-go/v2 v2.12.3 // indirect
+ github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
+ github.com/googleapis/gax-go/v2 v2.14.1 // indirect
+ github.com/gorilla/websocket v1.5.0 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
- github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
+ github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
- github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 // indirect
+ github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9 // indirect
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
- github.com/hashicorp/go-sockaddr v1.0.6 // indirect
- github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
- github.com/huandu/xstrings v1.4.0 // indirect
+ github.com/hashicorp/go-sockaddr v1.0.7 // indirect
+ github.com/hashicorp/hcl v1.0.1-vault-7 // indirect
+ github.com/huandu/xstrings v1.5.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
+ github.com/klauspost/compress v1.17.11 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
github.com/lestrrat-go/httpcc v1.0.1 // indirect
- github.com/lestrrat-go/httprc v1.0.5 // indirect
+ github.com/lestrrat-go/httprc v1.0.6 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect
- github.com/lestrrat-go/jwx/v2 v2.0.21 // indirect
+ github.com/lestrrat-go/jwx/v2 v2.1.3 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect
- github.com/mailru/easyjson v0.7.7 // indirect
+ github.com/mailru/easyjson v0.9.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mitchellh/reflectwalk v1.0.2 // indirect
- github.com/moby/spdystream v0.2.0 // indirect
+ github.com/moby/spdystream v0.4.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
+ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
- github.com/prometheus/client_golang v1.19.0 // indirect
+ github.com/prometheus/client_golang v1.20.5 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
- github.com/prometheus/common v0.52.3 // indirect
- github.com/prometheus/procfs v0.13.0 // indirect
+ github.com/prometheus/common v0.62.0 // indirect
+ github.com/prometheus/procfs v0.15.1 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
- github.com/sony/gobreaker v0.5.0 // indirect
- github.com/spf13/cast v1.6.0 // indirect
+ github.com/sony/gobreaker v1.0.0 // indirect
+ github.com/spf13/cast v1.7.1 // indirect
github.com/spf13/pflag v1.0.5 // indirect
- github.com/tidwall/gjson v1.17.1 // indirect
+ github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/sjson v1.2.5 // indirect
- github.com/zalando/go-keyring v0.2.4 // indirect
- go.opencensus.io v0.24.0 // indirect
- go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.50.0 // indirect
- go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 // indirect
- go.opentelemetry.io/otel v1.25.0 // indirect
- go.opentelemetry.io/otel/metric v1.25.0 // indirect
- go.opentelemetry.io/otel/trace v1.25.0 // indirect
- golang.org/x/crypto v0.22.0 // indirect
- golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 // indirect
- golang.org/x/net v0.24.0 // indirect
- golang.org/x/sync v0.7.0 // indirect
- golang.org/x/sys v0.19.0 // indirect
- golang.org/x/term v0.19.0 // indirect
- golang.org/x/text v0.14.0 // indirect
- golang.org/x/time v0.5.0 // indirect
- golang.org/x/tools v0.20.0 // indirect
+ github.com/x448/float16 v0.8.4 // indirect
+ github.com/zalando/go-keyring v0.2.6 // indirect
+ go.opentelemetry.io/auto/sdk v1.1.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
+ go.opentelemetry.io/otel v1.34.0 // indirect
+ go.opentelemetry.io/otel/metric v1.34.0 // indirect
+ go.opentelemetry.io/otel/trace v1.34.0 // indirect
+ golang.org/x/crypto v0.32.0 // indirect
+ golang.org/x/net v0.34.0 // indirect
+ golang.org/x/sync v0.10.0 // indirect
+ golang.org/x/sys v0.29.0 // indirect
+ golang.org/x/term v0.28.0 // indirect
+ golang.org/x/text v0.21.0 // indirect
+ golang.org/x/time v0.9.0 // indirect
+ golang.org/x/tools v0.29.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
- google.golang.org/genproto v0.0.0-20240412170617-26222e5d3d56 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20240412170617-26222e5d3d56 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20240412170617-26222e5d3d56 // indirect
- google.golang.org/grpc v1.63.2 // indirect
- google.golang.org/protobuf v1.33.0 // indirect
+ google.golang.org/genproto v0.0.0-20250124145028-65684f501c47 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 // indirect
+ google.golang.org/grpc v1.70.0 // indirect
+ google.golang.org/protobuf v1.36.4 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919 // indirect
- k8s.io/component-base v0.29.3 // indirect
- k8s.io/klog/v2 v2.120.1 // indirect
- k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3 // indirect
- sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
- sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
+ k8s.io/klog/v2 v2.130.1 // indirect
+ k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
+ sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
+ sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
)
diff --git a/e2e/go.sum b/e2e/go.sum
index 949fdaeb868..f12c0f1063c 100644
--- a/e2e/go.sum
+++ b/e2e/go.sum
@@ -1,3 +1,5 @@
+al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXyho=
+al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@@ -18,60 +20,66 @@ cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmW
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
-cloud.google.com/go v0.112.2 h1:ZaGT6LiG7dBzi6zNOvVZwacaXlmf3lRqnC4DQzqyRQw=
-cloud.google.com/go v0.112.2/go.mod h1:iEqjp//KquGIJV/m+Pk3xecgKNhV+ry+vVTsy4TbDms=
+cloud.google.com/go v0.118.0 h1:tvZe1mgqRxpiVa3XlIGMiPcEUbP1gNXELgD4y/IXmeQ=
+cloud.google.com/go v0.118.0/go.mod h1:zIt2pkedt/mo+DQjcT4/L3NDxzHPR29j5HcclNH+9PM=
+cloud.google.com/go/auth v0.14.0 h1:A5C4dKV/Spdvxcl0ggWwWEzzP7AZMJSEIgrkngwhGYM=
+cloud.google.com/go/auth v0.14.0/go.mod h1:CYsoRL1PdiDuqeQpZE0bP2pnPrGqFcOkI0nldEQis+A=
+cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
+cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
-cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU=
-cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls=
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
-cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
-cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
+cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
+cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
-cloud.google.com/go/iam v1.1.7 h1:z4VHOhwKLF/+UYXAJDFwGtNF0b6gjsW1Pk9Ml0U/IoM=
-cloud.google.com/go/iam v1.1.7/go.mod h1:J4PMPg8TtyurAUvSmPj8FF3EDgY1SPRZxcUGrn7WXGA=
+cloud.google.com/go/iam v1.3.1 h1:KFf8SaT71yYq+sQtRISn90Gyhyf4X8RGgeAVC8XGf3E=
+cloud.google.com/go/iam v1.3.1/go.mod h1:3wMtuyT4NcbnYNPLMBzYRFiEfjKfJlLVLrisE7bwm34=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
-cloud.google.com/go/secretmanager v1.12.0 h1:e5pIo/QEgiFiHPVJPxM5jbtUr4O/u5h2zLHYtkFQr24=
-cloud.google.com/go/secretmanager v1.12.0/go.mod h1:Y1Gne3Ag+fZ2TDTiJc8ZJCMFbi7k1rYT4Rw30GXfvlk=
+cloud.google.com/go/secretmanager v1.14.3 h1:XVGHbcXEsbrgi4XHzgK5np81l1eO7O72WOXHhXUemrM=
+cloud.google.com/go/secretmanager v1.14.3/go.mod h1:Pwzcfn69Ni9Lrk1/XBzo1H9+MCJwJ6CDCoeoQUsMN+c=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
+dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
+dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
+dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0/go.mod h1:3Ug6Qzto9anB6mGlEdgYMDF5zHQ+wwhEaYR4s17PHMw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2 h1:FDif4R1+UUR+00q6wquyX90K7A8dN+R5E8GEadoP7sU=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2/go.mod h1:aiYBYui4BJ/BJCAIKs92XiPyQfTaBWqvHujDwKb6CBU=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 h1:1mvYtZfWQAnwNah/C+Z+Jb9rQH95LPE2vlmMuWAHJk8=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1/go.mod h1:75I/mXtme1JyWFtz8GocPHVFyH421IBoZErnO16dd0k=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1 h1:Bk5uOhSAenHyR5P61D/NzeQCv+4fEVV8mOkJ82NqpWw=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1/go.mod h1:QZ4pw3or1WPmRBxf0cHd1tknzrT54WPBOQoGutCPvSU=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aMclParm9/5Vgp+TY51uBQ=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
-github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
+github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=
github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw=
github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk=
-github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8=
-github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c=
-github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 h1:wkAZRgT/pn8HhFyzfe9UnqOjJYqlembgCTi72Bm/xKk=
-github.com/Azure/go-autorest/autorest/azure/auth v0.5.12/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg=
-github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg=
+github.com/Azure/go-autorest/autorest/adal v0.9.24 h1:BHZfgGsGwdkHDyZdtQRQk1WeUdW0m2WPAwuHZwUi5i4=
+github.com/Azure/go-autorest/autorest/adal v0.9.24/go.mod h1:7T1+g0PYFmACYW5LlG2fcoPiPlFHjClyRGL7dRlP5c8=
+github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 h1:Ov8avRZi2vmrE2JcXw+tu5K/yB41r7xK9GZDiBF7NdM=
+github.com/Azure/go-autorest/autorest/azure/auth v0.5.13/go.mod h1:5BAVfWLWXihP47vYrPuBKKf4cS0bXI+KM9Qx6ETDJYo=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 h1:w77/uPk80ZET2F+AfQExZyEWtn+0Rk/uw17m9fv5Ajc=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
@@ -87,39 +95,39 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
+github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
+github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/DelineaXPM/dsv-sdk-go/v2 v2.1.2 h1:cmX2QC9s5kPqmghWLLZP8YRFO1ZD/C59BpNH2ujP99w=
-github.com/DelineaXPM/dsv-sdk-go/v2 v2.1.2/go.mod h1:tNlpIXJlIwQlRbobXDPme4qv/Rc8+a1GbuUhE3m4JhQ=
+github.com/DelineaXPM/dsv-sdk-go/v2 v2.2.0 h1:62E66sDf+Hs1TChuu3R7d+0U5s7yV84QIOvvnfxtUJM=
+github.com/DelineaXPM/dsv-sdk-go/v2 v2.2.0/go.mod h1:58Pflli0BtqeF0VgluDSSVE5QlIfLOJvat0JSvo/d70=
+github.com/DelineaXPM/tss-sdk-go/v2 v2.0.3 h1:Yk8VZUIer8deRzi1Zx2Di2wEpw138IP09O5eKUYmDRs=
+github.com/DelineaXPM/tss-sdk-go/v2 v2.0.3/go.mod h1:xz6FXP2Do88Vc5Hx7OamZgZC1W45yfmLy4+iDKxlGXo=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
-github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
-github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
-github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
-github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
+github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
+github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.5 h1:ly0WKARATneFzwBlTZ2lUyjtLqoOEYqt1vOlf89za/4=
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.5/go.mod h1:W6DMNwPyIE3jpXDaJOvCKUT/kHPZrpl/BGiIVUILbMk=
github.com/akeylesslabs/akeyless-go/v3 v3.6.3 h1:fMF8SMDiBL9CufVjLUyF1Z+Z04t5CC3KGOROSjaJ/eA=
github.com/akeylesslabs/akeyless-go/v3 v3.6.3/go.mod h1:xcSXQWFRzKupIPCFRd9/mFYW0lHnDnWVvMD/pQ0x7sU=
-github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0=
-github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
github.com/aliyun/alibaba-cloud-sdk-go v1.62.271 h1:0QmSDMovuCyUbYp70MZHoTi/GYnHb/wYEIIBqoVsCjs=
github.com/aliyun/alibaba-cloud-sdk-go v1.62.271/go.mod h1:Api2AkmMgGaSUAhmk76oaFObkoeCPc/bKAqcyplPODs=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/aws/aws-sdk-go v1.41.13/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
-github.com/aws/aws-sdk-go v1.51.21 h1:UrT6JC9R9PkYYXDZBV0qDKTualMr+bfK2eboTknMgbs=
-github.com/aws/aws-sdk-go v1.51.21/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
+github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk=
+github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
-github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
-github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -130,22 +138,24 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cyberark/conjur-api-go v0.11.1 h1:vjaMkw0geJsA+ikMM6UDLg4VLFQWKo/B0i9IWlOQ1f0=
-github.com/cyberark/conjur-api-go v0.11.1/go.mod h1:n1p46Hj9l8wkZjM17cVYdfcatyPboWyioLGlC0QszCs=
-github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8GQs=
-github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps=
+github.com/cyberark/conjur-api-go v0.12.10 h1:exseTvvp7l4Fhw6RTE0kq9Ddipsk+941k945Nyoq8CE=
+github.com/cyberark/conjur-api-go v0.12.10/go.mod h1:XNoyT5ZBLJAGjqXmelLv+eYMG4QxYkZWiw1zld3m0QQ=
+github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0=
+github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
-github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
-github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk=
-github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
+github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU=
+github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
@@ -157,8 +167,10 @@ github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCv
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
-github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
-github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
+github.com/external-secrets/sprig/v3 v3.3.0 h1:uO5rmIKSjjONthpCIU8xKbBpAJd0zL/6XFEdC+JsSqU=
+github.com/external-secrets/sprig/v3 v3.3.0/go.mod h1:tvPBN33djer3sQffmfEfcQdL5VYKYmetb4Zbe6wtAq8=
+github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
+github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fluxcd/helm-controller/api v0.37.2 h1:tkLezpRdqPDz7HoKHFu92sV+ppOCVDxkjFTh8/lpff8=
@@ -173,16 +185,18 @@ github.com/fluxcd/source-controller/api v1.2.3 h1:71mXv3Qg9HEhcpqOq1ObmoE+P/HuZN
github.com/fluxcd/source-controller/api v1.2.3/go.mod h1:5gaIVVH7hgb8p3HKFp8P6hGmZEC8fKSt4EcrG3g5vZI=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
-github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
-github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
+github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
+github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
+github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
-github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
+github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
+github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
-github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
@@ -193,23 +207,25 @@ github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
-github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
-github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
-github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
-github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
+github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
+github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
+github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
-github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
+github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
@@ -217,8 +233,8 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
+github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
@@ -248,8 +264,10 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
-github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
+github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
+github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
+github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
+github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -284,56 +302,57 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
-github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
+github.com/google/pprof v0.0.0-20250125003558-7fdb3d7e6fa0 h1:my2ucqBZmv+cWHIhZNSIYKzgN8EBGyHdC7zD5sASRAg=
+github.com/google/pprof v0.0.0-20250125003558-7fdb3d7e6fa0/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
-github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
+github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
+github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
-github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
+github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
+github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA=
-github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4=
-github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
+github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
+github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
+github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
-github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
-github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
-github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
+github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
+github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
-github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 h1:iBt4Ew4XEGLfh6/bPk4rSYmuZJGizr6/x/AEizP0CQc=
-github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8/go.mod h1:aiJI+PIApBRQG7FZTEBx5GiiX+HbOHilUdNxUZi4eV0=
+github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9 h1:FW0YttEnUNDJ2WL9XcrrfteS1xW8u+sh4ggM8pN5isQ=
+github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
-github.com/hashicorp/go-sockaddr v1.0.6 h1:RSG8rKU28VTUTvEKghe5gIhIQpv8evvNpnDEyqO4u9I=
-github.com/hashicorp/go-sockaddr v1.0.6/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI=
+github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw=
+github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
-github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM=
-github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
-github.com/hashicorp/vault/api v1.12.2 h1:7YkCTE5Ni90TcmYHDBExdt4WGJxhpzaHqR6uGbQb/rE=
-github.com/hashicorp/vault/api v1.12.2/go.mod h1:LSGf1NGT1BnvFFnKVtnvcaLBM2Lz+gJdpL6HUYed8KE=
+github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I=
+github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
+github.com/hashicorp/vault/api v1.15.0 h1:O24FYQCWwhwKnF7CuSqP30S51rTV7vz1iACXE/pj5DA=
+github.com/hashicorp/vault/api v1.15.0/go.mod h1:+5YTO09JGn0u+b6ySD/LLVf8WkJCPLAL2Vkmrn2+CM8=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
-github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
-github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
+github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
@@ -348,8 +367,12 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
+github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
+github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
+github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
@@ -363,18 +386,18 @@ github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
-github.com/lestrrat-go/httprc v1.0.5 h1:bsTfiH8xaKOJPrg1R+E3iE/AWZr/x0Phj9PBTG/OLUk=
-github.com/lestrrat-go/httprc v1.0.5/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
+github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k=
+github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
-github.com/lestrrat-go/jwx/v2 v2.0.21 h1:jAPKupy4uHgrHFEdjVjNkUgoBKtVDgrQPB/h55FHrR0=
-github.com/lestrrat-go/jwx/v2 v2.0.21/go.mod h1:09mLW8zto6bWL9GbwnqAli+ArLf+5M33QLQPDggkUWM=
+github.com/lestrrat-go/jwx/v2 v2.1.3 h1:Ud4lb2QuxRClYAmRleF50KrbKIoM1TddXgBrneT5/Jo=
+github.com/lestrrat-go/jwx/v2 v2.1.3/go.mod h1:q6uFgbgZfEmQrfJfrCo90QcQOcXFMfbI/fO0NqRtvZo=
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
-github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
-github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
+github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
+github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
@@ -387,8 +410,8 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
-github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8=
-github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
+github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8=
+github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -399,37 +422,42 @@ github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3P
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
-github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
-github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
-github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
+github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus=
+github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
+github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
+github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
+github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
+github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
-github.com/oracle/oci-go-sdk/v65 v65.63.1 h1:dYL7sk9L1+C9LCmoq+zjPMNteuJJfk54YExq/4pV9xQ=
-github.com/oracle/oci-go-sdk/v65 v65.63.1/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0=
+github.com/oracle/oci-go-sdk/v65 v65.81.3 h1:L4JcHSV4xLxySfZOQumUazlRN/2u/7r7Muw0Apg7UYI=
+github.com/oracle/oci-go-sdk/v65 v65.81.3/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
-github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
+github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.52.3 h1:5f8uj6ZwHSscOGNdIQg6OiZv/ybiK2CO2q2drVZAQSA=
-github.com/prometheus/common v0.52.3/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U=
-github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
-github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
+github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
+github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
+github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
+github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
-github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
+github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
-github.com/scaleway/scaleway-sdk-go v1.0.0-beta.26 h1:F+GIVtGqCFxPxO46ujf8cEOP574MBoRm3gNbPXECbxs=
-github.com/scaleway/scaleway-sdk-go v1.0.0-beta.26/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
+github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30 h1:yoKAVkEVwAqbGbR8n87rHQ1dulL25rKloGadb3vm770=
+github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30/go.mod h1:sH0u6fq6x4R5M7WxkoQFY/o7UaiItec0o1LinLCJNq8=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
@@ -437,17 +465,19 @@ github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
+github.com/sony/gobreaker v1.0.0 h1:feX5fGGXSl3dYd4aHZItw+FpHLvvoaqkawKjVNiFMNQ=
+github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
-github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
+github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
+github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -456,14 +486,13 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
-github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
-github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
+github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
@@ -475,15 +504,17 @@ github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaO
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
-github.com/xanzy/go-gitlab v0.102.0 h1:ExHuJ1OTQ2yt25zBMMj0G96ChBirGYv8U7HyUiYkZ+4=
-github.com/xanzy/go-gitlab v0.102.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
+github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
+github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-github.com/zalando/go-keyring v0.2.4 h1:wi2xxTqdiwMKbM6TWwi+uJCG/Tum2UV0jqaQhCa9/68=
-github.com/zalando/go-keyring v0.2.4/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=
+github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s=
+github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI=
+gitlab.com/gitlab-org/api/client-go v0.120.0 h1:geCJjojDXxWVmUcTxPcOUCenAWElWB5dVfX3HJGeAMc=
+gitlab.com/gitlab-org/api/client-go v0.120.0/go.mod h1:ygHmS3AU3TpvK+AC6DYO1QuAxLlv6yxYK+/Votr/WFQ=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -493,18 +524,22 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.50.0 h1:zvpPXY7RfYAGSdYQLjp6zxdJNSYD/+FFoCTQN9IPxBs=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.50.0/go.mod h1:BMn8NB1vsxTljvuorms2hyOs8IBuuBEq0pl7ltOfy30=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 h1:cEPbyTSEHlQR89XVlyo78gqluF8Y3oMeBkXGWzQsfXY=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0/go.mod h1:DKdbWcT4GH1D0Y3Sqt/PFXt2naRKDWtU+eE6oLdFNA8=
-go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k=
-go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg=
-go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA=
-go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s=
-go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw=
-go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc=
-go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM=
-go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I=
+go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
+go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
+go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
+go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
+go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
+go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
+go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
@@ -520,16 +555,15 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
-golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
-golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
-golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
+golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
+golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -540,8 +574,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc=
-golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -610,8 +642,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
-golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
-golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
+golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
+golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -625,8 +657,8 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
-golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg=
-golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8=
+golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
+golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -640,8 +672,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
-golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
+golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
+golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -691,9 +723,8 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
-golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
+golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
@@ -702,9 +733,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
-golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
-golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
-golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
+golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
+golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -718,13 +748,14 @@ golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
+golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
-golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
+golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -775,8 +806,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
-golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
+golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
+golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -805,8 +836,8 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.45.0/go.mod h1:ISLIJCedJolbZvDfAk+Ctuq5hf+aJ33WgtUsfyFoLXA=
-google.golang.org/api v0.172.0 h1:/1OcMZGPmW1rX2LCu2CmGUD1KXK1+pfzxotxyRUCCdk=
-google.golang.org/api v0.172.0/go.mod h1:+fJZq6QXWfa9pXhnIzsjx4yI22d4aI9ZpLb58gvXjis=
+google.golang.org/api v0.218.0 h1:x6JCjEWeZ9PFCRe9z0FBrNwj7pB7DOAqT35N+IPnAUA=
+google.golang.org/api v0.218.0/go.mod h1:5VGHBAkxrA/8EFjLVEYmMUJ8/8+gWWQ3s4cFH0FxG2M=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -854,12 +885,12 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210413151531-c14fb6ef47c3/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
-google.golang.org/genproto v0.0.0-20240412170617-26222e5d3d56 h1:LlcUFJ4BLmJVS4Kly+WCK7LQqcevmycHj88EPgyhNx8=
-google.golang.org/genproto v0.0.0-20240412170617-26222e5d3d56/go.mod h1:n1CaIKYMIlxFt1zJE/1kU40YpSL0drGMbl0Idum1VSs=
-google.golang.org/genproto/googleapis/api v0.0.0-20240412170617-26222e5d3d56 h1:KuFzeG+qPmpT8KpJXcrKAyeHhn64dgEICWlccP9qp0U=
-google.golang.org/genproto/googleapis/api v0.0.0-20240412170617-26222e5d3d56/go.mod h1:wTHjrkbcS8AoQbb/0v9bFIPItZQPAsyVfgG9YPUhjAM=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240412170617-26222e5d3d56 h1:zviK8GX4VlMstrK3JkexM5UHjH1VOkRebH9y3jhSBGk=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240412170617-26222e5d3d56/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
+google.golang.org/genproto v0.0.0-20250124145028-65684f501c47 h1:SI8Hf7K4+uVYchXqZiMfP44PZ83xomMWovbcFfm0P8Q=
+google.golang.org/genproto v0.0.0-20250124145028-65684f501c47/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE=
+google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g=
+google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 h1:91mG8dNTpkC0uChJUQ9zCiRqx3GEEFOWaRZ0mI6Oj2I=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -879,8 +910,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
-google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -894,13 +925,15 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
-google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
+google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
+gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@@ -909,7 +942,6 @@ gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -925,32 +957,30 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-k8s.io/api v0.28.1 h1:i+0O8k2NPBCPYaMB+uCkseEbawEt/eFaiRqUx8aB108=
-k8s.io/api v0.28.1/go.mod h1:uBYwID+66wiL28Kn2tBjBYQdEU0Xk0z5qF8bIBqk/Dg=
-k8s.io/apiextensions-apiserver v0.28.1 h1:l2ThkBRjrWpw4f24uq0Da2HaEgqJZ7pcgiEUTKSmQZw=
-k8s.io/apiextensions-apiserver v0.28.1/go.mod h1:sVvrI+P4vxh2YBBcm8n2ThjNyzU4BQGilCQ/JAY5kGs=
-k8s.io/apimachinery v0.28.1 h1:EJD40og3GizBSV3mkIoXQBsws32okPOy+MkRyzh6nPY=
-k8s.io/apimachinery v0.28.1/go.mod h1:X0xh/chESs2hP9koe+SdIAcXWcQ+RM5hy0ZynB+yEvw=
-k8s.io/client-go v0.28.1 h1:pRhMzB8HyLfVwpngWKE8hDcXRqifh1ga2Z/PU9SXVK8=
-k8s.io/client-go v0.28.1/go.mod h1:pEZA3FqOsVkCc07pFVzK076R+P/eXqsgx5zuuRWukNE=
-k8s.io/component-base v0.28.1 h1:LA4AujMlK2mr0tZbQDZkjWbdhTV5bRyEyAFe0TJxlWg=
-k8s.io/component-base v0.28.1/go.mod h1:jI11OyhbX21Qtbav7JkhehyBsIRfnO8oEgoAR12ArIU=
-k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
-k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
-k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3 h1:SbdLaI6mM6ffDSJCadEaD4IkuPzepLDGlkd2xV0t1uA=
-k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
-k8s.io/utils v0.0.0-20240310230437-4693a0247e57 h1:gbqbevonBh57eILzModw6mrkbwM0gQBEuevE/AaBsHY=
-k8s.io/utils v0.0.0-20240310230437-4693a0247e57/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
+k8s.io/api v0.31.0 h1:b9LiSjR2ym/SzTOlfMHm1tr7/21aD7fSkqgD/CVJBCo=
+k8s.io/api v0.31.0/go.mod h1:0YiFF+JfFxMM6+1hQei8FY8M7s1Mth+z/q7eF1aJkTE=
+k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk=
+k8s.io/apiextensions-apiserver v0.31.0/go.mod h1:b9aMDEYaEe5sdK+1T0KU78ApR/5ZVp4i56VacZYEHxk=
+k8s.io/apimachinery v0.31.0 h1:m9jOiSr3FoSSL5WO9bjm1n6B9KROYYgNZOb4tyZ1lBc=
+k8s.io/apimachinery v0.31.0/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
+k8s.io/client-go v0.31.0 h1:QqEJzNjbN2Yv1H79SsS+SWnXkBgVu4Pj3CJQgbx0gI8=
+k8s.io/client-go v0.31.0/go.mod h1:Y9wvC76g4fLjmU0BA+rV+h2cncoadjvjjkkIGoTLcGU=
+k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
+k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
+k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg=
+k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas=
+k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
+k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
-sigs.k8s.io/controller-runtime v0.17.3 h1:65QmN7r3FWgTxDMz9fvGnO1kbf2nu+acg9p2R9oYYYk=
-sigs.k8s.io/controller-runtime v0.17.3/go.mod h1:N0jpP5Lo7lMTF9aL56Z/B2oWBJjey6StQM0jRbKQXtY=
-sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
-sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
-sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
-sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
+sigs.k8s.io/controller-runtime v0.20.1 h1:JbGMAG/X94NeM3xvjenVUaBjy6Ui4Ogd/J5ZtjZnHaE=
+sigs.k8s.io/controller-runtime v0.20.1/go.mod h1:BrP3w158MwvB3ZbNpaAcIKkHQ7YGpYnzpoSTZ8E14WU=
+sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
+sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
+sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk=
+sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
-software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
-software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
+software.sslmate.com/src/go-pkcs12 v0.5.0 h1:EC6R394xgENTpZ4RltKydeDUjtlM5drOYIG9c6TVj2M=
+software.sslmate.com/src/go-pkcs12 v0.5.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
diff --git a/e2e/run.sh b/e2e/run.sh
index f71c134a1c2..52cebd976ec 100755
--- a/e2e/run.sh
+++ b/e2e/run.sh
@@ -84,6 +84,9 @@ kubectl run --rm \
--env="DELINEA_TENANT=${DELINEA_TENANT:-}" \
--env="DELINEA_CLIENT_ID=${DELINEA_CLIENT_ID:-}" \
--env="DELINEA_CLIENT_SECRET=${DELINEA_CLIENT_SECRET:-}" \
+ --env="SECRETSERVER_USERNAME=${SECRETSERVER_USERNAME:-}" \
+ --env="SECRETSERVER_PASSWORD=${SECRETSERVER_PASSWORD:-}" \
+ --env="SECRETSERVER_URL=${SECRETSERVER_URL:-}" \
--env="VERSION=${VERSION}" \
--env="TEST_SUITES=${TEST_SUITES}" \
--overrides='{ "apiVersion": "v1", "spec":{"serviceAccountName": "external-secrets-e2e"}}' \
diff --git a/e2e/suites/provider/cases/akeyless/provider.go b/e2e/suites/provider/cases/akeyless/provider.go
index c1bdb5c3f09..1117b10d242 100644
--- a/e2e/suites/provider/cases/akeyless/provider.go
+++ b/e2e/suites/provider/cases/akeyless/provider.go
@@ -187,10 +187,10 @@ func (a *akeylessProvider) GetToken() (string, error) {
}
authOut, _, err := a.restAPIClient.Auth(ctx).Body(*authBody).Execute()
+ if errors.As(err, &apiErr) {
+ return "", fmt.Errorf("authentication failed: %v", string(apiErr.Body()))
+ }
if err != nil {
- if errors.As(err, &apiErr) {
- return "", fmt.Errorf("authentication failed: %v", string(apiErr.Body()))
- }
return "", fmt.Errorf("authentication failed: %w", err)
}
diff --git a/e2e/suites/provider/cases/aws/common.go b/e2e/suites/provider/cases/aws/common.go
index 04459715acd..e941d5b6765 100644
--- a/e2e/suites/provider/cases/aws/common.go
+++ b/e2e/suites/provider/cases/aws/common.go
@@ -94,9 +94,17 @@ func newStaticStoreProvider(serviceType esv1beta1.AWSServiceType, region, secret
}
}
-// SessionTagsStore is namespaced and references
-// static credentials from a secret. It assumes a role and specifies session tags
-func SetupSessionTagsStore(f *framework.Framework, kid, sak, st, region, role string, sessionTags []*esv1beta1.Tag, serviceType esv1beta1.AWSServiceType) {
+type AccessOpts struct {
+ KID string
+ SAK string
+ ST string
+ Region string
+ Role string
+}
+
+// SetupSessionTagsStore is namespaced and references
+// static credentials from a secret. It assumes a Role and specifies session tags
+func SetupSessionTagsStore(f *framework.Framework, access AccessOpts, sessionTags []*esv1beta1.Tag, serviceType esv1beta1.AWSServiceType) {
credsName := "provider-secret-sess-tags"
awsCreds := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
@@ -104,9 +112,9 @@ func SetupSessionTagsStore(f *framework.Framework, kid, sak, st, region, role st
Namespace: f.Namespace.Name,
},
StringData: map[string]string{
- staticKeyID: kid,
- staticSecretAccessKey: sak,
- staticySessionToken: st,
+ staticKeyID: access.KID,
+ staticSecretAccessKey: access.SAK,
+ staticySessionToken: access.ST,
},
}
err := f.CRClient.Create(context.Background(), awsCreds)
@@ -118,16 +126,16 @@ func SetupSessionTagsStore(f *framework.Framework, kid, sak, st, region, role st
Namespace: f.Namespace.Name,
},
Spec: esv1beta1.SecretStoreSpec{
- Provider: newStaticStoreProvider(serviceType, region, credsName, role, "", sessionTags),
+ Provider: newStaticStoreProvider(serviceType, access.Region, credsName, access.Role, "", sessionTags),
},
}
err = f.CRClient.Create(context.Background(), secretStore)
Expect(err).ToNot(HaveOccurred())
}
-// ExternalIDStore is namespaced and references
+// SetupExternalIDStore is namespaced and references
// static credentials from a secret. It assumes a role and specifies an externalID
-func SetupExternalIDStore(f *framework.Framework, kid, sak, st, region, role, externalID string, sessionTags []*esv1beta1.Tag, serviceType esv1beta1.AWSServiceType) {
+func SetupExternalIDStore(f *framework.Framework, access AccessOpts, externalID string, sessionTags []*esv1beta1.Tag, serviceType esv1beta1.AWSServiceType) {
credsName := "provider-secret-ext-id"
awsCreds := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
@@ -135,9 +143,9 @@ func SetupExternalIDStore(f *framework.Framework, kid, sak, st, region, role, ex
Namespace: f.Namespace.Name,
},
StringData: map[string]string{
- staticKeyID: kid,
- staticSecretAccessKey: sak,
- staticySessionToken: st,
+ staticKeyID: access.KID,
+ staticSecretAccessKey: access.SAK,
+ staticySessionToken: access.ST,
},
}
err := f.CRClient.Create(context.Background(), awsCreds)
@@ -149,25 +157,25 @@ func SetupExternalIDStore(f *framework.Framework, kid, sak, st, region, role, ex
Namespace: f.Namespace.Name,
},
Spec: esv1beta1.SecretStoreSpec{
- Provider: newStaticStoreProvider(serviceType, region, credsName, role, externalID, sessionTags),
+ Provider: newStaticStoreProvider(serviceType, access.Region, credsName, access.Role, externalID, sessionTags),
},
}
err = f.CRClient.Create(context.Background(), secretStore)
Expect(err).ToNot(HaveOccurred())
}
-// StaticStore is namespaced and references
+// SetupStaticStore is namespaced and references
// static credentials from a secret.
-func SetupStaticStore(f *framework.Framework, kid, sak, st, region string, serviceType esv1beta1.AWSServiceType) {
+func SetupStaticStore(f *framework.Framework, access AccessOpts, serviceType esv1beta1.AWSServiceType) {
awsCreds := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: StaticCredentialsSecretName,
Namespace: f.Namespace.Name,
},
StringData: map[string]string{
- staticKeyID: kid,
- staticSecretAccessKey: sak,
- staticySessionToken: st,
+ staticKeyID: access.KID,
+ staticSecretAccessKey: access.SAK,
+ staticySessionToken: access.ST,
},
}
err := f.CRClient.Create(context.Background(), awsCreds)
@@ -179,7 +187,7 @@ func SetupStaticStore(f *framework.Framework, kid, sak, st, region string, servi
Namespace: f.Namespace.Name,
},
Spec: esv1beta1.SecretStoreSpec{
- Provider: newStaticStoreProvider(serviceType, region, StaticCredentialsSecretName, "", "", nil),
+ Provider: newStaticStoreProvider(serviceType, access.Region, StaticCredentialsSecretName, "", "", nil),
},
}
err = f.CRClient.Create(context.Background(), secretStore)
@@ -188,7 +196,7 @@ func SetupStaticStore(f *framework.Framework, kid, sak, st, region string, servi
// CreateReferentStaticStore creates a CSS with referent auth and
// creates a secret with static authentication credentials in the ExternalSecret namespace.
-func CreateReferentStaticStore(f *framework.Framework, kid, sak, st, region string, serviceType esv1beta1.AWSServiceType) {
+func CreateReferentStaticStore(f *framework.Framework, access AccessOpts, serviceType esv1beta1.AWSServiceType) {
ns := f.Namespace.Name
awsCreds := &corev1.Secret{
@@ -197,9 +205,9 @@ func CreateReferentStaticStore(f *framework.Framework, kid, sak, st, region stri
Namespace: ns,
},
StringData: map[string]string{
- staticKeyID: kid,
- staticSecretAccessKey: sak,
- staticySessionToken: st,
+ staticKeyID: access.KID,
+ staticSecretAccessKey: access.SAK,
+ staticySessionToken: access.ST,
},
}
err := f.CRClient.Create(context.Background(), awsCreds)
@@ -210,7 +218,7 @@ func CreateReferentStaticStore(f *framework.Framework, kid, sak, st, region stri
Name: ReferentSecretStoreName(f),
},
Spec: esv1beta1.SecretStoreSpec{
- Provider: newStaticStoreProvider(serviceType, region, StaticReferentCredentialsSecretName, "", "", nil),
+ Provider: newStaticStoreProvider(serviceType, access.Region, StaticReferentCredentialsSecretName, "", "", nil),
},
}
err = f.CRClient.Create(context.Background(), secretStore)
diff --git a/e2e/suites/provider/cases/aws/parameterstore/provider.go b/e2e/suites/provider/cases/aws/parameterstore/provider.go
index 01183e3d5dd..5ba30330885 100644
--- a/e2e/suites/provider/cases/aws/parameterstore/provider.go
+++ b/e2e/suites/provider/cases/aws/parameterstore/provider.go
@@ -68,8 +68,8 @@ func NewProvider(f *framework.Framework, kid, sak, st, region, saName, saNamespa
}
BeforeEach(func() {
- awscommon.SetupStaticStore(f, kid, sak, st, region, esv1beta1.AWSServiceParameterStore)
- awscommon.CreateReferentStaticStore(f, kid, sak, st, region, esv1beta1.AWSServiceParameterStore)
+ awscommon.SetupStaticStore(f, awscommon.AccessOpts{KID: kid, SAK: sak, ST: st, Region: region}, esv1beta1.AWSServiceParameterStore)
+ awscommon.CreateReferentStaticStore(f, awscommon.AccessOpts{KID: kid, SAK: sak, ST: st, Region: region}, esv1beta1.AWSServiceParameterStore)
prov.SetupReferencedIRSAStore()
prov.SetupMountedIRSAStore()
})
diff --git a/e2e/suites/provider/cases/aws/secretsmanager/provider.go b/e2e/suites/provider/cases/aws/secretsmanager/provider.go
index 717a14803bc..6633b21f0f9 100644
--- a/e2e/suites/provider/cases/aws/secretsmanager/provider.go
+++ b/e2e/suites/provider/cases/aws/secretsmanager/provider.go
@@ -69,10 +69,21 @@ func NewProvider(f *framework.Framework, kid, sak, st, region, saName, saNamespa
}
BeforeEach(func() {
- awscommon.SetupStaticStore(f, kid, sak, st, region, esv1beta1.AWSServiceSecretsManager)
- awscommon.SetupExternalIDStore(f, kid, sak, st, region, awscommon.IAMRoleExternalID, awscommon.IAMTrustedExternalID, nil, esv1beta1.AWSServiceSecretsManager)
- awscommon.SetupSessionTagsStore(f, kid, sak, st, region, awscommon.IAMRoleSessionTags, nil, esv1beta1.AWSServiceSecretsManager)
- awscommon.CreateReferentStaticStore(f, kid, sak, st, region, esv1beta1.AWSServiceSecretsManager)
+ awscommon.SetupStaticStore(f, awscommon.AccessOpts{KID: kid, SAK: sak, ST: st, Region: region}, esv1beta1.AWSServiceSecretsManager)
+ awscommon.SetupExternalIDStore(
+ f,
+ awscommon.AccessOpts{KID: kid, SAK: sak, ST: st, Region: region, Role: awscommon.IAMRoleExternalID},
+ awscommon.IAMTrustedExternalID,
+ nil,
+ esv1beta1.AWSServiceSecretsManager,
+ )
+ awscommon.SetupSessionTagsStore(
+ f,
+ awscommon.AccessOpts{KID: kid, SAK: sak, ST: st, Region: region, Role: awscommon.IAMRoleSessionTags},
+ nil,
+ esv1beta1.AWSServiceSecretsManager,
+ )
+ awscommon.CreateReferentStaticStore(f, awscommon.AccessOpts{KID: kid, SAK: sak, ST: st, Region: region}, esv1beta1.AWSServiceSecretsManager)
prov.SetupReferencedIRSAStore()
prov.SetupMountedIRSAStore()
})
diff --git a/e2e/suites/provider/cases/aws/secretsmanager/secretsmanager.go b/e2e/suites/provider/cases/aws/secretsmanager/secretsmanager.go
index 67930ba06e1..cedaac83cd0 100644
--- a/e2e/suites/provider/cases/aws/secretsmanager/secretsmanager.go
+++ b/e2e/suites/provider/cases/aws/secretsmanager/secretsmanager.go
@@ -26,10 +26,9 @@ import (
)
const (
- withStaticAuth = "with static auth"
- withExtID = "with externalID"
- withSessionTags = "with session tags"
- withReferentStaticAuth = "with static referent auth"
+ withStaticAuth = "with static auth"
+ withExtID = "with externalID"
+ withSessionTags = "with session tags"
)
var _ = Describe("[aws] ", Label("aws", "secretsmanager"), func() {
diff --git a/e2e/suites/provider/cases/gitlab/provider.go b/e2e/suites/provider/cases/gitlab/provider.go
index 094703f85fd..eca1903856b 100644
--- a/e2e/suites/provider/cases/gitlab/provider.go
+++ b/e2e/suites/provider/cases/gitlab/provider.go
@@ -21,10 +21,10 @@ import (
// nolint
. "github.com/onsi/ginkgo/v2"
+ gitlab "gitlab.com/gitlab-org/api/client-go"
// nolint
. "github.com/onsi/gomega"
- "github.com/xanzy/go-gitlab"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
diff --git a/e2e/suites/provider/cases/import.go b/e2e/suites/provider/cases/import.go
index e8561c56161..5a20e529f55 100644
--- a/e2e/suites/provider/cases/import.go
+++ b/e2e/suites/provider/cases/import.go
@@ -27,4 +27,5 @@ import (
_ "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/template"
_ "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/vault"
_ "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/conjur"
+ _ "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/secretserver"
)
diff --git a/e2e/suites/provider/cases/secretserver/config.go b/e2e/suites/provider/cases/secretserver/config.go
new file mode 100644
index 00000000000..bb7bc865157
--- /dev/null
+++ b/e2e/suites/provider/cases/secretserver/config.go
@@ -0,0 +1,41 @@
+package secretserver
+
+import (
+ "fmt"
+ "os"
+)
+
+type config struct {
+ username string
+ password string
+ serverURL string
+}
+
+func loadConfigFromEnv() (*config, error) {
+ var cfg config
+ var err error
+
+ // Required settings
+ cfg.username, err = getEnv("SECRETSERVER_USERNAME")
+ if err != nil {
+ return nil, err
+ }
+ cfg.password, err = getEnv("SECRETSERVER_PASSWORD")
+ if err != nil {
+ return nil, err
+ }
+ cfg.serverURL, err = getEnv("SECRETSERVER_URL")
+ if err != nil {
+ return nil, err
+ }
+
+ return &cfg, nil
+}
+
+func getEnv(name string) (string, error) {
+ value, ok := os.LookupEnv(name)
+ if !ok {
+ return "", fmt.Errorf("environment variable %q is not set", name)
+ }
+ return value, nil
+}
diff --git a/e2e/suites/provider/cases/secretserver/provider.go b/e2e/suites/provider/cases/secretserver/provider.go
new file mode 100644
index 00000000000..9b1b7cf33af
--- /dev/null
+++ b/e2e/suites/provider/cases/secretserver/provider.go
@@ -0,0 +1,58 @@
+package secretserver
+
+import (
+ "encoding/json"
+
+ "github.com/DelineaXPM/tss-sdk-go/v2/server"
+ "github.com/external-secrets/external-secrets-e2e/framework"
+ "github.com/onsi/gomega"
+)
+
+
+type secretStoreProvider struct {
+ api *server.Server
+ cfg *config
+ framework *framework.Framework
+ secretID map[string]int
+}
+
+func (p *secretStoreProvider) init(cfg *config, f *framework.Framework) {
+ p.cfg = cfg
+ p.secretID = make(map[string]int)
+ p.framework = f
+ secretserverClient, err := server.New(server.Configuration{
+ Credentials: server.UserCredential{
+ Username: cfg.username,
+ Password: cfg.password,
+ },
+ ServerURL: cfg.serverURL,
+ })
+ gomega.Expect(err).ToNot(gomega.HaveOccurred())
+
+ p.api = secretserverClient
+}
+
+func (p *secretStoreProvider) CreateSecret(key string, val framework.SecretEntry) {
+ var data map[string]interface{}
+ err := json.Unmarshal([]byte(val.Value), &data)
+ gomega.Expect(err).ToNot(gomega.HaveOccurred())
+
+ fields := make([]server.SecretField, 1)
+ fields[0].FieldID = 329 // Data
+ fields[0].ItemValue = val.Value
+
+ s, err := p.api.CreateSecret(server.Secret{
+ SecretTemplateID: 6051, // custom template
+ SiteID: 1,
+ FolderID: 10,
+ Name: key,
+ Fields: fields,
+ })
+ gomega.Expect(err).ToNot(gomega.HaveOccurred())
+ p.secretID[key] = s.ID
+}
+
+func (p *secretStoreProvider) DeleteSecret(key string) {
+ err := p.api.DeleteSecret(p.secretID[key])
+ gomega.Expect(err).ToNot(gomega.HaveOccurred())
+}
diff --git a/e2e/suites/provider/cases/secretserver/secretserver.go b/e2e/suites/provider/cases/secretserver/secretserver.go
new file mode 100644
index 00000000000..f0ba2bfeed0
--- /dev/null
+++ b/e2e/suites/provider/cases/secretserver/secretserver.go
@@ -0,0 +1,92 @@
+package secretserver
+
+import (
+ "context"
+ _"fmt"
+ "github.com/external-secrets/external-secrets-e2e/framework"
+ "github.com/external-secrets/external-secrets-e2e/suites/provider/cases/common"
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+ "github.com/onsi/ginkgo/v2"
+ "github.com/onsi/gomega"
+ v1 "k8s.io/api/core/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+var _ = ginkgo.Describe("[secretserver]", ginkgo.Label("secretserver"), func() {
+
+ f := framework.New("eso-secretserver")
+
+ // Initialization is deferred so that assertions work.
+ provider := &secretStoreProvider{}
+
+ ginkgo.BeforeEach(func() {
+
+ cfg, err := loadConfigFromEnv()
+ gomega.Expect(err).ToNot(gomega.HaveOccurred())
+
+ provider.init(cfg, f)
+ createResources(context.Background(), f, cfg)
+ })
+
+ ginkgo.DescribeTable("sync secrets", framework.TableFuncWithExternalSecret(f, provider),
+ ginkgo.Entry(common.JSONDataWithTemplate(f)),
+ ginkgo.Entry(common.JSONDataWithProperty(f)),
+ ginkgo.Entry(common.JSONDataWithoutTargetName(f)),
+ ginkgo.Entry(common.JSONDataWithTemplateFromLiteral(f)),
+ ginkgo.Entry(common.TemplateFromConfigmaps(f)),
+ ginkgo.Entry(common.JSONDataFromSync(f)), // <--
+ ginkgo.Entry(common.JSONDataFromRewrite(f)), // <--
+ ginkgo.Entry(common.NestedJSONWithGJSON(f)),
+ ginkgo.Entry(common.DockerJSONConfig(f)),
+ ginkgo.Entry(common.DataPropertyDockerconfigJSON(f)),
+ ginkgo.Entry(common.SSHKeySyncDataProperty(f)),
+ ginkgo.Entry(common.DecodingPolicySync(f)), // <--
+ )
+})
+
+func createResources(ctx context.Context, f *framework.Framework, cfg *config) {
+
+ secretName := "secretserver-credential"
+ secretKey := "password"
+ // Creating a secret to hold the Delinea client secret.
+ secretSpec := v1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: secretName,
+ Namespace: f.Namespace.Name,
+ },
+ StringData: map[string]string{
+ secretKey: cfg.password,
+ },
+ }
+
+ err := f.CRClient.Create(ctx, &secretSpec)
+ gomega.Expect(err).ToNot(gomega.HaveOccurred())
+
+ // Creating SecretStore.
+ secretStoreSpec := esv1beta1.SecretStore{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: f.Namespace.Name,
+ Namespace: f.Namespace.Name,
+ },
+ Spec: esv1beta1.SecretStoreSpec{
+ Provider: &esv1beta1.SecretStoreProvider{
+ SecretServer: &esv1beta1.SecretServerProvider{
+ ServerURL: cfg.serverURL,
+ Username: &esv1beta1.SecretServerProviderRef{
+ Value: cfg.username,
+ },
+ Password: &esv1beta1.SecretServerProviderRef{
+ SecretRef: &esmeta.SecretKeySelector{
+ Name: secretName,
+ Key: secretKey,
+ },
+ },
+ },
+ },
+ },
+ }
+
+ err = f.CRClient.Create(ctx, &secretStoreSpec)
+ gomega.Expect(err).ToNot(gomega.HaveOccurred())
+}
diff --git a/e2e/suites/provider/cases/template/template.go b/e2e/suites/provider/cases/template/template.go
index 3c60ec459c1..a2819b10ee7 100644
--- a/e2e/suites/provider/cases/template/template.go
+++ b/e2e/suites/provider/cases/template/template.go
@@ -133,7 +133,7 @@ func genericPushSecretTemplate(f *framework.Framework) (string, func(*framework.
Type: v1.SecretTypeOpaque,
}
tc.PushSecret.Spec.Selector = esv1alpha1.PushSecretSelector{
- Secret: esv1alpha1.PushSecretSecret{
+ Secret: &esv1alpha1.PushSecretSecret{
Name: secretKey1,
},
}
diff --git a/e2e/suites/provider/cases/vault/vault.go b/e2e/suites/provider/cases/vault/vault.go
index 453167e7e5c..ce16025fe84 100644
--- a/e2e/suites/provider/cases/vault/vault.go
+++ b/e2e/suites/provider/cases/vault/vault.go
@@ -82,6 +82,8 @@ var _ = Describe("[vault]", Label("vault"), func() {
framework.Compose(withApprole, f, common.DataPropertyDockerconfigJSON, useApproleAuth),
framework.Compose(withApprole, f, common.JSONDataWithoutTargetName, useApproleAuth),
// use v1 provider
+ framework.Compose(withV1, f, common.FindByName, useV1Provider),
+ framework.Compose(withV1, f, common.FindByNameAndRewrite, useV1Provider),
framework.Compose(withV1, f, common.JSONDataFromSync, useV1Provider),
framework.Compose(withV1, f, common.JSONDataFromRewrite, useV1Provider),
framework.Compose(withV1, f, common.JSONDataWithProperty, useV1Provider),
@@ -292,7 +294,7 @@ func testInvalidMtlsStore(tc *framework.TestCase) {
Expect(string(ss.Status.Conditions[0].Type)).Should(Equal("Ready"))
Expect(string(ss.Status.Conditions[0].Status)).Should(Equal("False"))
Expect(ss.Status.Conditions[0].Reason).Should(Equal("ValidationFailed"))
- Expect(ss.Status.Conditions[0].Message).Should(Equal("unable to validate store"))
+ Expect(ss.Status.Conditions[0].Message).Should(ContainSubstring("unable to validate store"))
return true, nil
})
Expect(err).ToNot(HaveOccurred())
diff --git a/go.mod b/go.mod
index c646daff382..92d6a7232ed 100644
--- a/go.mod
+++ b/go.mod
@@ -1,221 +1,186 @@
module github.com/external-secrets/external-secrets
-go 1.22.1
+go 1.23.4
+
+replace github.com/Masterminds/sprig/v3 => github.com/external-secrets/sprig/v3 v3.3.0
require (
- cloud.google.com/go/iam v1.1.7
- cloud.google.com/go/secretmanager v1.12.0
+ cloud.google.com/go/iam v1.3.1
+ cloud.google.com/go/secretmanager v1.14.3
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible
github.com/Azure/go-autorest/autorest v0.11.29
- github.com/Azure/go-autorest/autorest/adal v0.9.23
- github.com/Azure/go-autorest/autorest/azure/auth v0.5.12
- github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2
- github.com/IBM/go-sdk-core/v5 v5.16.5
- github.com/IBM/secrets-manager-go-sdk/v2 v2.0.4
+ github.com/Azure/go-autorest/autorest/adal v0.9.24
+ github.com/Azure/go-autorest/autorest/azure/auth v0.5.13
+ github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2
+ github.com/IBM/go-sdk-core/v5 v5.18.5
+ github.com/IBM/secrets-manager-go-sdk/v2 v2.0.9
github.com/Masterminds/goutils v1.1.1 // indirect
- github.com/Masterminds/sprig/v3 v3.2.3
+ github.com/Masterminds/sprig/v3 v3.3.0
github.com/PaesslerAG/jsonpath v0.1.1
github.com/ahmetb/gen-crd-api-reference-docs v0.3.0
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.5
- github.com/aws/aws-sdk-go v1.51.21
- github.com/go-logr/logr v1.4.1
+ github.com/aws/aws-sdk-go v1.55.6
+ github.com/go-logr/logr v1.4.2
github.com/go-test/deep v1.0.4 // indirect
github.com/google/go-cmp v0.6.0
github.com/google/uuid v1.6.0
- github.com/googleapis/gax-go/v2 v2.12.3
- github.com/hashicorp/vault/api v1.12.2
- github.com/hashicorp/vault/api/auth/approle v0.6.0
- github.com/hashicorp/vault/api/auth/kubernetes v0.6.0
- github.com/hashicorp/vault/api/auth/ldap v0.6.0
- github.com/huandu/xstrings v1.4.0 // indirect
- github.com/onsi/ginkgo/v2 v2.17.1
- github.com/onsi/gomega v1.30.0
- github.com/oracle/oci-go-sdk/v65 v65.63.1
- github.com/prometheus/client_golang v1.19.0
+ github.com/googleapis/gax-go/v2 v2.14.1
+ github.com/hashicorp/vault/api v1.15.0
+ github.com/hashicorp/vault/api/auth/approle v0.8.0
+ github.com/hashicorp/vault/api/auth/kubernetes v0.8.0
+ github.com/hashicorp/vault/api/auth/ldap v0.8.0
+ github.com/huandu/xstrings v1.5.0 // indirect
+ github.com/onsi/ginkgo/v2 v2.22.2
+ github.com/onsi/gomega v1.36.2
+ github.com/oracle/oci-go-sdk/v65 v65.81.3
+ github.com/prometheus/client_golang v1.20.5
github.com/prometheus/client_model v0.6.1
- github.com/spf13/cobra v1.8.0
- github.com/stretchr/testify v1.9.0
- github.com/tidwall/gjson v1.17.1
- github.com/xanzy/go-gitlab v0.102.0
- github.com/yandex-cloud/go-genproto v0.0.0-20240401111333-b9ee0d3d9e6b
- github.com/yandex-cloud/go-sdk v0.0.0-20240318084659-dfa50323a0b4
- github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a
+ github.com/spf13/cobra v1.8.1
+ github.com/stretchr/testify v1.10.0
+ github.com/tidwall/gjson v1.18.0
+ github.com/yandex-cloud/go-genproto v0.0.0-20241220122821-aeb3b05efd1c
+ github.com/yandex-cloud/go-sdk v0.0.0-20241220131134-2393e243c134
+ github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78
go.uber.org/zap v1.27.0
- golang.org/x/crypto v0.22.0
- golang.org/x/oauth2 v0.19.0
- google.golang.org/api v0.172.0
- google.golang.org/genproto v0.0.0-20240412170617-26222e5d3d56
- google.golang.org/grpc v1.63.2
+ golang.org/x/crypto v0.32.0
+ golang.org/x/oauth2 v0.25.0
+ google.golang.org/api v0.218.0
+ google.golang.org/genproto v0.0.0-20250124145028-65684f501c47
+ google.golang.org/grpc v1.70.0
gopkg.in/yaml.v3 v3.0.1
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919
- k8s.io/api v0.29.3
- k8s.io/apiextensions-apiserver v0.29.3
- k8s.io/apimachinery v0.29.3
- k8s.io/client-go v0.29.3
- k8s.io/utils v0.0.0-20240310230437-4693a0247e57
- sigs.k8s.io/controller-runtime v0.17.3
- sigs.k8s.io/controller-tools v0.14.0
+ k8s.io/api v0.32.1
+ k8s.io/apiextensions-apiserver v0.32.1
+ k8s.io/apimachinery v0.32.1
+ k8s.io/client-go v0.32.1
+ k8s.io/utils v0.0.0-20241210054802-24370beab758
+ sigs.k8s.io/controller-runtime v0.20.1
+ sigs.k8s.io/controller-tools v0.17.1
)
require github.com/1Password/connect-sdk-go v1.5.3
require (
- github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1
- github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2
- github.com/DelineaXPM/dsv-sdk-go/v2 v2.1.2
+ dario.cat/mergo v1.0.1
+ github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0
+ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1
+ github.com/BeyondTrust/go-client-library-passwordsafe v0.14.1
+ github.com/DelineaXPM/dsv-sdk-go/v2 v2.2.0
+ github.com/DelineaXPM/tss-sdk-go/v2 v2.0.3
github.com/Onboardbase/go-cryptojs-aes-decrypt v0.0.0-20230430095000-27c0d3a9016d
github.com/akeylesslabs/akeyless-go/v3 v3.6.3
- github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.6
- github.com/alibabacloud-go/kms-20160120/v3 v3.1.3
- github.com/alibabacloud-go/openapi-util v0.1.0
+ github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10
+ github.com/alibabacloud-go/kms-20160120/v3 v3.2.3
+ github.com/alibabacloud-go/openapi-util v0.1.1
github.com/alibabacloud-go/tea v1.2.2
- github.com/alibabacloud-go/tea-utils/v2 v2.0.5
- github.com/aliyun/credentials-go v1.3.2
- github.com/avast/retry-go/v4 v4.5.1
- github.com/cyberark/conjur-api-go v0.11.1
+ github.com/alibabacloud-go/tea-utils/v2 v2.0.7
+ github.com/aliyun/credentials-go v1.4.3
+ github.com/avast/retry-go/v4 v4.6.0
+ github.com/cenkalti/backoff/v4 v4.3.0
+ github.com/cyberark/conjur-api-go v0.12.10
github.com/fortanix/sdkms-client-go v0.4.0
github.com/go-openapi/strfmt v0.23.0
github.com/golang-jwt/jwt/v5 v5.2.1
github.com/hashicorp/golang-lru v1.0.2
- github.com/hashicorp/vault/api/auth/aws v0.6.0
- github.com/hashicorp/vault/api/auth/userpass v0.6.0
- github.com/keeper-security/secrets-manager-go/core v1.6.2
- github.com/lestrrat-go/jwx/v2 v2.0.21
- github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1
- github.com/passbolt/go-passbolt v0.7.0
- github.com/pulumi/esc v0.8.3
- github.com/scaleway/scaleway-sdk-go v1.0.0-beta.26
- github.com/sethvargo/go-password v0.2.0
+ github.com/hashicorp/vault/api/auth/aws v0.8.0
+ github.com/hashicorp/vault/api/auth/userpass v0.8.0
+ github.com/keeper-security/secrets-manager-go/core v1.6.4
+ github.com/lestrrat-go/jwx/v2 v2.1.3
+ github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2
+ github.com/passbolt/go-passbolt v0.7.1
+ github.com/previder/vault-cli v0.1.2
+ github.com/pulumi/esc-sdk/sdk v0.11.0
+ github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30
+ github.com/sethvargo/go-password v0.3.1
github.com/spf13/pflag v1.0.5
github.com/tidwall/sjson v1.2.5
+ gitlab.com/gitlab-org/api/client-go v0.120.0
+ k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7
sigs.k8s.io/yaml v1.4.0
- software.sslmate.com/src/go-pkcs12 v0.4.0
+ software.sslmate.com/src/go-pkcs12 v0.5.0
)
require (
- cloud.google.com/go/compute/metadata v0.2.3 // indirect
- dario.cat/mergo v1.0.0 // indirect
- github.com/Microsoft/go-winio v0.6.1 // indirect
- github.com/ProtonMail/go-crypto v1.0.0 // indirect
+ al.essio.dev/pkg/shellescape v1.5.1 // indirect
+ cloud.google.com/go/auth v0.14.0 // indirect
+ cloud.google.com/go/auth/oauth2adapt v0.2.7 // indirect
+ cloud.google.com/go/compute/metadata v0.6.0 // indirect
+ github.com/ProtonMail/go-crypto v1.1.5 // indirect
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect
- github.com/ProtonMail/gopenpgp/v2 v2.7.4 // indirect
- github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect
- github.com/agext/levenshtein v1.2.3 // indirect
- github.com/alessio/shellescape v1.4.2 // indirect
- github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.4 // indirect
- github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 // indirect
+ github.com/ProtonMail/gopenpgp/v2 v2.8.2 // indirect
+ github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.7 // indirect
+ github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 // indirect
github.com/alibabacloud-go/darabonba-array v0.1.0 // indirect
github.com/alibabacloud-go/darabonba-encode-util v0.0.2 // indirect
github.com/alibabacloud-go/darabonba-map v0.0.2 // indirect
github.com/alibabacloud-go/darabonba-signature-util v0.0.7 // indirect
github.com/alibabacloud-go/darabonba-string v1.0.2 // indirect
- github.com/alibabacloud-go/debug v1.0.0 // indirect
+ github.com/alibabacloud-go/debug v1.0.1 // indirect
github.com/alibabacloud-go/endpoint-util v1.1.1 // indirect
- github.com/alibabacloud-go/tea-utils v1.4.5 // indirect
github.com/alibabacloud-go/tea-xml v1.1.3 // indirect
- github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
- github.com/atotto/clipboard v0.1.4 // indirect
- github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
- github.com/blang/semver v3.5.1+incompatible // indirect
- github.com/charmbracelet/bubbles v0.18.0 // indirect
- github.com/charmbracelet/bubbletea v0.25.0 // indirect
- github.com/charmbracelet/lipgloss v0.10.0 // indirect
- github.com/cheggaaa/pb v1.0.29 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
- github.com/cloudflare/circl v1.3.7 // indirect
- github.com/containerd/console v1.0.4 // indirect
- github.com/cyphar/filepath-securejoin v0.2.4 // indirect
- github.com/danieljoos/wincred v1.2.1 // indirect
- github.com/djherbis/times v1.6.0 // indirect
- github.com/emirpasic/gods v1.18.1 // indirect
+ github.com/cloudflare/circl v1.5.0 // indirect
+ github.com/danieljoos/wincred v1.2.2 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
- github.com/gabriel-vasile/mimetype v1.4.3 // indirect
- github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
- github.com/go-git/go-billy/v5 v5.5.0 // indirect
- github.com/go-git/go-git/v5 v5.12.0 // indirect
- github.com/go-jose/go-jose/v3 v3.0.3 // indirect
+ github.com/fxamacker/cbor/v2 v2.7.0 // indirect
+ github.com/gabriel-vasile/mimetype v1.4.8 // indirect
+ github.com/go-jose/go-jose/v4 v4.0.4 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
- github.com/go-playground/validator/v10 v10.19.0 // indirect
+ github.com/go-playground/validator/v10 v10.24.0 // indirect
+ github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
- github.com/gofrs/flock v0.8.1 // indirect
- github.com/golang/glog v1.2.1 // indirect
- github.com/google/gnostic-models v0.6.8 // indirect
- github.com/google/s2a-go v0.1.7 // indirect
+ github.com/gofrs/flock v0.12.1 // indirect
+ github.com/google/btree v1.1.3 // indirect
+ github.com/google/gnostic-models v0.6.9 // indirect
+ github.com/google/s2a-go v0.1.9 // indirect
github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
- github.com/hashicorp/hcl/v2 v2.20.1 // indirect
- github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
- github.com/kevinburke/ssh_config v1.2.0 // indirect
- github.com/lestrrat-go/httprc v1.0.5 // indirect
- github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
- github.com/mattn/go-localereader v0.0.1 // indirect
- github.com/mattn/go-runewidth v0.0.15 // indirect
- github.com/mitchellh/go-ps v1.0.0 // indirect
- github.com/mitchellh/go-wordwrap v1.0.1 // indirect
- github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
- github.com/muesli/cancelreader v0.2.2 // indirect
- github.com/muesli/reflow v0.3.0 // indirect
- github.com/muesli/termenv v0.15.2 // indirect
- github.com/opentracing/basictracer-go v1.1.0 // indirect
- github.com/pgavlin/fx v0.1.6 // indirect
- github.com/pjbgf/sha1cd v0.3.0 // indirect
- github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 // indirect
- github.com/pulumi/pulumi/sdk/v3 v3.112.0 // indirect
- github.com/rivo/uniseg v0.4.7 // indirect
- github.com/rogpeppe/go-internal v1.12.0 // indirect
- github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 // indirect
- github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect
+ github.com/klauspost/compress v1.17.11 // indirect
+ github.com/lestrrat-go/httprc v1.0.6 // indirect
github.com/segmentio/asm v1.2.0 // indirect
- github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
- github.com/skeema/knownhosts v1.2.2 // indirect
- github.com/texttheater/golang-levenshtein v1.0.1 // indirect
github.com/tjfoc/gmsm v1.4.1 // indirect
- github.com/tweekmonster/luser v0.0.0-20161003172636-3fa38070dbd7 // indirect
- github.com/xanzy/ssh-agent v0.3.3 // indirect
- github.com/zalando/go-keyring v0.2.4 // indirect
- github.com/zclconf/go-cty v1.14.4 // indirect
- go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.50.0 // indirect
- go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 // indirect
- go.opentelemetry.io/otel v1.25.0 // indirect
- go.opentelemetry.io/otel/metric v1.25.0 // indirect
- go.opentelemetry.io/otel/trace v1.25.0 // indirect
- golang.org/x/sync v0.7.0 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20240412170617-26222e5d3d56 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20240412170617-26222e5d3d56 // indirect
- gopkg.in/warnings.v0 v0.1.2 // indirect
- k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3 // indirect
- lukechampine.com/frand v1.4.2 // indirect
- sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
+ github.com/x448/float16 v0.8.4 // indirect
+ github.com/zalando/go-keyring v0.2.6 // indirect
+ go.opentelemetry.io/auto/sdk v1.1.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
+ go.opentelemetry.io/otel v1.34.0 // indirect
+ go.opentelemetry.io/otel/metric v1.34.0 // indirect
+ go.opentelemetry.io/otel/trace v1.34.0 // indirect
+ golang.org/x/sync v0.10.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 // indirect
+ gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
+ gopkg.in/ghodss/yaml.v1 v1.0.0 // indirect
+ sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
)
require (
- cloud.google.com/go/compute v1.25.1 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect
- github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
+ github.com/Azure/go-autorest/autorest/date v0.3.0
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
github.com/Azure/go-autorest/autorest/validation v0.3.1 // indirect
github.com/Azure/go-autorest/logger v0.2.1 // indirect
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
- github.com/Masterminds/semver/v3 v3.2.1 // indirect
- github.com/PaesslerAG/gval v1.2.2 // indirect
+ github.com/Masterminds/semver/v3 v3.3.1 // indirect
+ github.com/PaesslerAG/gval v1.2.4 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/beorn7/perks v1.0.1 // indirect
- github.com/cenkalti/backoff/v3 v3.2.2 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
- github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
- github.com/emicklei/go-restful/v3 v3.12.0 // indirect
+ github.com/emicklei/go-restful/v3 v3.12.1 // indirect
github.com/evanphx/json-patch v5.6.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
- github.com/fatih/color v1.16.0 // indirect
- github.com/fsnotify/fsnotify v1.7.0 // indirect
+ github.com/fatih/color v1.18.0 // indirect
+ github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/ghodss/yaml v1.0.0 // indirect
- github.com/go-chef/chef v0.29.0
+ github.com/go-chef/chef v0.30.1
github.com/go-logr/zapr v1.3.0 // indirect
github.com/go-openapi/errors v0.22.0 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
@@ -223,28 +188,26 @@ require (
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
- github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
- github.com/gobuffalo/flect v1.0.2 // indirect
- github.com/goccy/go-json v0.10.2 // indirect
+ github.com/gobuffalo/flect v1.0.3 // indirect
+ github.com/goccy/go-json v0.10.4 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
- github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
- github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
+ github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
+ github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
- github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd // indirect
- github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
+ github.com/google/pprof v0.0.0-20250125003558-7fdb3d7e6fa0 // indirect
+ github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
- github.com/hashicorp/go-retryablehttp v0.7.5
+ github.com/hashicorp/go-retryablehttp v0.7.7
github.com/hashicorp/go-rootcerts v1.0.2 // indirect
- github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 // indirect
+ github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9 // indirect
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect
- github.com/hashicorp/go-sockaddr v1.0.6 // indirect
- github.com/hashicorp/hcl v1.0.1-vault-5 // indirect
- github.com/imdario/mergo v0.3.16 // indirect
+ github.com/hashicorp/go-sockaddr v1.0.7 // indirect
+ github.com/hashicorp/hcl v1.0.1-vault-7 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
@@ -255,8 +218,8 @@ require (
github.com/lestrrat-go/httpcc v1.0.1 // indirect
github.com/lestrrat-go/iter v1.0.2 // indirect
github.com/lestrrat-go/option v1.0.1 // indirect
- github.com/mailru/easyjson v0.7.7 // indirect
- github.com/mattn/go-colorable v0.1.13 // indirect
+ github.com/mailru/easyjson v0.9.0 // indirect
+ github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
@@ -269,38 +232,35 @@ require (
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect
- github.com/pmezard/go-difflib v1.0.0 // indirect
- github.com/prometheus/common v0.52.3 // indirect
- github.com/prometheus/procfs v0.13.0 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
+ github.com/prometheus/common v0.62.0 // indirect
+ github.com/prometheus/procfs v0.15.1 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/shopspring/decimal v1.4.0 // indirect
- github.com/sony/gobreaker v0.5.0 // indirect
- github.com/spf13/cast v1.6.0 // indirect
+ github.com/sony/gobreaker v1.0.0 // indirect
+ github.com/spf13/cast v1.7.1 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect
github.com/uber/jaeger-lib v2.4.1+incompatible // indirect
- go.mongodb.org/mongo-driver v1.15.0 // indirect
- go.opencensus.io v0.24.0 // indirect
+ go.mongodb.org/mongo-driver v1.17.2 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
- golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8
- golang.org/x/mod v0.17.0 // indirect
- golang.org/x/net v0.24.0 // indirect
- golang.org/x/sys v0.19.0 // indirect
- golang.org/x/term v0.19.0 // indirect
- golang.org/x/text v0.14.0 // indirect
- golang.org/x/time v0.5.0 // indirect
- golang.org/x/tools v0.20.0 // indirect
+ golang.org/x/mod v0.22.0 // indirect
+ golang.org/x/net v0.34.0 // indirect
+ golang.org/x/sys v0.29.0 // indirect
+ golang.org/x/term v0.28.0 // indirect
+ golang.org/x/text v0.21.0 // indirect
+ golang.org/x/time v0.9.0
+ golang.org/x/tools v0.29.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
- google.golang.org/protobuf v1.33.0 // indirect
+ google.golang.org/protobuf v1.36.4 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
- gopkg.in/yaml.v2 v2.4.0 // indirect
- k8s.io/component-base v0.29.3 // indirect
- k8s.io/gengo v0.0.0-20240404160639-a0386bf69313 // indirect
+ gopkg.in/yaml.v2 v2.4.0
+ k8s.io/gengo v0.0.0-20250106234829-0359904fc2a6 // indirect
k8s.io/klog v1.0.0 // indirect
- k8s.io/klog/v2 v2.120.1 // indirect
- sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
+ k8s.io/klog/v2 v2.130.1 // indirect
+ sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
)
diff --git a/go.sum b/go.sum
index e255c153bca..d2b0e3dd99b 100644
--- a/go.sum
+++ b/go.sum
@@ -1,3 +1,5 @@
+al.essio.dev/pkg/shellescape v1.5.1 h1:86HrALUujYS/h+GtqoB26SBEdkWfmMI6FubjXlsXyho=
+al.essio.dev/pkg/shellescape v1.5.1/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@@ -18,36 +20,39 @@ cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmW
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
-cloud.google.com/go v0.112.2 h1:ZaGT6LiG7dBzi6zNOvVZwacaXlmf3lRqnC4DQzqyRQw=
-cloud.google.com/go v0.112.2/go.mod h1:iEqjp//KquGIJV/m+Pk3xecgKNhV+ry+vVTsy4TbDms=
+cloud.google.com/go v0.118.0 h1:tvZe1mgqRxpiVa3XlIGMiPcEUbP1gNXELgD4y/IXmeQ=
+cloud.google.com/go v0.118.0/go.mod h1:zIt2pkedt/mo+DQjcT4/L3NDxzHPR29j5HcclNH+9PM=
+cloud.google.com/go/auth v0.14.0 h1:A5C4dKV/Spdvxcl0ggWwWEzzP7AZMJSEIgrkngwhGYM=
+cloud.google.com/go/auth v0.14.0/go.mod h1:CYsoRL1PdiDuqeQpZE0bP2pnPrGqFcOkI0nldEQis+A=
+cloud.google.com/go/auth/oauth2adapt v0.2.7 h1:/Lc7xODdqcEw8IrZ9SvwnlLX6j9FHQM74z6cBk9Rw6M=
+cloud.google.com/go/auth/oauth2adapt v0.2.7/go.mod h1:NTbTTzfvPl1Y3V1nPpOgl2w6d/FjO7NNUQaWSox6ZMc=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
-cloud.google.com/go/compute v1.25.1 h1:ZRpHJedLtTpKgr3RV1Fx23NuaAEN1Zfx9hw1u4aJdjU=
-cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//uf3IB011ls=
cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
-cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
-cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
+cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
+cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
-cloud.google.com/go/iam v1.1.7 h1:z4VHOhwKLF/+UYXAJDFwGtNF0b6gjsW1Pk9Ml0U/IoM=
-cloud.google.com/go/iam v1.1.7/go.mod h1:J4PMPg8TtyurAUvSmPj8FF3EDgY1SPRZxcUGrn7WXGA=
+cloud.google.com/go/iam v1.3.1 h1:KFf8SaT71yYq+sQtRISn90Gyhyf4X8RGgeAVC8XGf3E=
+cloud.google.com/go/iam v1.3.1/go.mod h1:3wMtuyT4NcbnYNPLMBzYRFiEfjKfJlLVLrisE7bwm34=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
-cloud.google.com/go/secretmanager v1.12.0 h1:e5pIo/QEgiFiHPVJPxM5jbtUr4O/u5h2zLHYtkFQr24=
-cloud.google.com/go/secretmanager v1.12.0/go.mod h1:Y1Gne3Ag+fZ2TDTiJc8ZJCMFbi7k1rYT4Rw30GXfvlk=
+cloud.google.com/go/secretmanager v1.14.3 h1:XVGHbcXEsbrgi4XHzgK5np81l1eO7O72WOXHhXUemrM=
+cloud.google.com/go/secretmanager v1.14.3/go.mod h1:Pwzcfn69Ni9Lrk1/XBzo1H9+MCJwJ6CDCoeoQUsMN+c=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
-dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
+dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
+dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
github.com/1Password/connect-sdk-go v1.5.3 h1:KyjJ+kCKj6BwB2Y8tPM1Ixg5uIS6HsB0uWA8U38p/Uk=
github.com/1Password/connect-sdk-go v1.5.3/go.mod h1:5rSymY4oIYtS4G3t0oMkGAXBeoYiukV3vkqlnEjIDJs=
@@ -55,27 +60,28 @@ github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.8.0/go.mod h1:3Ug6Qzto9anB6mGlEdgYMDF5zHQ+wwhEaYR4s17PHMw=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ=
+github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.4.0/go.mod h1:1fXstnBMas5kzG+S3q8UoJcmyU6nUeunJcMDHcRYHhs=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2 h1:FDif4R1+UUR+00q6wquyX90K7A8dN+R5E8GEadoP7sU=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2/go.mod h1:aiYBYui4BJ/BJCAIKs92XiPyQfTaBWqvHujDwKb6CBU=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1 h1:1mvYtZfWQAnwNah/C+Z+Jb9rQH95LPE2vlmMuWAHJk8=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.1/go.mod h1:75I/mXtme1JyWFtz8GocPHVFyH421IBoZErnO16dd0k=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1 h1:Bk5uOhSAenHyR5P61D/NzeQCv+4fEVV8mOkJ82NqpWw=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.1/go.mod h1:QZ4pw3or1WPmRBxf0cHd1tknzrT54WPBOQoGutCPvSU=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.1/go.mod h1:s4kgfzA0covAXNicZHDMN58jExvcng2mC/DepXiF1EI=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2 h1:LqbJ/WzJUwBf8UiaSzgX7aMclParm9/5Vgp+TY51uBQ=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0/go.mod h1:iZDifYGJTIgIIkYRNWPENUnqx6bJ2xnSDFI2tjwZNuY=
github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=
github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
-github.com/Azure/go-autorest/autorest v0.11.24/go.mod h1:G6kyRlFnTuSbEYkQGawPfsCswgme4iYf6rfSKUDzbCc=
+github.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=
github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw=
github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs=
github.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=
github.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk=
-github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8=
-github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c=
-github.com/Azure/go-autorest/autorest/azure/auth v0.5.12 h1:wkAZRgT/pn8HhFyzfe9UnqOjJYqlembgCTi72Bm/xKk=
-github.com/Azure/go-autorest/autorest/azure/auth v0.5.12/go.mod h1:84w/uV8E37feW2NCJ08uT9VBfjfUHpgLVnG2InYD6cg=
-github.com/Azure/go-autorest/autorest/azure/cli v0.4.5/go.mod h1:ADQAXrkgm7acgWVUNamOgh8YNrv4p27l3Wc55oVfpzg=
+github.com/Azure/go-autorest/autorest/adal v0.9.24 h1:BHZfgGsGwdkHDyZdtQRQk1WeUdW0m2WPAwuHZwUi5i4=
+github.com/Azure/go-autorest/autorest/adal v0.9.24/go.mod h1:7T1+g0PYFmACYW5LlG2fcoPiPlFHjClyRGL7dRlP5c8=
+github.com/Azure/go-autorest/autorest/azure/auth v0.5.13 h1:Ov8avRZi2vmrE2JcXw+tu5K/yB41r7xK9GZDiBF7NdM=
+github.com/Azure/go-autorest/autorest/azure/auth v0.5.13/go.mod h1:5BAVfWLWXihP47vYrPuBKKf4cS0bXI+KM9Qx6ETDJYo=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 h1:w77/uPk80ZET2F+AfQExZyEWtn+0Rk/uw17m9fv5Ajc=
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0=
github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw=
@@ -91,83 +97,81 @@ github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+Z
github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo=
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
+github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
+github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE=
github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
-github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ=
+github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
+github.com/BeyondTrust/go-client-library-passwordsafe v0.14.1 h1:jUBIBDX93tX0VsQrKxvG5e5eOrZ7LQy99cshFmDAdWM=
+github.com/BeyondTrust/go-client-library-passwordsafe v0.14.1/go.mod h1:72FMrpiz1fUSiIIIAXiCzQ55Y83spsu2jl5n/Stzfks=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
-github.com/DelineaXPM/dsv-sdk-go/v2 v2.1.2 h1:cmX2QC9s5kPqmghWLLZP8YRFO1ZD/C59BpNH2ujP99w=
-github.com/DelineaXPM/dsv-sdk-go/v2 v2.1.2/go.mod h1:tNlpIXJlIwQlRbobXDPme4qv/Rc8+a1GbuUhE3m4JhQ=
+github.com/DelineaXPM/dsv-sdk-go/v2 v2.2.0 h1:62E66sDf+Hs1TChuu3R7d+0U5s7yV84QIOvvnfxtUJM=
+github.com/DelineaXPM/dsv-sdk-go/v2 v2.2.0/go.mod h1:58Pflli0BtqeF0VgluDSSVE5QlIfLOJvat0JSvo/d70=
+github.com/DelineaXPM/tss-sdk-go/v2 v2.0.3 h1:Yk8VZUIer8deRzi1Zx2Di2wEpw138IP09O5eKUYmDRs=
+github.com/DelineaXPM/tss-sdk-go/v2 v2.0.3/go.mod h1:xz6FXP2Do88Vc5Hx7OamZgZC1W45yfmLy4+iDKxlGXo=
github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
-github.com/IBM/go-sdk-core/v5 v5.16.5 h1:5ZltNcryRI8kVcuvNJGR2EXKqb7HtM4atA0Nm5QwAFE=
-github.com/IBM/go-sdk-core/v5 v5.16.5/go.mod h1:GatGZpxlo1KaxiRN6E10/rNgWtUtx1hN/GoHSCaSPKA=
-github.com/IBM/secrets-manager-go-sdk/v2 v2.0.4 h1:xa9e+POVqaXxXHXkSMCOVAbKdUNEu86jQmo5hcpd+L4=
-github.com/IBM/secrets-manager-go-sdk/v2 v2.0.4/go.mod h1:5gq8D8uWOIbqOm1uztay6lpOysgJaxxEsaVZLWGWb40=
+github.com/IBM/go-sdk-core/v5 v5.18.5 h1:g0JRl3sYXJczB/yuDlrN6x22LJ6jIxhp0Sa4ARNW60c=
+github.com/IBM/go-sdk-core/v5 v5.18.5/go.mod h1:KonTFRR+8ZSgw5cxBSYo6E4WZoY1+7n1kfHM82VcjFU=
+github.com/IBM/secrets-manager-go-sdk/v2 v2.0.9 h1:SQiXt1JKHbwtYya8T/4YjmX/OQt8LI9HtUV/+Fw9iJo=
+github.com/IBM/secrets-manager-go-sdk/v2 v2.0.9/go.mod h1:KyKK+yU5fcuCIyz+UXuaHq1ckVWHiW3ji4nj0eonTTc=
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
-github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
-github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
-github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA=
-github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM=
-github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
-github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
-github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
+github.com/Masterminds/semver/v3 v3.3.1 h1:QtNSWtVZ3nBfk8mAOu/B6v7FMJ+NHTIgUPi7rj+4nv4=
+github.com/Masterminds/semver/v3 v3.3.1/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Onboardbase/go-cryptojs-aes-decrypt v0.0.0-20230430095000-27c0d3a9016d h1:V7xPdg5XgCcUJgL57zfZSNOIvrDPWA4SpWuRJ0UVwKs=
github.com/Onboardbase/go-cryptojs-aes-decrypt v0.0.0-20230430095000-27c0d3a9016d/go.mod h1:WI6HYqD62DSW+C0gMS0zHe/vXhZVCUg2ecVosnglPNc=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/PaesslerAG/gval v1.0.0/go.mod h1:y/nm5yEyTeX6av0OfKJNp9rBNj2XrGhAf5+v24IBN1I=
-github.com/PaesslerAG/gval v1.2.2 h1:Y7iBzhgE09IGTt5QgGQ2IdaYYYOU134YGHBThD+wm9E=
-github.com/PaesslerAG/gval v1.2.2/go.mod h1:XRFLwvmkTEdYziLdaCeCa5ImcGVrfQbeNUbVR+C6xac=
+github.com/PaesslerAG/gval v1.2.4 h1:rhX7MpjJlcxYwL2eTTYIOBUyEKZ+A96T9vQySWkVUiU=
+github.com/PaesslerAG/gval v1.2.4/go.mod h1:XRFLwvmkTEdYziLdaCeCa5ImcGVrfQbeNUbVR+C6xac=
github.com/PaesslerAG/jsonpath v0.1.0/go.mod h1:4BzmtoM/PI8fPO4aQGIusjGxGir2BzcV0grWtFzq1Y8=
github.com/PaesslerAG/jsonpath v0.1.1 h1:c1/AToHQMVsduPAa4Vh6xp2U0evy4t8SWp8imEsylIk=
github.com/PaesslerAG/jsonpath v0.1.1/go.mod h1:lVboNxFGal/VwW6d9JzIy56bUsYAP6tH/x80vjnCseY=
-github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
-github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
-github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
+github.com/ProtonMail/go-crypto v1.1.5 h1:eoAQfK2dwL+tFSFpr7TbOaPNUbPiJj4fLYwwGE1FQO4=
+github.com/ProtonMail/go-crypto v1.1.5/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k=
github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw=
-github.com/ProtonMail/gopenpgp/v2 v2.7.4 h1:Vz/8+HViFFnf2A6XX8JOvZMrA6F5puwNvvF21O1mRlo=
-github.com/ProtonMail/gopenpgp/v2 v2.7.4/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g=
-github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=
-github.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=
-github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo=
-github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558=
+github.com/ProtonMail/gopenpgp/v2 v2.8.2 h1:fe/XagfxkHRCr+cLFMcoF7XwaASRGSmK/fmcmK8yo6o=
+github.com/ProtonMail/gopenpgp/v2 v2.8.2/go.mod h1:pPWZyRQWpQ7g8NWsdZmUynNZ1R09k4MdbSHvm+KooqM=
github.com/ahmetb/gen-crd-api-reference-docs v0.3.0 h1:+XfOU14S4bGuwyvCijJwhhBIjYN+YXS18jrCY2EzJaY=
github.com/ahmetb/gen-crd-api-reference-docs v0.3.0/go.mod h1:TdjdkYhlOifCQWPs1UdTma97kQQMozf5h26hTuG70u8=
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.5 h1:ly0WKARATneFzwBlTZ2lUyjtLqoOEYqt1vOlf89za/4=
github.com/akeylesslabs/akeyless-go-cloud-id v0.3.5/go.mod h1:W6DMNwPyIE3jpXDaJOvCKUT/kHPZrpl/BGiIVUILbMk=
github.com/akeylesslabs/akeyless-go/v3 v3.6.3 h1:fMF8SMDiBL9CufVjLUyF1Z+Z04t5CC3KGOROSjaJ/eA=
github.com/akeylesslabs/akeyless-go/v3 v3.6.3/go.mod h1:xcSXQWFRzKupIPCFRd9/mFYW0lHnDnWVvMD/pQ0x7sU=
-github.com/alessio/shellescape v1.4.2 h1:MHPfaU+ddJ0/bYWpgIeUnQUqKrlJ1S7BfEYPM4uEoM0=
-github.com/alessio/shellescape v1.4.2/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
-github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.4 h1:KFhE49hRMIl+i3NUB3NNTlVi+TPnQxGoeJ8S4HUEC+Q=
-github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.4/go.mod h1:ppRyTMegTC+9W6kVXwolCfP9beqnVUq9pC7h3EE/Igs=
-github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo=
+github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.6/go.mod h1:4EUIoxs/do24zMOGGqYVWgw0s9NtiylnJglOeEB5UJo=
+github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.7 h1:SIKnUmazSs7nkKZ+Il+eXnEi1l3oT/eILOcO/fOO3gg=
+github.com/alibabacloud-go/alibabacloud-gateway-pop v0.0.7/go.mod h1:VbdwKafy23IfBtTQ0btLD4E7AdqOqqpY1JCWkDftNTs=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4/go.mod h1:sCavSAvdzOjul4cEqeVtvlSaSScfNsTQ+46HwlTL1hc=
+github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5 h1:zE8vH9C7JiZLNJJQ5OwjU9mSi4T9ef9u3BURT6LCLC8=
+github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.5/go.mod h1:tWnyE9AjF8J8qqLk645oUmVUnFybApTQWklQmi5tY6g=
github.com/alibabacloud-go/darabonba-array v0.1.0 h1:vR8s7b1fWAQIjEjWnuF0JiKsCvclSRTfDzZHTYqfufY=
github.com/alibabacloud-go/darabonba-array v0.1.0/go.mod h1:BLKxr0brnggqOJPqT09DFJ8g3fsDshapUD3C3aOEFaI=
github.com/alibabacloud-go/darabonba-encode-util v0.0.2 h1:1uJGrbsGEVqWcWxrS9MyC2NG0Ax+GpOM5gtupki31XE=
github.com/alibabacloud-go/darabonba-encode-util v0.0.2/go.mod h1:JiW9higWHYXm7F4PKuMgEUETNZasrDM6vqVr/Can7H8=
github.com/alibabacloud-go/darabonba-map v0.0.2 h1:qvPnGB4+dJbJIxOOfawxzF3hzMnIpjmafa0qOTp6udc=
github.com/alibabacloud-go/darabonba-map v0.0.2/go.mod h1:28AJaX8FOE/ym8OUFWga+MtEzBunJwQGceGQlvaPGPc=
-github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.6 h1:y1K+zKhpWcxso8zqI03CcYuwgyZPFwQdwAQOXAeuOVM=
-github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.6/go.mod h1:CzQnh+94WDnJOnKZH5YRyouL+OOcdBnXY5VWAf0McgI=
+github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.9/go.mod h1:bb+Io8Sn2RuM3/Rpme6ll86jMyFSrD1bxeV/+v61KeU=
+github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10 h1:GEYkMApgpKEVDn6z12DcH1EGYpDYRB8JxsazM4Rywak=
+github.com/alibabacloud-go/darabonba-openapi/v2 v2.0.10/go.mod h1:26a14FGhZVELuz2cc2AolvW4RHmIO3/HRwsdHhaIPDE=
github.com/alibabacloud-go/darabonba-signature-util v0.0.7 h1:UzCnKvsjPFzApvODDNEYqBHMFt1w98wC7FOo0InLyxg=
github.com/alibabacloud-go/darabonba-signature-util v0.0.7/go.mod h1:oUzCYV2fcCH797xKdL6BDH8ADIHlzrtKVjeRtunBNTQ=
github.com/alibabacloud-go/darabonba-string v1.0.2 h1:E714wms5ibdzCqGeYJ9JCFywE5nDyvIXIIQbZVFkkqo=
github.com/alibabacloud-go/darabonba-string v1.0.2/go.mod h1:93cTfV3vuPhhEwGGpKKqhVW4jLe7tDpo3LUM0i0g6mA=
github.com/alibabacloud-go/debug v0.0.0-20190504072949-9472017b5c68/go.mod h1:6pb/Qy8c+lqua8cFpEy7g39NRRqOWc3rOwAy8m5Y2BY=
-github.com/alibabacloud-go/debug v1.0.0 h1:3eIEQWfay1fB24PQIEzXAswlVJtdQok8f3EVN5VrBnA=
github.com/alibabacloud-go/debug v1.0.0/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
+github.com/alibabacloud-go/debug v1.0.1 h1:MsW9SmUtbb1Fnt3ieC6NNZi6aEwrXfDksD4QA6GSbPg=
+github.com/alibabacloud-go/debug v1.0.1/go.mod h1:8gfgZCCAC3+SCzjWtY053FrOcd4/qlH6IHTI4QyICOc=
github.com/alibabacloud-go/endpoint-util v1.1.0/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=
github.com/alibabacloud-go/endpoint-util v1.1.1 h1:ZkBv2/jnghxtU0p+upSU0GGzW1VL9GQdZO3mcSUTUy8=
github.com/alibabacloud-go/endpoint-util v1.1.1/go.mod h1:O5FuCALmCKs2Ff7JFJMudHs0I5EBgecXXxZRyswlEjE=
-github.com/alibabacloud-go/kms-20160120/v3 v3.1.3 h1:DJfmRHjsyRmRFB7q2N9g2+wym4qxZixnsxHfiI4UhtQ=
-github.com/alibabacloud-go/kms-20160120/v3 v3.1.3/go.mod h1:3gJSRZ2CKrDdUGXIfv+pTWLE2mav3b2ASlqmN4jPU7A=
-github.com/alibabacloud-go/openapi-util v0.1.0 h1:0z75cIULkDrdEhkLWgi9tnLe+KhAFE/r5Pb3312/eAY=
+github.com/alibabacloud-go/kms-20160120/v3 v3.2.3 h1:vamGcYQFwXVqR6RWcrVTTqlIXZVsYjaA7pZbx+Xw6zw=
+github.com/alibabacloud-go/kms-20160120/v3 v3.2.3/go.mod h1:3rIyughsFDLie1ut9gQJXkWkMg/NfXBCk+OtXnPu3lw=
github.com/alibabacloud-go/openapi-util v0.1.0/go.mod h1:sQuElr4ywwFRlCCberQwKRFhRzIyG4QTP/P4y1CJ6Ws=
+github.com/alibabacloud-go/openapi-util v0.1.1 h1:ujGErJjG8ncRW6XtBBMphzHTvCxn4DjrVw4m04HsS28=
+github.com/alibabacloud-go/openapi-util v0.1.1/go.mod h1:/UehBSE2cf1gYT43GV4E+RxTdLRzURImCYY0aRmlXpw=
github.com/alibabacloud-go/tea v1.1.0/go.mod h1:IkGyUSX4Ba1V+k4pCtJUc6jDpZLFph9QMy2VUPTwukg=
github.com/alibabacloud-go/tea v1.1.7/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
github.com/alibabacloud-go/tea v1.1.8/go.mod h1:/tmnEaQMyb4Ky1/5D+SE1BAsa5zj/KeGOFfwYm3N/p4=
@@ -178,63 +182,35 @@ github.com/alibabacloud-go/tea v1.2.1/go.mod h1:qbzof29bM/IFhLMtJPrgTGK3eauV5J2w
github.com/alibabacloud-go/tea v1.2.2 h1:aTsR6Rl3ANWPfqeQugPglfurloyBJY85eFy7Gc1+8oU=
github.com/alibabacloud-go/tea v1.2.2/go.mod h1:CF3vOzEMAG+bR4WOql8gc2G9H3EkH3ZLAQdpmpXMgwk=
github.com/alibabacloud-go/tea-utils v1.3.1/go.mod h1:EI/o33aBfj3hETm4RLiAxF/ThQdSngxrpF8rKUDJjPE=
-github.com/alibabacloud-go/tea-utils v1.4.5 h1:h0/6Xd2f3bPE4XHTvkpjwxowIwRCJAJOqY6Eq8f3zfA=
-github.com/alibabacloud-go/tea-utils v1.4.5/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw=
-github.com/alibabacloud-go/tea-utils/v2 v2.0.5 h1:EUakYEUAwr6L3wLT0vejIw2rc0IA1RSXDwLnIb3f2vU=
github.com/alibabacloud-go/tea-utils/v2 v2.0.5/go.mod h1:dL6vbUT35E4F4bFTHL845eUloqaerYBYPsdWR2/jhe4=
+github.com/alibabacloud-go/tea-utils/v2 v2.0.6/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
+github.com/alibabacloud-go/tea-utils/v2 v2.0.7 h1:WDx5qW3Xa5ZgJ1c8NfqJkF6w+AU5wB8835UdhPr6Ax0=
+github.com/alibabacloud-go/tea-utils/v2 v2.0.7/go.mod h1:qxn986l+q33J5VkialKMqT/TTs3E+U9MJpd001iWQ9I=
github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0=
github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
github.com/aliyun/credentials-go v1.1.2/go.mod h1:ozcZaMR5kLM7pwtCMEpVmQ242suV6qTJya2bDq4X1Tw=
github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=
-github.com/aliyun/credentials-go v1.3.2 h1:L4WppI9rctC8PdlMgyTkF8bBsy9pyKQEzBD1bHMRl+g=
-github.com/aliyun/credentials-go v1.3.2/go.mod h1:tlpz4uys4Rn7Ik4/piGRrTbXy2uLKvePgQJJduE+Y5c=
-github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
-github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
-github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
-github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
-github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
-github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/aliyun/credentials-go v1.3.6/go.mod h1:1LxUuX7L5YrZUWzBrRyk0SwSdH4OmPrib8NVePL3fxM=
+github.com/aliyun/credentials-go v1.3.10/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
+github.com/aliyun/credentials-go v1.4.3 h1:N3iHyvHRMyOwY1+0qBLSf3hb5JFiOujVSVuEpgeGttY=
+github.com/aliyun/credentials-go v1.4.3/go.mod h1:Jm6d+xIgwJVLVWT561vy67ZRP4lPTQxMbEYRuT2Ti1U=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
-github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
-github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
-github.com/avast/retry-go/v4 v4.5.1 h1:AxIx0HGi4VZ3I02jr78j5lZ3M6x1E0Ivxa6b0pUUh7o=
-github.com/avast/retry-go/v4 v4.5.1/go.mod h1:/sipNsvNB3RRuT5iNcb6h73nw3IBmXJ/H3XrCQYSOpc=
-github.com/aws/aws-sdk-go v1.30.27/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
+github.com/avast/retry-go/v4 v4.6.0 h1:K9xNA+KeB8HHc2aWFuLb25Offp+0iVRXEvFx8IinRJA=
+github.com/avast/retry-go/v4 v4.6.0/go.mod h1:gvWlPhBVsvBbLkVGDg/KwvBv0bEkCOLRRSHKIr2PyOE=
github.com/aws/aws-sdk-go v1.34.0/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
github.com/aws/aws-sdk-go v1.41.13/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
-github.com/aws/aws-sdk-go v1.49.22/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
-github.com/aws/aws-sdk-go v1.51.21 h1:UrT6JC9R9PkYYXDZBV0qDKTualMr+bfK2eboTknMgbs=
-github.com/aws/aws-sdk-go v1.51.21/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
-github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
-github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
+github.com/aws/aws-sdk-go v1.55.6 h1:cSg4pvZ3m8dgYcgqB97MrcdjUmZ1BeMYKUxMMB89IPk=
+github.com/aws/aws-sdk-go v1.55.6/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas=
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4=
-github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
-github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
-github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
-github.com/c2h5oh/datasize v0.0.0-20200112174442-28bbd4740fee/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M=
-github.com/cenkalti/backoff/v3 v3.0.0/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
-github.com/cenkalti/backoff/v3 v3.2.2 h1:cfUAAO3yvKMYKPrvhDuHSwQnhZNk/RMHKdZqKTxfm6M=
-github.com/cenkalti/backoff/v3 v3.2.2/go.mod h1:cIeZDE3IrqwwJl6VUwCN6trj1oXrTS4rc0ij+ULvLYs=
+github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
+github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
-github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw=
-github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
-github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
-github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMtwg/AFW3s=
-github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE=
-github.com/cheggaaa/pb v1.0.29 h1:FckUN5ngEk2LpvuG0fw1GEFx6LtyY2pWI/Z2QgCnEYo=
-github.com/cheggaaa/pb v1.0.29/go.mod h1:W40334L7FMC5JKWldsTWbdGjLo0RxUKK73K+TuPxX30=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
@@ -242,97 +218,75 @@ github.com/clbanning/mxj/v2 v2.5.5/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA=
-github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
-github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
+github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys=
+github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
-github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
-github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
-github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
+github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/ctdk/goiardi v0.11.10 h1:IB/3Afl1pC2Q4KGwzmhHPAoJfe8VtU51wZ2V0QkvsL0=
github.com/ctdk/goiardi v0.11.10/go.mod h1:Pr6Cj6Wsahw45myttaOEZeZ0LE7p1qzWmzgsBISkrNI=
-github.com/cyberark/conjur-api-go v0.11.1 h1:vjaMkw0geJsA+ikMM6UDLg4VLFQWKo/B0i9IWlOQ1f0=
-github.com/cyberark/conjur-api-go v0.11.1/go.mod h1:n1p46Hj9l8wkZjM17cVYdfcatyPboWyioLGlC0QszCs=
-github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
-github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
-github.com/danieljoos/wincred v1.2.1 h1:dl9cBrupW8+r5250DYkYxocLeZ1Y4vB1kxgtjxw8GQs=
-github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps=
+github.com/cyberark/conjur-api-go v0.12.10 h1:exseTvvp7l4Fhw6RTE0kq9Ddipsk+941k945Nyoq8CE=
+github.com/cyberark/conjur-api-go v0.12.10/go.mod h1:XNoyT5ZBLJAGjqXmelLv+eYMG4QxYkZWiw1zld3m0QQ=
+github.com/danieljoos/wincred v1.2.2 h1:774zMFJrqaeYCK2W57BgAem/MLi6mtSE47MB6BOJ0i0=
+github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
+github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
-github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
-github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
-github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
-github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
-github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
-github.com/emicklei/go-restful/v3 v3.12.0 h1:y2DdzBAURM29NFF94q6RaY4vjIH1rtwDapwQtU84iWk=
-github.com/emicklei/go-restful/v3 v3.12.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
-github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
-github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
+github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU=
+github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
-github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U=
github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg=
github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ=
-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
+github.com/external-secrets/sprig/v3 v3.3.0 h1:uO5rmIKSjjONthpCIU8xKbBpAJd0zL/6XFEdC+JsSqU=
+github.com/external-secrets/sprig/v3 v3.3.0/go.mod h1:tvPBN33djer3sQffmfEfcQdL5VYKYmetb4Zbe6wtAq8=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
-github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
-github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
+github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
+github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fortanix/sdkms-client-go v0.4.0 h1:5cKiFJ4rzc69mhsVVI5Ma5ynr/k5vhvws0yfzfIro/k=
github.com/fortanix/sdkms-client-go v0.4.0/go.mod h1:gjylIGX+6poVSe+JkbNsLTvseLd+rLjvcGFgXpW56Lo=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
-github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
-github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
-github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
-github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
+github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
+github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
+github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
+github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
+github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
+github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/gliderlabs/ssh v0.3.7 h1:iV3Bqi942d9huXnzEF2Mt+CY9gLu8DNM4Obd+8bODRE=
-github.com/gliderlabs/ssh v0.3.7/go.mod h1:zpHEXBstFnQYtGnB8k8kQLol82umzn/2/snG7alWVD8=
-github.com/go-chef/chef v0.29.0 h1:7U9P8op5jqaDXo6wANjopiirSENtemTVfoXFEgG8hso=
-github.com/go-chef/chef v0.29.0/go.mod h1:7RU1oCrRErTrkmIszkhJ9vHw7Bv2hZ1Vv1C1qKj01fc=
-github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
-github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
-github.com/go-git/go-billy/v5 v5.5.0 h1:yEY4yhzCDuMGSv83oGxiBotRzhwhNr8VZyphhiu+mTU=
-github.com/go-git/go-billy/v5 v5.5.0/go.mod h1:hmexnoNsr2SJU1Ju67OaNz5ASJY3+sHgFRpCtpDCKow=
-github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
-github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
-github.com/go-git/go-git/v5 v5.12.0 h1:7Md+ndsjrzZxbddRDZjF14qK+NN56sy6wkqaVrjZtys=
-github.com/go-git/go-git/v5 v5.12.0/go.mod h1:FTM9VKtnI2m65hNI/TenDDDnUf2Q9FHnXYjuz9i5OEY=
+github.com/go-chef/chef v0.30.1 h1:yvOSijEBWAQtRbBPj9hz1atEJUU6HckPc7AaEyZXnLg=
+github.com/go-chef/chef v0.30.1/go.mod h1:7RU1oCrRErTrkmIszkhJ9vHw7Bv2hZ1Vv1C1qKj01fc=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
-github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
-github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
-github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
+github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
+github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
-github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
+github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
@@ -353,41 +307,38 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4=
-github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
+github.com/go-playground/validator/v10 v10.24.0 h1:KHQckvo8G6hlWnrPX4NJJ+aBfWNAE/HH+qdL2cBpCmg=
+github.com/go-playground/validator/v10 v10.24.0/go.mod h1:GGzBIJMuE98Ic/kJsBXbz1x/7cByt++cQ+YOuDM5wus=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
-github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
-github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
-github.com/go-test/deep v1.0.2/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
+github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
+github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
github.com/go-test/deep v1.0.4 h1:u2CU3YKy9I2pmu9pX0eq50wCgjfGIt539SqR7FbHiho=
github.com/go-test/deep v1.0.4/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA=
-github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA=
-github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
-github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
-github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
+github.com/gobuffalo/flect v1.0.3 h1:xeWBM2nui+qnVvNM4S3foBhCAL2XgPU+a7FdpelbTq4=
+github.com/gobuffalo/flect v1.0.3/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs=
+github.com/goccy/go-json v0.10.4 h1:JSwxQzIqKfmFX1swYPpUThQZp/Ka4wzJdK0LWVytLPM=
+github.com/goccy/go-json v0.10.4/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
-github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
+github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
+github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
-github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
-github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
+github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
+github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4=
-github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ=
+github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
@@ -417,8 +368,10 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
-github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
-github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
+github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
+github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
+github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
+github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -430,7 +383,6 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
@@ -455,94 +407,76 @@ github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd h1:gbpYu9NMq8jhDVbvlGkMFWCjLFlqqEZjEmObmhUy6Vo=
-github.com/google/pprof v0.0.0-20240409012703-83162a5b38cd/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
+github.com/google/pprof v0.0.0-20250125003558-7fdb3d7e6fa0 h1:my2ucqBZmv+cWHIhZNSIYKzgN8EBGyHdC7zD5sASRAg=
+github.com/google/pprof v0.0.0-20250125003558-7fdb3d7e6fa0/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
-github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
+github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
+github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
+github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
-github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
+github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw=
+github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
-github.com/googleapis/gax-go/v2 v2.12.3 h1:5/zPPDvw8Q1SuXjrqrZslrqT7dL/uJT2CQii/cLCKqA=
-github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4=
+github.com/googleapis/gax-go/v2 v2.14.1 h1:hb0FFeiPaQskmvakKu5EbCbpntQn48jyHuvrkurSS/Q=
+github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEPqouaLeN2IUxoA=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
-github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
-github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
-github.com/hashicorp/go-hclog v0.16.2/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ=
github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
-github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
-github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
-github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
-github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
+github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
+github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=
github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=
-github.com/hashicorp/go-secure-stdlib/awsutil v0.1.6/go.mod h1:MpCPSPGLDILGb4JMm94/mMi3YysIqsXzGCzkEZjcjXg=
github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0 h1:I8bynUKMh9I7JdwtW9voJ0xmHvBpxQtLjrMFDYmhOxY=
github.com/hashicorp/go-secure-stdlib/awsutil v0.3.0/go.mod h1:oKHSQs4ivIfZ3fbXGQOop1XuDfdSb8RIsWTGaAanSfg=
-github.com/hashicorp/go-secure-stdlib/parseutil v0.1.6/go.mod h1:QmrqtbKuxxSWTN3ETMPuB+VtEiBJ/A9XhoYGv8E1uD8=
-github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 h1:iBt4Ew4XEGLfh6/bPk4rSYmuZJGizr6/x/AEizP0CQc=
-github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8/go.mod h1:aiJI+PIApBRQG7FZTEBx5GiiX+HbOHilUdNxUZi4eV0=
-github.com/hashicorp/go-secure-stdlib/strutil v0.1.1/go.mod h1:gKOamz3EwoIoJq7mlMIRBpVTAUn8qPCrEclOKKWhD3U=
+github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9 h1:FW0YttEnUNDJ2WL9XcrrfteS1xW8u+sh4ggM8pN5isQ=
+github.com/hashicorp/go-secure-stdlib/parseutil v0.1.9/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=
github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=
-github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
-github.com/hashicorp/go-sockaddr v1.0.6 h1:RSG8rKU28VTUTvEKghe5gIhIQpv8evvNpnDEyqO4u9I=
-github.com/hashicorp/go-sockaddr v1.0.6/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI=
-github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
+github.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw=
+github.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c=
github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM=
-github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
-github.com/hashicorp/hcl/v2 v2.20.1 h1:M6hgdyz7HYt1UN9e61j+qKJBqR3orTWbI1HKBJEdxtc=
-github.com/hashicorp/hcl/v2 v2.20.1/go.mod h1:TZDqQ4kNKCbh1iJp99FdPiUaVDDUPivbqxZulxDYqL4=
-github.com/hashicorp/vault/api v1.12.0/go.mod h1:si+lJCYO7oGkIoNPAN8j3azBLTn9SjMGS+jFaHd1Cck=
-github.com/hashicorp/vault/api v1.12.2 h1:7YkCTE5Ni90TcmYHDBExdt4WGJxhpzaHqR6uGbQb/rE=
-github.com/hashicorp/vault/api v1.12.2/go.mod h1:LSGf1NGT1BnvFFnKVtnvcaLBM2Lz+gJdpL6HUYed8KE=
-github.com/hashicorp/vault/api/auth/approle v0.6.0 h1:ELfFFQlTM/e97WJKu1HvNFa7lQ3tlTwwzrR1NJE1V7Y=
-github.com/hashicorp/vault/api/auth/approle v0.6.0/go.mod h1:CCoIl1xBC3lAWpd1HV+0ovk76Z8b8Mdepyk21h3pGk0=
-github.com/hashicorp/vault/api/auth/aws v0.6.0 h1:L4mBSAW44EjgX4OJ3w6aDXQeehuGE9OMY9ldNbKgGXM=
-github.com/hashicorp/vault/api/auth/aws v0.6.0/go.mod h1:m4ye0+jgUsLtE+UBszQFgz+0fRiE4qF7MDWgI+mDxbg=
-github.com/hashicorp/vault/api/auth/kubernetes v0.6.0 h1:K8sKGhtTAqGKfzaaYvUSIOAqTOIn3Gk1EsCEAMzZHtM=
-github.com/hashicorp/vault/api/auth/kubernetes v0.6.0/go.mod h1:Htwcjez5J9PwAHaZ1EYMBlgGq3/in5ajUV4+WCPihPE=
-github.com/hashicorp/vault/api/auth/ldap v0.6.0 h1:uvGmLzWQtZ0VZ8TCT2zTfdBNFHiFEG3Z9dQbXp0vZeE=
-github.com/hashicorp/vault/api/auth/ldap v0.6.0/go.mod h1:XE11jJa/5/2wyY1kageQrOlE/q2pmviegh4i5sLf7io=
-github.com/hashicorp/vault/api/auth/userpass v0.6.0 h1:wpiGIbS7CMdqqqs7GNQMO+AQW6DxecGBDTgxaBW5R9Q=
-github.com/hashicorp/vault/api/auth/userpass v0.6.0/go.mod h1:BYLic7wPxTqn35FX0nKU2oCdZYEDJ/UCFQY0zO4AImI=
+github.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I=
+github.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=
+github.com/hashicorp/vault/api v1.15.0 h1:O24FYQCWwhwKnF7CuSqP30S51rTV7vz1iACXE/pj5DA=
+github.com/hashicorp/vault/api v1.15.0/go.mod h1:+5YTO09JGn0u+b6ySD/LLVf8WkJCPLAL2Vkmrn2+CM8=
+github.com/hashicorp/vault/api/auth/approle v0.8.0 h1:FuVtWZ0xD6+wz1x0l5s0b4852RmVXQNEiKhVXt6lfQY=
+github.com/hashicorp/vault/api/auth/approle v0.8.0/go.mod h1:NV7O9r5JUtNdVnqVZeMHva81AIdpG0WoIQohNt1VCPM=
+github.com/hashicorp/vault/api/auth/aws v0.8.0 h1:6E14D7eHjV+Ytk8HmKLbTGS/LaXD9hP2FXe7IIKCrHc=
+github.com/hashicorp/vault/api/auth/aws v0.8.0/go.mod h1:SweK5366gCeO5krBk6Fpjz/MX2oa+iiIZz/Nu8/nMZw=
+github.com/hashicorp/vault/api/auth/kubernetes v0.8.0 h1:6jPcORq7OHwf+MCbaaUmiBvMhETAaZ7+i97WfZtF5kc=
+github.com/hashicorp/vault/api/auth/kubernetes v0.8.0/go.mod h1:nfl5sRUUork0ZSfV3xf+pgAFQSD5kSkL0k9axg523DM=
+github.com/hashicorp/vault/api/auth/ldap v0.8.0 h1:rMd27r3VplnE7NXOpxJTge8wJf3tnXK6Q46Drq54vSQ=
+github.com/hashicorp/vault/api/auth/ldap v0.8.0/go.mod h1:01zeaPvJUIGmMWEyEfQAiborO/ajDR1PIh5j/yym+QM=
+github.com/hashicorp/vault/api/auth/userpass v0.8.0 h1:JFFzMld+VO/S1v8HQNJzcy+3o+xfx/iH49dsiQ1G5jk=
+github.com/hashicorp/vault/api/auth/userpass v0.8.0/go.mod h1:+XbsSnbbyo+yjySfKcIsyl28kO4C/c4Czo7og0XCtUo=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
-github.com/huandu/xstrings v1.4.0 h1:D17IlohoQq4UcpqD7fDk80P7l+lwAmlFaBHgOipl2FU=
-github.com/huandu/xstrings v1.4.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
+github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
+github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
-github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
-github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
-github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
-github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
@@ -556,13 +490,14 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
-github.com/keeper-security/secrets-manager-go/core v1.6.2 h1:bRZUJI/s5WwVbceSNlKyKqYuBNKkZCyNPH4lU2GYiF0=
-github.com/keeper-security/secrets-manager-go/core v1.6.2/go.mod h1:dtlaeeds9+SZsbDAZnQRsDSqEAK9a62SYtqhNql+VgQ=
-github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
-github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
-github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
+github.com/keeper-security/secrets-manager-go/core v1.6.4 h1:ly2XvAgDxHoHVvFXOIYlxzxBF0yoQir1KfNHUNG4eRA=
+github.com/keeper-security/secrets-manager-go/core v1.6.4/go.mod h1:dtlaeeds9+SZsbDAZnQRsDSqEAK9a62SYtqhNql+VgQ=
+github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6 h1:IsMZxCuZqKuao2vNdfD82fjjgPLfyHLpR41Z88viRWs=
+github.com/keybase/go-keychain v0.0.0-20231219164618-57a3676c3af6/go.mod h1:3VeWNIJaW+O5xpRQbPp0Ybqu1vJd/pm7s2F473HRrkw=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
+github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
+github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
@@ -580,57 +515,33 @@ github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
-github.com/lestrrat-go/httprc v1.0.5 h1:bsTfiH8xaKOJPrg1R+E3iE/AWZr/x0Phj9PBTG/OLUk=
-github.com/lestrrat-go/httprc v1.0.5/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
+github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k=
+github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
-github.com/lestrrat-go/jwx/v2 v2.0.21 h1:jAPKupy4uHgrHFEdjVjNkUgoBKtVDgrQPB/h55FHrR0=
-github.com/lestrrat-go/jwx/v2 v2.0.21/go.mod h1:09mLW8zto6bWL9GbwnqAli+ArLf+5M33QLQPDggkUWM=
+github.com/lestrrat-go/jwx/v2 v2.1.3 h1:Ud4lb2QuxRClYAmRleF50KrbKIoM1TddXgBrneT5/Jo=
+github.com/lestrrat-go/jwx/v2 v2.1.3/go.mod h1:q6uFgbgZfEmQrfJfrCo90QcQOcXFMfbI/fO0NqRtvZo=
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
-github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
-github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
-github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
-github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
-github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
-github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
+github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
+github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
-github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
+github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
+github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
-github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
-github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
-github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
-github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1 h1:NicmruxkeqHjDv03SfSxqmaLuisddudfP3h5wdXFbhM=
-github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1/go.mod h1:eyp4DdUJAKkr9tvxR3jWhw2mDK7CWABMG5r9uyaKC7I=
-github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
+github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2 h1:yVCLo4+ACVroOEr4iFU1iH46Ldlzz2rTuu18Ra7M8sU=
+github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2/go.mod h1:VzB2VoMh1Y32/QqDfg9ZJYHj99oM4LiGtqPZydTiQSQ=
github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw=
github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw=
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
-github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
-github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU=
github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8=
-github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
-github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
-github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
-github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
@@ -645,14 +556,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
-github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
-github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
-github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA=
-github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo=
-github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
-github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
-github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
-github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
@@ -662,99 +565,77 @@ github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
-github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
-github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
-github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8=
-github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ=
-github.com/opentracing/basictracer-go v1.1.0 h1:Oa1fTSBvAl8pa3U+IJYqrKm0NALwH9OsgwOqDv4xJW0=
-github.com/opentracing/basictracer-go v1.1.0/go.mod h1:V2HZueSJEp879yv285Aap1BS69fQMD+MNP1mRs6mBQc=
-github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
+github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU=
+github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk=
+github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8=
+github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY=
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A=
github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU=
-github.com/oracle/oci-go-sdk/v65 v65.63.1 h1:dYL7sk9L1+C9LCmoq+zjPMNteuJJfk54YExq/4pV9xQ=
-github.com/oracle/oci-go-sdk/v65 v65.63.1/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0=
-github.com/passbolt/go-passbolt v0.7.0 h1:zwwTCwL3vjTTKln1hxwKuzzax4R/yvxGXSZhMh0OY5Y=
-github.com/passbolt/go-passbolt v0.7.0/go.mod h1:af3TVSJ+0A4sXeK8KgVzhV8Tej/i25biFIQjhL0FOMk=
-github.com/pgavlin/fx v0.1.6 h1:r9jEg69DhNoCd3Xh0+5mIbdbS3PqWrVWujkY76MFRTU=
-github.com/pgavlin/fx v0.1.6/go.mod h1:KWZJ6fqBBSh8GxHYqwYCf3rYE7Gp2p0N8tJp8xv9u9M=
-github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
-github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
+github.com/oracle/oci-go-sdk/v65 v65.81.3 h1:L4JcHSV4xLxySfZOQumUazlRN/2u/7r7Muw0Apg7UYI=
+github.com/oracle/oci-go-sdk/v65 v65.81.3/go.mod h1:IBEV9l1qBzUpo7zgGaRUhbB05BVfcDGYRFBCPlTcPp0=
+github.com/passbolt/go-passbolt v0.7.1 h1:boNYHZmSnWl/3bKbUiaWgF/mELCtHfliGHzggf884GE=
+github.com/passbolt/go-passbolt v0.7.1/go.mod h1:if/jzzYYUjRtq/5h+l+J5Dka0f5dED67QM1lhpTx4pY=
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
-github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
-github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/previder/vault-cli v0.1.2 h1:aui5v+L243JGbRaJ65z5XsuItjyCtoBND32v1XU3gd4=
+github.com/previder/vault-cli v0.1.2/go.mod h1:u9JDPB5/Em/Czjb/yIwfTODr31kKmeSO3JGrheLMaP8=
+github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
+github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
-github.com/prometheus/common v0.52.3 h1:5f8uj6ZwHSscOGNdIQg6OiZv/ybiK2CO2q2drVZAQSA=
-github.com/prometheus/common v0.52.3/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U=
-github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
-github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
-github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231 h1:vkHw5I/plNdTr435cARxCW6q9gc0S/Yxz7Mkd38pOb0=
-github.com/pulumi/appdash v0.0.0-20231130102222-75f619a67231/go.mod h1:murToZ2N9hNJzewjHBgfFdXhZKjY3z5cYC1VXk+lbFE=
-github.com/pulumi/esc v0.8.3 h1:myeDL6dD/mz34zZjCL8s7d/tWHBJYxfMxDCL11MHoqc=
-github.com/pulumi/esc v0.8.3/go.mod h1:v5VAPxYDa9DRwvubbzKt4ZYf5y0esWC2ccSp/AT923I=
-github.com/pulumi/pulumi/sdk/v3 v3.112.0 h1:cq2x5N6iuYhSLdeOdRs+LIq0EneB0Cb54WOlD/VaX3E=
-github.com/pulumi/pulumi/sdk/v3 v3.112.0/go.mod h1:JWSzKBoHd8rlncC1DhXLf7YdV+Bk/Qf+hSZOOQh0WwQ=
+github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
+github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
+github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
+github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
+github.com/pulumi/esc-sdk/sdk v0.11.0 h1:Bpir0Mo6n3WkSt7R2YIo8r4Z3iQ82d3P8BKMR2miQbw=
+github.com/pulumi/esc-sdk/sdk v0.11.0/go.mod h1:J6+8bCUJyLXvYOmTAc90/EhU1iUPr1Koo3NUnFzY78k=
github.com/r3labs/diff v0.0.0-20191120142937-b4ed99a31f5a h1:2v4Ipjxa3sh+xn6GvtgrMub2ci4ZLQMvTaYIba2lfdc=
github.com/r3labs/diff v0.0.0-20191120142937-b4ed99a31f5a/go.mod h1:ozniNEFS3j1qCwHKdvraMn1WJOsUxHd7lYfukEIS4cs=
-github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
-github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
-github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
+github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E=
+github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
-github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
+github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
+github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
-github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06 h1:OkMGxebDjyw0ULyrTYWeN0UNCCkmCWfjPnIA2W6oviI=
-github.com/sabhiram/go-gitignore v0.0.0-20210923224102-525f6e181f06/go.mod h1:+ePHsJ1keEjQtpvf9HHw0f4ZeJ0TLRsxhunSI2hYJSs=
-github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
-github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
-github.com/scaleway/scaleway-sdk-go v1.0.0-beta.26 h1:F+GIVtGqCFxPxO46ujf8cEOP574MBoRm3gNbPXECbxs=
-github.com/scaleway/scaleway-sdk-go v1.0.0-beta.26/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
+github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30 h1:yoKAVkEVwAqbGbR8n87rHQ1dulL25rKloGadb3vm770=
+github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30/go.mod h1:sH0u6fq6x4R5M7WxkoQFY/o7UaiItec0o1LinLCJNq8=
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
-github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
-github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
-github.com/sethvargo/go-password v0.2.0 h1:BTDl4CC/gjf/axHMaDQtw507ogrXLci6XRiLc7i/UHI=
-github.com/sethvargo/go-password v0.2.0/go.mod h1:Ym4Mr9JXLBycr02MFuVQ/0JHidNetSgbzutTr3zsYXE=
+github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU=
+github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs=
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
-github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/skeema/knownhosts v1.2.2 h1:Iug2P4fLmDw9f41PB6thxUkNUkJzB5i+1/exaj40L3A=
-github.com/skeema/knownhosts v1.2.2/go.mod h1:xYbVRSPxqBZFrdmDyMmsOs+uX1UZC3nTN3ThzgDxUwo=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v1.1.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg=
github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
+github.com/sony/gobreaker v1.0.0 h1:feX5fGGXSl3dYd4aHZItw+FpHLvvoaqkawKjVNiFMNQ=
+github.com/sony/gobreaker v1.0.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
-github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
-github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
-github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
+github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
+github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
+github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
+github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -775,13 +656,11 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
-github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
-github.com/texttheater/golang-levenshtein v1.0.1 h1:+cRNoVrfiwufQPhoMzB6N0Yf/Mqajr6t1lOv8GyGE2U=
-github.com/texttheater/golang-levenshtein v1.0.1/go.mod h1:PYAKrbF5sAiq9wd+H82hs7gNaen0CplQ9uvm6+enD/8=
+github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
+github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
-github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
-github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
+github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
+github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
@@ -792,35 +671,30 @@ github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6
github.com/tjfoc/gmsm v1.3.2/go.mod h1:HaUcFuY0auTiaHB9MHFGCPx5IaLhTUd2atbCFBQXn9w=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
-github.com/tweekmonster/luser v0.0.0-20161003172636-3fa38070dbd7 h1:X9dsIWPuuEJlPX//UmRKophhOKCGXc46RVIGuttks68=
-github.com/tweekmonster/luser v0.0.0-20161003172636-3fa38070dbd7/go.mod h1:UxoP3EypF8JfGEjAII8jx1q8rQyDnX8qdTCs/UQBVIE=
github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o=
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg=
github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
-github.com/xanzy/go-gitlab v0.102.0 h1:ExHuJ1OTQ2yt25zBMMj0G96ChBirGYv8U7HyUiYkZ+4=
-github.com/xanzy/go-gitlab v0.102.0/go.mod h1:ETg8tcj4OhrB84UEgeE8dSuV/0h4BBL1uOV/qK0vlyI=
-github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
-github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
-github.com/yandex-cloud/go-genproto v0.0.0-20240318083951-4fe6125f286e/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE=
-github.com/yandex-cloud/go-genproto v0.0.0-20240401111333-b9ee0d3d9e6b h1:mOhpdzir8wyeM0AzMPKj6RteKpRjaP661fBPzJcRD+g=
-github.com/yandex-cloud/go-genproto v0.0.0-20240401111333-b9ee0d3d9e6b/go.mod h1:HEUYX/p8966tMUHHT+TsS0hF/Ca/NYwqprC5WXSDMfE=
-github.com/yandex-cloud/go-sdk v0.0.0-20240318084659-dfa50323a0b4 h1:wtzLQJmghkSUb1YkeFphIh7ST7NNVDaVOJZSAJcjMdw=
-github.com/yandex-cloud/go-sdk v0.0.0-20240318084659-dfa50323a0b4/go.mod h1:9d1MV6u4lK715YXnZceKqhP4L0bKBKmv4mSLnVSjJaM=
-github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk=
-github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4=
+github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
+github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
+github.com/yandex-cloud/go-genproto v0.0.0-20241220122821-aeb3b05efd1c h1:Rnr+lDYXVkP+3eT8/d68iq4G/UeIhyCQk+HKa8toTvg=
+github.com/yandex-cloud/go-genproto v0.0.0-20241220122821-aeb3b05efd1c/go.mod h1:0LDD/IZLIUIV4iPH+YcF+jysO3jkSvADFGm4dCAuwQo=
+github.com/yandex-cloud/go-sdk v0.0.0-20241220131134-2393e243c134 h1:qmpz0Kvr9GAng8LAhRcKIpY71CEAcL3EBkftVlsP5Cw=
+github.com/yandex-cloud/go-sdk v0.0.0-20241220131134-2393e243c134/go.mod h1:KgZCJrxdhdw/sKhTQ/M3S9WOLri2PCnBlc4C3s+PfKY=
+github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
+github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-github.com/zalando/go-keyring v0.2.4 h1:wi2xxTqdiwMKbM6TWwi+uJCG/Tum2UV0jqaQhCa9/68=
-github.com/zalando/go-keyring v0.2.4/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=
-github.com/zclconf/go-cty v1.14.4 h1:uXXczd9QDGsgu0i/QFR/hzI5NYCHLf6NQw/atrbnhq8=
-github.com/zclconf/go-cty v1.14.4/go.mod h1:VvMs5i0vgZdhYawQNq5kePSpLAoz8u1xvZgrPIxfnZE=
-go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc=
-go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
+github.com/zalando/go-keyring v0.2.6 h1:r7Yc3+H+Ux0+M72zacZoItR3UDxeWfKTcabvkI8ua9s=
+github.com/zalando/go-keyring v0.2.6/go.mod h1:2TCrxYrbUNYfNS/Kgy/LSrkSQzZ5UPVH85RwfczwvcI=
+gitlab.com/gitlab-org/api/client-go v0.120.0 h1:geCJjojDXxWVmUcTxPcOUCenAWElWB5dVfX3HJGeAMc=
+gitlab.com/gitlab-org/api/client-go v0.120.0/go.mod h1:ygHmS3AU3TpvK+AC6DYO1QuAxLlv6yxYK+/Votr/WFQ=
+go.mongodb.org/mongo-driver v1.17.2 h1:gvZyk8352qSfzyZ2UMWcpDpMSGEr1eqE4T793SqyhzM=
+go.mongodb.org/mongo-driver v1.17.2/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
@@ -830,19 +704,22 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.50.0 h1:zvpPXY7RfYAGSdYQLjp6zxdJNSYD/+FFoCTQN9IPxBs=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.50.0/go.mod h1:BMn8NB1vsxTljvuorms2hyOs8IBuuBEq0pl7ltOfy30=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0 h1:cEPbyTSEHlQR89XVlyo78gqluF8Y3oMeBkXGWzQsfXY=
-go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.50.0/go.mod h1:DKdbWcT4GH1D0Y3Sqt/PFXt2naRKDWtU+eE6oLdFNA8=
-go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k=
-go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg=
-go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA=
-go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s=
-go.opentelemetry.io/otel/sdk v1.22.0 h1:6coWHw9xw7EfClIC/+O31R8IY3/+EiRFHevmHafB2Gw=
-go.opentelemetry.io/otel/sdk v1.22.0/go.mod h1:iu7luyVGYovrRpe2fmj3CVKouQNdTOkxtLzPvPz1DOc=
-go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM=
-go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I=
-go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
+go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
+go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
+go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
+go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
+go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
+go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
+go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
+go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
+go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
+go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
@@ -854,21 +731,15 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20211215153901-e495a2d5b3d3/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
-golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
-golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
@@ -876,8 +747,9 @@ golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
-golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
-golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
+golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
+golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
+golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -888,8 +760,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
-golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8 h1:ESSUROHIBHg7USnszlcdmjBEwdMj9VUvU+OPk4yl2mc=
-golang.org/x/exp v0.0.0-20240409090435-93d18d7e34b8/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -915,8 +785,8 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
-golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
+golang.org/x/mod v0.22.0 h1:D4nJWe9zXqHOmWqj4VMOJhvzj7bEZg4wEYa759z1pH4=
+golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -936,7 +806,6 @@ golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLL
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
-golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
@@ -952,22 +821,22 @@ golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v
golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
-golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ=
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
-golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
-golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
+golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
+golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
+golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -981,8 +850,8 @@ golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210413134643-5e61552d6c78/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
-golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg=
-golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8=
+golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
+golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -996,23 +865,18 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
-golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
+golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1029,7 +893,6 @@ golang.org/x/sys v0.0.0-20200509044756-6aff5f38e54f/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1037,28 +900,22 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210412220455-f1c623a9e750/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1068,13 +925,13 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
-golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
+golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo=
golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
@@ -1082,8 +939,9 @@ golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
-golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
-golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
+golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
+golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
+golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1099,16 +957,15 @@ golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
+golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
-golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
+golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
+golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
@@ -1161,8 +1018,8 @@ golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
-golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
+golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
+golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1191,8 +1048,8 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.45.0/go.mod h1:ISLIJCedJolbZvDfAk+Ctuq5hf+aJ33WgtUsfyFoLXA=
-google.golang.org/api v0.172.0 h1:/1OcMZGPmW1rX2LCu2CmGUD1KXK1+pfzxotxyRUCCdk=
-google.golang.org/api v0.172.0/go.mod h1:+fJZq6QXWfa9pXhnIzsjx4yI22d4aI9ZpLb58gvXjis=
+google.golang.org/api v0.218.0 h1:x6JCjEWeZ9PFCRe9z0FBrNwj7pB7DOAqT35N+IPnAUA=
+google.golang.org/api v0.218.0/go.mod h1:5VGHBAkxrA/8EFjLVEYmMUJ8/8+gWWQ3s4cFH0FxG2M=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1223,7 +1080,6 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
-google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
@@ -1241,13 +1097,12 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210413151531-c14fb6ef47c3/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
-google.golang.org/genproto v0.0.0-20211021150943-2b146023228c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20240412170617-26222e5d3d56 h1:LlcUFJ4BLmJVS4Kly+WCK7LQqcevmycHj88EPgyhNx8=
-google.golang.org/genproto v0.0.0-20240412170617-26222e5d3d56/go.mod h1:n1CaIKYMIlxFt1zJE/1kU40YpSL0drGMbl0Idum1VSs=
-google.golang.org/genproto/googleapis/api v0.0.0-20240412170617-26222e5d3d56 h1:KuFzeG+qPmpT8KpJXcrKAyeHhn64dgEICWlccP9qp0U=
-google.golang.org/genproto/googleapis/api v0.0.0-20240412170617-26222e5d3d56/go.mod h1:wTHjrkbcS8AoQbb/0v9bFIPItZQPAsyVfgG9YPUhjAM=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240412170617-26222e5d3d56 h1:zviK8GX4VlMstrK3JkexM5UHjH1VOkRebH9y3jhSBGk=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240412170617-26222e5d3d56/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
+google.golang.org/genproto v0.0.0-20250124145028-65684f501c47 h1:SI8Hf7K4+uVYchXqZiMfP44PZ83xomMWovbcFfm0P8Q=
+google.golang.org/genproto v0.0.0-20250124145028-65684f501c47/go.mod h1:qbZzneIOXSq+KFAFut9krLfRLZiFLzZL5u2t8SV83EE=
+google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47 h1:5iw9XJTD4thFidQmFVvx0wi4g5yOHk76rNRUxz1ZG5g=
+google.golang.org/genproto/googleapis/api v0.0.0-20250124145028-65684f501c47/go.mod h1:AfA77qWLcidQWywD0YgqfpJzf50w2VjzBml3TybHeJU=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47 h1:91mG8dNTpkC0uChJUQ9zCiRqx3GEEFOWaRZ0mI6Oj2I=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20250124145028-65684f501c47/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1261,17 +1116,14 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
-google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
-google.golang.org/grpc v1.63.2 h1:MUeiw1B2maTVZthpU5xvASfTh3LDbxHd6IJ6QQVU+xM=
-google.golang.org/grpc v1.63.2/go.mod h1:WAX/8DgncnokcFUldAxq7GeB5DXHDbMF+lLvDomNkRA=
+google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
+google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1284,10 +1136,9 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
-google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
+google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -1295,6 +1146,10 @@ gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
+gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
+gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
+gopkg.in/ghodss/yaml.v1 v1.0.0 h1:JlY4R6oVz+ZSvcDhVfNQ/k/8Xo6yb2s1PBhslPZPX4c=
+gopkg.in/ghodss/yaml.v1 v1.0.0/go.mod h1:HDvRMPQLqycKPs9nWLuzZWxsxRzISLCRORiDpBUOMqg=
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.56.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@@ -1302,13 +1157,9 @@ gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
-gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
@@ -1323,46 +1174,40 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
-k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw=
-k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80=
-k8s.io/apiextensions-apiserver v0.29.3 h1:9HF+EtZaVpFjStakF4yVufnXGPRppWFEQ87qnO91YeI=
-k8s.io/apiextensions-apiserver v0.29.3/go.mod h1:po0XiY5scnpJfFizNGo6puNU6Fq6D70UJY2Cb2KwAVc=
-k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU=
-k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU=
-k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg=
-k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0=
-k8s.io/component-base v0.29.3 h1:Oq9/nddUxlnrCuuR2K/jp6aflVvc0uDvxMzAWxnGzAo=
-k8s.io/component-base v0.29.3/go.mod h1:Yuj33XXjuOk2BAaHsIGHhCKZQAgYKhqIxIjIr2UXYio=
+k8s.io/api v0.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc=
+k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k=
+k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw=
+k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto=
+k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs=
+k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
+k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU=
+k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg=
k8s.io/gengo v0.0.0-20201203183100-97869a43a9d9/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
-k8s.io/gengo v0.0.0-20240404160639-a0386bf69313 h1:wBIDZID8ju9pwOiLlV22YYKjFGtiNSWgHf5CnKLRUuM=
-k8s.io/gengo v0.0.0-20240404160639-a0386bf69313/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
+k8s.io/gengo v0.0.0-20250106234829-0359904fc2a6 h1:1+JP7kneHC0+mprySiI1c9c9QsBsXMMaozt6+asWx3Y=
+k8s.io/gengo v0.0.0-20250106234829-0359904fc2a6/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/klog v0.2.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8=
k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
-k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
-k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
-k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3 h1:SbdLaI6mM6ffDSJCadEaD4IkuPzepLDGlkd2xV0t1uA=
-k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
-k8s.io/utils v0.0.0-20240310230437-4693a0247e57 h1:gbqbevonBh57eILzModw6mrkbwM0gQBEuevE/AaBsHY=
-k8s.io/utils v0.0.0-20240310230437-4693a0247e57/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
-lukechampine.com/frand v1.4.2 h1:RzFIpOvkMXuPMBb9maa4ND4wjBn71E1Jpf8BzJHMaVw=
-lukechampine.com/frand v1.4.2/go.mod h1:4S/TM2ZgrKejMcKMbeLjISpJMO+/eZ1zu3vYX9dtj3s=
-pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA=
-pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=
+k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
+k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
+k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg=
+k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas=
+k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
+k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
-sigs.k8s.io/controller-runtime v0.17.3 h1:65QmN7r3FWgTxDMz9fvGnO1kbf2nu+acg9p2R9oYYYk=
-sigs.k8s.io/controller-runtime v0.17.3/go.mod h1:N0jpP5Lo7lMTF9aL56Z/B2oWBJjey6StQM0jRbKQXtY=
-sigs.k8s.io/controller-tools v0.14.0 h1:rnNoCC5wSXlrNoBKKzL70LNJKIQKEzT6lloG6/LF73A=
-sigs.k8s.io/controller-tools v0.14.0/go.mod h1:TV7uOtNNnnR72SpzhStvPkoS/U5ir0nMudrkrC4M9Sc=
-sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
-sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
-sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
-sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
+sigs.k8s.io/controller-runtime v0.20.1 h1:JbGMAG/X94NeM3xvjenVUaBjy6Ui4Ogd/J5ZtjZnHaE=
+sigs.k8s.io/controller-runtime v0.20.1/go.mod h1:BrP3w158MwvB3ZbNpaAcIKkHQ7YGpYnzpoSTZ8E14WU=
+sigs.k8s.io/controller-tools v0.17.1 h1:bQ+dKCS7jY9AgpefenBDtm6geJZCHVKbegpLynxgyus=
+sigs.k8s.io/controller-tools v0.17.1/go.mod h1:3QXAdrmdxYuQ4MifvbCAFD9wLXn7jylnfBPYS4yVDdc=
+sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
+sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
+sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk=
+sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
-software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=
-software.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
+software.sslmate.com/src/go-pkcs12 v0.5.0 h1:EC6R394xgENTpZ4RltKydeDUjtlM5drOYIG9c6TVj2M=
+software.sslmate.com/src/go-pkcs12 v0.5.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
diff --git a/hack/api-docs/Dockerfile b/hack/api-docs/Dockerfile
index 16fea10a780..a701559a5ae 100644
--- a/hack/api-docs/Dockerfile
+++ b/hack/api-docs/Dockerfile
@@ -11,18 +11,18 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-FROM alpine:3.19@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b
+FROM alpine:3.21@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099
RUN apk add -U --no-cache \
- python3 \
- python3-dev \
- py3-pip \
- musl-dev \
- git \
- openssh \
- git-fast-import \
bash \
+ diffutils \
gcc \
- diffutils
+ git \
+ git-fast-import \
+ musl-dev \
+ openssh \
+ py3-pip \
+ python3 \
+ python3-dev
ENV PATH=$PATH:/.venv/bin
COPY requirements.txt /
diff --git a/hack/api-docs/mkdocs.yml b/hack/api-docs/mkdocs.yml
index c1de698ea97..94a54520e15 100644
--- a/hack/api-docs/mkdocs.yml
+++ b/hack/api-docs/mkdocs.yml
@@ -7,6 +7,17 @@ edit_uri: ./edit/main/docs/
remote_branch: gh-pages
theme:
name: material
+ palette:
+ - scheme: default
+ media: "(prefers-color-scheme: light)"
+ toggle:
+ icon: material/brightness-7
+ name: Switch to dark mode
+ - scheme: slate
+ media: "(prefers-color-scheme: dark)"
+ toggle:
+ icon: material/brightness-4
+ name: Switch to light mode
features:
- navigation.tabs
- navigation.indexes
@@ -39,12 +50,14 @@ extra:
property: G-QP38TD8K7V
nav:
- Introduction:
- - Introduction: index.md
- - Overview: introduction/overview.md
- - Getting started: introduction/getting-started.md
- - FAQ: introduction/faq.md
- - Stability and Support: introduction/stability-support.md
- - Deprecation Policy: introduction/deprecation-policy.md
+ - Introduction: index.md
+ - Overview: introduction/overview.md
+ - Glossary: introduction/glossary.md
+ - Prerequisites: introduction/prerequisites.md
+ - Getting started: introduction/getting-started.md
+ - FAQ: introduction/faq.md
+ - Stability and Support: introduction/stability-support.md
+ - Deprecation Policy: introduction/deprecation-policy.md
- API:
- Components: api/components.md
- Core Resources:
@@ -57,102 +70,94 @@ nav:
- "api/generator/index.md"
- Azure Container Registry: api/generator/acr.md
- AWS Elastic Container Registry: api/generator/ecr.md
+ - AWS STS Session Token: api/generator/sts.md
+ - Cluster Generator: api/generator/cluster.md
- Google Container Registry: api/generator/gcr.md
+ - Quay: api/generator/quay.md
- Vault Dynamic Secret: api/generator/vault.md
- Password: api/generator/password.md
- Fake: api/generator/fake.md
- Webhook: api/generator/webhook.md
+ - Github: api/generator/github.md
+ - UUID: api/generator/uuid.md
- Reference Docs:
- API specification: api/spec.md
- Controller Options: api/controller-options.md
- Metrics: api/metrics.md
- Guides:
- - Introduction: guides/introduction.md
- - External Secrets:
- - Extract structured data: guides/all-keys-one-secret.md
- - Find Secrets by Name or Metadata: guides/getallsecrets.md
- - Rewriting Keys: guides/datafrom-rewrite.md
- - Advanced Templating:
- v2: guides/templating.md
- v1: guides/templating-v1.md
- - Kubernetes Secret Types: guides/common-k8s-secret-types.md
- - "Lifecycle: ownership & deletion": guides/ownership-deletion-policy.md
- - Decoding Strategies: guides/decoding-strategy.md
- - Controller Classes: guides/controller-class.md
- - Generators: guides/generator.md
- - Push Secrets: guides/pushsecrets.md
- - Operations:
- - Multi Tenancy: guides/multi-tenancy.md
- - Security Best Practices: guides/security-best-practices.md
- - Threat Model: guides/threat-model.md
- - Upgrading to v1beta1: guides/v1beta1.md
- - Using Latest Image: guides/using-latest-image.md
- - Disable Cluster Features: guides/disable-cluster-features.md
+ - Introduction: guides/introduction.md
+ - External Secrets:
+ - Extract structured data: guides/all-keys-one-secret.md
+ - Find Secrets by Name or Metadata: guides/getallsecrets.md
+ - Rewriting Keys: guides/datafrom-rewrite.md
+ - Advanced Templating:
+ - v2: guides/templating.md
+ - v1: guides/templating-v1.md
+ - Kubernetes Secret Types: guides/common-k8s-secret-types.md
+ - "Lifecycle: ownership & deletion": guides/ownership-deletion-policy.md
+ - Decoding Strategies: guides/decoding-strategy.md
+ - Controller Classes: guides/controller-class.md
+ - Generators: guides/generator.md
+ - Push Secrets: guides/pushsecrets.md
+ - Operations:
+ - Multi Tenancy: guides/multi-tenancy.md
+ - Security Best Practices: guides/security-best-practices.md
+ - Threat Model: guides/threat-model.md
+ - Upgrading to v1beta1: guides/v1beta1.md
+ - Using Latest Image: guides/using-latest-image.md
+ - Disable Cluster Features: guides/disable-cluster-features.md
+ - Tooling:
+ - Using the esoctl tool: guides/using-esoctl-tool.md
- Provider:
- - AWS Secrets Manager: provider/aws-secrets-manager.md
- - AWS Parameter Store: provider/aws-parameter-store.md
- - Azure Key Vault: provider/azure-key-vault.md
- - Chef: provider/chef.md
- - CyberArk Conjur: provider/conjur.md
- - Google Cloud Secret Manager: provider/google-secrets-manager.md
- - HashiCorp Vault: provider/hashicorp-vault.md
- - Kubernetes: provider/kubernetes.md
- - IBM Secrets Manager: provider/ibm-secrets-manager.md
- - Akeyless: provider/akeyless.md
- - Yandex Certificate Manager: provider/yandex-certificate-manager.md
- - Yandex Lockbox: provider/yandex-lockbox.md
- - Alibaba Cloud: provider/alibaba.md
- - GitLab Variables: provider/gitlab-variables.md
- - Oracle Vault: provider/oracle-vault.md
- - 1Password Secrets Automation: provider/1password-automation.md
- - Webhook: provider/webhook.md
- - Fake: provider/fake.md
- - senhasegura DevOps Secrets Management (DSM): provider/senhasegura-dsm.md
- - Doppler: provider/doppler.md
- - Keeper Security: provider/keeper-security.md
- - Cloak End 2 End Encrypted Secrets: provider/cloak.md
- - Scaleway: provider/scaleway.md
- - Delinea: provider/delinea.md
- - Passbolt: provider/passbolt.md
- - Pulumi ESC: provider/pulumi.md
- - Onboardbase: provider/onboardbase.md
+ - AWS Secrets Manager: provider/aws-secrets-manager.md
+ - AWS Parameter Store: provider/aws-parameter-store.md
+ - Azure Key Vault: provider/azure-key-vault.md
+ - BeyondTrust: provider/beyondtrust.md
+ - Bitwarden Secrets Manager: provider/bitwarden-secrets-manager.md
+ - Chef: provider/chef.md
+ - CyberArk Conjur: provider/conjur.md
+ - Device42: provider/device42.md
+ - Google Cloud Secret Manager: provider/google-secrets-manager.md
+ - HashiCorp Vault: provider/hashicorp-vault.md
+ - Kubernetes: provider/kubernetes.md
+ - IBM Secrets Manager: provider/ibm-secrets-manager.md
+ - Akeyless: provider/akeyless.md
+ - Yandex Certificate Manager: provider/yandex-certificate-manager.md
+ - Yandex Lockbox: provider/yandex-lockbox.md
+ - Alibaba Cloud: provider/alibaba.md
+ - GitLab Variables: provider/gitlab-variables.md
+ - Oracle Vault: provider/oracle-vault.md
+ - 1Password Secrets Automation: provider/1password-automation.md
+ - Webhook: provider/webhook.md
+ - Fake: provider/fake.md
+ - senhasegura DevOps Secrets Management (DSM): provider/senhasegura-dsm.md
+ - Doppler: provider/doppler.md
+ - Keeper Security: provider/keeper-security.md
+ - Cloak End 2 End Encrypted Secrets: provider/cloak.md
+ - Scaleway: provider/scaleway.md
+ - Delinea: provider/delinea.md
+ - Secret Server: provider/secretserver.md
+ - Passbolt: provider/passbolt.md
+ - Pulumi ESC: provider/pulumi.md
+ - Onboardbase: provider/onboardbase.md
+ - Password Depot: provider-passworddepot.md
+ - Fortanix: provider/fortanix.md
+ - Infisical: provider/infisical.md
+ - Previder: provider/previder.md
- Examples:
- - FluxCD: examples/gitops-using-fluxcd.md
- - Anchore Engine: examples/anchore-engine-credentials.md
- - Jenkins: examples/jenkins-kubernetes-credentials.md
- - BitWarden: examples/bitwarden.md
+ - FluxCD: examples/gitops-using-fluxcd.md
+ - Anchore Engine: examples/anchore-engine-credentials.md
+ - Jenkins: examples/jenkins-kubernetes-credentials.md
+ - BitWarden: examples/bitwarden.md
- Community:
- - Contributing:
- - Developer guide: contributing/devguide.md
- - Contributing Process: contributing/process.md
- - Release Process: contributing/release.md
- - Code of Conduct: contributing/coc.md
- - Roadmap: contributing/roadmap.md
- - External Resources:
- - Talks: eso-talks.md
- - Demos: eso-demos.md
- - Blogs: eso-blogs.md
- - AWS:
- - Secrets Manager: provider-aws-secrets-manager.md
- - Parameter Store: provider-aws-parameter-store.md
- - Azure:
- - Key Vault: provider-azure-key-vault.md
- - Google:
- - Secrets Manager: provider-google-secrets-manager.md
- - IBM:
- - Secrets Manager: provider-ibm-secrets-manager.md
- - HashiCorp Vault: provider-hashicorp-vault.md
- - Yandex:
- - Lockbox: provider-yandex-lockbox.md
- - Password Depot: provider-passworddepot.md
- - Gitlab:
- - Gitlab Project Variables: provider-gitlab-project-variables.md
- - Oracle:
- - Oracle Vault: provider-oracle-vault.md
- - References:
- - API specification: spec.md
- - Contributing:
- - Developer guide: contributing-devguide.md
- - Contributing Process: contributing-process.md
- - Code of Conduct: contributing-coc.md
- - Deprecation Policy: deprecation-policy.md
+ - Contributing:
+ - Developer guide: contributing/devguide.md
+ - Contributing Process: contributing/process.md
+ - Release Process: contributing/release.md
+ - Code of Conduct: contributing/coc.md
+ - Roadmap: contributing/roadmap.md
+ - External Resources:
+ - Talks: eso-talks.md
+ - Demos: eso-demos.md
+ - Blogs: eso-blogs.md
+ - Tools: eso-tools.md
diff --git a/hack/api-docs/requirements.txt b/hack/api-docs/requirements.txt
index 5e222b4f868..d04f3ac4ef4 100644
--- a/hack/api-docs/requirements.txt
+++ b/hack/api-docs/requirements.txt
@@ -1,42 +1,42 @@
-Babel==2.14.0
-certifi==2024.2.2
-charset-normalizer==3.3.2
-click==8.1.7
+Babel==2.16.0
+certifi==2024.12.14
+charset-normalizer==3.4.1
+click==8.1.8
colorama==0.4.6
csscompressor==0.9.5
ghp-import==2.1.0
htmlmin==0.1.12
-idna==3.7
-importlib-metadata==7.1.0
-importlib-resources==6.4.0
-Jinja2==3.1.3
+idna==3.10
+importlib-metadata==8.6.1
+importlib-resources==6.5.2
+Jinja2==3.1.5
jsmin==3.0.1
-livereload==2.6.3
-Markdown==3.6
-MarkupSafe==2.1.5
+livereload==2.7.1
+Markdown==3.7
+MarkupSafe==3.0.2
mergedeep==1.3.4
mike @ git+https://github.com/jimporter/mike@300593c338b18f61f604d18457c351e166318020
-mkdocs==1.6.0
-mkdocs-macros-plugin==1.0.5
-mkdocs-material==9.5.19
+mkdocs==1.6.1
+mkdocs-macros-plugin==1.3.7
+mkdocs-material==9.5.50
mkdocs-material-extensions==1.3.1
mkdocs-minify-plugin==0.8.0
-packaging==24.0
-paginate==0.5.6
+packaging==24.2
+paginate==0.5.7
pathspec==0.12.1
pep562==1.1
-platformdirs==4.2.0
-Pygments==2.17.2
-pymdown-extensions==10.8
+platformdirs==4.3.6
+Pygments==2.19.1
+pymdown-extensions==10.14.1
python-dateutil==2.9.0.post0
-PyYAML==6.0.1
+PyYAML==6.0.2
pyyaml_env_tag==0.1
-regex==2024.4.16
-requests==2.31.0
-six==1.16.0
-termcolor==2.4.0
-tornado==6.4
-urllib3==2.2.1
+regex==2024.11.6
+requests==2.32.3
+six==1.17.0
+termcolor==2.5.0
+tornado==6.4.2
+urllib3==2.3.0
verspec==0.1.0
-watchdog==4.0.0
-zipp==3.18.1
+watchdog==6.0.0
+zipp==3.21.0
diff --git a/hack/crd.generate.sh b/hack/crd.generate.sh
index abce2d018b9..46cecc19d95 100755
--- a/hack/crd.generate.sh
+++ b/hack/crd.generate.sh
@@ -15,6 +15,11 @@ go run sigs.k8s.io/controller-tools/cmd/controller-gen crd \
paths="./apis/..." \
output:crd:artifacts:config="${CRD_DIR}/bases"
+## Update resources list from kustomization.yaml
+ls "${CRD_DIR}"/bases | grep -v "kustomization.yaml" | jq -R -s -c 'split("\n")[:-1]' | yq -p=json - > kustomize-files.yaml
+yq -i '.resources = (load("kustomize-files.yaml"))' "${CRD_DIR}"/bases/kustomization.yaml
+rm kustomize-files.yaml
+
# Remove extra header lines in generated CRDs
# This is needed for building the helm chart
for f in "${CRD_DIR}"/bases/*.yaml; do
diff --git a/main.go b/main.go
index cf110eecedb..f010608cb66 100644
--- a/main.go
+++ b/main.go
@@ -16,8 +16,10 @@ limitations under the License.
package main
-import "github.com/external-secrets/external-secrets/cmd"
+import (
+ "github.com/external-secrets/external-secrets/cmd/controller"
+)
func main() {
- cmd.Execute()
+ controller.Execute()
}
diff --git a/overrides/main.html b/overrides/main.html
index 35591d61c3c..b8bea4f732a 100644
--- a/overrides/main.html
+++ b/overrides/main.html
@@ -1,8 +1,12 @@
{% extends "base.html" %}
-
{% block outdated %}
You're not viewing the latest version.
Click here to go to latest.
-{% endblock %}
\ No newline at end of file
+{% endblock %}
+
+{% block footer %}
+
+ {{ super() }}
+{% endblock %}
diff --git a/pkg/common/webhook/models.go b/pkg/common/webhook/models.go
index 1101f8aa11e..3151cbec63f 100644
--- a/pkg/common/webhook/models.go
+++ b/pkg/common/webhook/models.go
@@ -16,6 +16,8 @@ package webhook
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
)
type Spec struct {
@@ -55,31 +57,7 @@ type Spec struct {
// The provider for the CA bundle to use to validate webhook server certificate.
// +optional
- CAProvider *CAProvider `json:"caProvider,omitempty"`
-}
-type CAProviderType string
-
-const (
- CAProviderTypeSecret CAProviderType = "Secret"
- CAProviderTypeConfigMap CAProviderType = "ConfigMap"
-)
-
-// Defines a location to fetch the cert for the webhook provider from.
-type CAProvider struct {
- // The type of provider to use such as "Secret", or "ConfigMap".
- // +kubebuilder:validation:Enum="Secret";"ConfigMap"
- Type CAProviderType `json:"type"`
-
- // The name of the object located at the provider type.
- Name string `json:"name"`
-
- // The key the value inside of the provider type to use, only used with "Secret" type
- // +kubebuilder:validation:Optional
- Key string `json:"key,omitempty"`
-
- // The namespace the Provider type is in.
- // +optional
- Namespace *string `json:"namespace,omitempty"`
+ CAProvider *esv1beta1.CAProvider `json:"caProvider,omitempty"`
}
type Result struct {
diff --git a/pkg/common/webhook/webhook.go b/pkg/common/webhook/webhook.go
index 34989bc8933..f540d31ec5c 100644
--- a/pkg/common/webhook/webhook.go
+++ b/pkg/common/webhook/webhook.go
@@ -20,6 +20,7 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/json"
+ "errors"
"fmt"
"io"
"net/http"
@@ -35,6 +36,7 @@ import (
"github.com/external-secrets/external-secrets/pkg/constants"
"github.com/external-secrets/external-secrets/pkg/metrics"
"github.com/external-secrets/external-secrets/pkg/template/v2"
+ "github.com/external-secrets/external-secrets/pkg/utils"
"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
)
@@ -63,12 +65,12 @@ func (w *Webhook) getStoreSecret(ctx context.Context, ref SecretKeySelector) (*c
return nil, fmt.Errorf("failed to get clustersecretstore webhook secret %s: %w", ref.Name, err)
}
if w.EnforceLabels {
- expected, ok := secret.Labels["generators.external-secrets.io/type"]
+ expected, ok := secret.Labels["external-secrets.io/type"]
if !ok {
- return nil, fmt.Errorf("secret does not contain needed label to be used on webhook generator")
+ return nil, errors.New("secret does not contain needed label 'external-secrets.io/type: webhook'. Update secret label to use it with webhook")
}
if expected != "webhook" {
- return nil, fmt.Errorf("secret type is not 'webhook'")
+ return nil, errors.New("secret type is not 'webhook'")
}
}
return secret, nil
@@ -106,23 +108,30 @@ func (w *Webhook) GetSecretMap(ctx context.Context, provider *Spec, ref *esv1bet
}
// Change the map of generic objects to a map of byte arrays
values := make(map[string][]byte)
- for rKey, rValue := range jsonvalue {
- jVal, ok := rValue.(string)
- if !ok {
- return nil, fmt.Errorf("failed to get response (wrong type in key '%s': %T)", rKey, rValue)
+ for rKey := range jsonvalue {
+ values[rKey], err = utils.GetByteValueFromMap(jsonvalue, rKey)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get response for key '%s': %w", rKey, err)
}
- values[rKey] = []byte(jVal)
}
return values, nil
}
-func (w *Webhook) GetTemplateData(ctx context.Context, ref *esv1beta1.ExternalSecretDataRemoteRef, secrets []Secret) (map[string]map[string]string, error) {
+func (w *Webhook) GetTemplateData(ctx context.Context, ref *esv1beta1.ExternalSecretDataRemoteRef, secrets []Secret, urlEncode bool) (map[string]map[string]string, error) {
data := map[string]map[string]string{}
if ref != nil {
- data["remoteRef"] = map[string]string{
- "key": url.QueryEscape(ref.Key),
- "version": url.QueryEscape(ref.Version),
- "property": url.QueryEscape(ref.Property),
+ if urlEncode {
+ data["remoteRef"] = map[string]string{
+ "key": url.QueryEscape(ref.Key),
+ "version": url.QueryEscape(ref.Version),
+ "property": url.QueryEscape(ref.Property),
+ }
+ } else {
+ data["remoteRef"] = map[string]string{
+ "key": ref.Key,
+ "version": ref.Version,
+ "property": ref.Property,
+ }
}
}
for _, secref := range secrets {
@@ -142,21 +151,27 @@ func (w *Webhook) GetTemplateData(ctx context.Context, ref *esv1beta1.ExternalSe
func (w *Webhook) GetWebhookData(ctx context.Context, provider *Spec, ref *esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
if w.HTTP == nil {
- return nil, fmt.Errorf("http client not initialized")
+ return nil, errors.New("http client not initialized")
}
- data, err := w.GetTemplateData(ctx, ref, provider.Secrets)
+
+ escapedData, err := w.GetTemplateData(ctx, ref, provider.Secrets, true)
if err != nil {
return nil, err
}
+ rawData, err := w.GetTemplateData(ctx, ref, provider.Secrets, false)
+ if err != nil {
+ return nil, err
+ }
+
method := provider.Method
if method == "" {
method = http.MethodGet
}
- url, err := ExecuteTemplateString(provider.URL, data)
+ url, err := ExecuteTemplateString(provider.URL, escapedData)
if err != nil {
return nil, fmt.Errorf("failed to parse url: %w", err)
}
- body, err := ExecuteTemplate(provider.Body, data)
+ body, err := ExecuteTemplate(provider.Body, rawData)
if err != nil {
return nil, fmt.Errorf("failed to parse body: %w", err)
}
@@ -166,7 +181,7 @@ func (w *Webhook) GetWebhookData(ctx context.Context, provider *Spec, ref *esv1b
return nil, fmt.Errorf("failed to create request: %w", err)
}
for hKey, hValueTpl := range provider.Headers {
- hValue, err := ExecuteTemplateString(hValueTpl, data)
+ hValue, err := ExecuteTemplateString(hValueTpl, rawData)
if err != nil {
return nil, fmt.Errorf("failed to parse header %s: %w", hKey, err)
}
@@ -182,13 +197,18 @@ func (w *Webhook) GetWebhookData(ctx context.Context, provider *Spec, ref *esv1b
if resp.StatusCode == 404 {
return nil, esv1beta1.NoSecretError{}
}
+
+ if resp.StatusCode == http.StatusNotModified {
+ return nil, esv1beta1.NotModifiedError{}
+ }
+
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
return nil, fmt.Errorf("endpoint gave error %s", resp.Status)
}
return io.ReadAll(resp.Body)
}
-func (w *Webhook) GetHTTPClient(provider *Spec) (*http.Client, error) {
+func (w *Webhook) GetHTTPClient(ctx context.Context, provider *Spec) (*http.Client, error) {
client := &http.Client{}
if provider.Timeout != nil {
client.Timeout = provider.Timeout.Duration
@@ -197,7 +217,7 @@ func (w *Webhook) GetHTTPClient(provider *Spec) (*http.Client, error) {
// No need to process ca stuff if it is not there
return client, nil
}
- caCertPool, err := w.GetCACertPool(provider)
+ caCertPool, err := w.GetCACertPool(ctx, provider)
if err != nil {
return nil, err
}
@@ -211,37 +231,23 @@ func (w *Webhook) GetHTTPClient(provider *Spec) (*http.Client, error) {
return client, nil
}
-func (w *Webhook) GetCACertPool(provider *Spec) (*x509.CertPool, error) {
+func (w *Webhook) GetCACertPool(ctx context.Context, provider *Spec) (*x509.CertPool, error) {
caCertPool := x509.NewCertPool()
- if len(provider.CABundle) > 0 {
- ok := caCertPool.AppendCertsFromPEM(provider.CABundle)
- if !ok {
- return nil, fmt.Errorf("failed to append cabundle")
- }
+ ca, err := utils.FetchCACertFromSource(ctx, utils.CreateCertOpts{
+ CABundle: provider.CABundle,
+ CAProvider: provider.CAProvider,
+ StoreKind: w.StoreKind,
+ Namespace: w.Namespace,
+ Client: w.Kube,
+ })
+ if err != nil {
+ return nil, err
}
-
- if provider.CAProvider != nil {
- var cert []byte
- var err error
-
- switch provider.CAProvider.Type {
- case CAProviderTypeSecret:
- cert, err = w.GetCertFromSecret(provider)
- case CAProviderTypeConfigMap:
- cert, err = w.GetCertFromConfigMap(provider)
- default:
- err = fmt.Errorf("unknown caprovider type: %s", provider.CAProvider.Type)
- }
-
- if err != nil {
- return nil, err
- }
-
- ok := caCertPool.AppendCertsFromPEM(cert)
- if !ok {
- return nil, fmt.Errorf("failed to append cabundle")
- }
+ ok := caCertPool.AppendCertsFromPEM(ca)
+ if !ok {
+ return nil, errors.New("failed to append cabundle")
}
+
return caCertPool, nil
}
diff --git a/pkg/constants/constants.go b/pkg/constants/constants.go
index 56b50d4a97f..00e38ef96c9 100644
--- a/pkg/constants/constants.go
+++ b/pkg/constants/constants.go
@@ -23,6 +23,7 @@ const (
CallAWSSMCreateSecret = "CreateSecret"
CallAWSSMPutSecretValue = "PutSecretValue"
CallAWSSMListSecrets = "ListSecrets"
+ CallAWSSMBatchGetSecretValue = "BatchGetSecretValue"
ProviderAWSPS = "AWS/ParameterStore"
CallAWSPSGetParameter = "GetParameter"
@@ -94,7 +95,14 @@ const (
CallAKEYLESSSMGetRotatedSecretValue = "GetRotatedSecretValue"
CallAKEYLESSSMGetCertificateValue = "GetCertificateValue"
CallAKEYLESSSMGetDynamicSecretValue = "GetDynamicSecretsValue"
+ CallAKEYLESSSMCreateSecret = "CreateSecret"
+ CallAKEYLESSSMUpdateSecretVal = "UpdateSecretVal"
+ CallAKEYLESSSMDeleteItem = "DeleteItem"
StatusError = "error"
StatusSuccess = "success"
+
+ WellKnownLabelKey = "external-secrets.io/component"
+ WellKnownLabelValueController = "controller"
+ WellKnownLabelValueWebhook = "webhook"
)
diff --git a/pkg/controllers/clusterexternalsecret/clusterexternalsecret_controller.go b/pkg/controllers/clusterexternalsecret/clusterexternalsecret_controller.go
index 13c1cf0f4d2..2d5fec46d64 100644
--- a/pkg/controllers/clusterexternalsecret/clusterexternalsecret_controller.go
+++ b/pkg/controllers/clusterexternalsecret/clusterexternalsecret_controller.go
@@ -16,6 +16,7 @@ package clusterexternalsecret
import (
"context"
+ "errors"
"fmt"
"reflect"
"slices"
@@ -44,7 +45,7 @@ import (
ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
)
-// ClusterExternalSecretReconciler reconciles a ClusterExternalSecret object.
+// Reconciler reconciles a ClusterExternalSecret object.
type Reconciler struct {
client.Client
Log logr.Logger
@@ -90,6 +91,10 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
p := client.MergeFrom(clusterExternalSecret.DeepCopy())
defer r.deferPatch(ctx, log, &clusterExternalSecret, p)
+ return r.reconcile(ctx, log, &clusterExternalSecret)
+}
+
+func (r *Reconciler) reconcile(ctx context.Context, log logr.Logger, clusterExternalSecret *esv1beta1.ClusterExternalSecret) (ctrl.Result, error) {
refreshInt := r.RequeueInterval
if clusterExternalSecret.Spec.RefreshInterval != nil {
refreshInt = clusterExternalSecret.Spec.RefreshInterval.Duration
@@ -101,27 +106,52 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
}
if prevName := clusterExternalSecret.Status.ExternalSecretName; prevName != esName {
// ExternalSecretName has changed, so remove the old ones
- for _, ns := range clusterExternalSecret.Status.ProvisionedNamespaces {
- if err := r.deleteExternalSecret(ctx, prevName, clusterExternalSecret.Name, ns); err != nil {
- log.Error(err, "could not delete ExternalSecret")
- return ctrl.Result{}, err
- }
+ if err := r.removeOldSecrets(ctx, log, clusterExternalSecret, prevName); err != nil {
+ return ctrl.Result{}, err
}
}
clusterExternalSecret.Status.ExternalSecretName = esName
- namespaces, err := r.getTargetNamespaces(ctx, &clusterExternalSecret)
+ namespaces, err := r.getTargetNamespaces(ctx, clusterExternalSecret)
if err != nil {
log.Error(err, "failed to get target Namespaces")
+ failedNamespaces := map[string]error{
+ "unknown": err,
+ }
+ condition := NewClusterExternalSecretCondition(failedNamespaces)
+ SetClusterExternalSecretCondition(clusterExternalSecret, *condition)
+
+ clusterExternalSecret.Status.FailedNamespaces = toNamespaceFailures(failedNamespaces)
+
return ctrl.Result{}, err
}
failedNamespaces := r.deleteOutdatedExternalSecrets(ctx, namespaces, esName, clusterExternalSecret.Name, clusterExternalSecret.Status.ProvisionedNamespaces)
- provisionedNamespaces := []string{}
+ provisionedNamespaces := r.gatherProvisionedNamespaces(ctx, log, clusterExternalSecret, namespaces, esName, failedNamespaces)
+
+ condition := NewClusterExternalSecretCondition(failedNamespaces)
+ SetClusterExternalSecretCondition(clusterExternalSecret, *condition)
+
+ clusterExternalSecret.Status.FailedNamespaces = toNamespaceFailures(failedNamespaces)
+ sort.Strings(provisionedNamespaces)
+ clusterExternalSecret.Status.ProvisionedNamespaces = provisionedNamespaces
+
+ return ctrl.Result{RequeueAfter: refreshInt}, nil
+}
+
+func (r *Reconciler) gatherProvisionedNamespaces(
+ ctx context.Context,
+ log logr.Logger,
+ clusterExternalSecret *esv1beta1.ClusterExternalSecret,
+ namespaces []v1.Namespace,
+ esName string,
+ failedNamespaces map[string]error,
+) []string {
+ var provisionedNamespaces []string //nolint:prealloc // we don't know the size
for _, namespace := range namespaces {
var existingES esv1beta1.ExternalSecret
- err = r.Get(ctx, types.NamespacedName{
+ err := r.Get(ctx, types.NamespacedName{
Name: esName,
Namespace: namespace.Name,
}, &existingES)
@@ -132,11 +162,11 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
}
if err == nil && !isExternalSecretOwnedBy(&existingES, clusterExternalSecret.Name) {
- failedNamespaces[namespace.Name] = fmt.Errorf("external secret already exists in namespace")
+ failedNamespaces[namespace.Name] = errors.New("external secret already exists in namespace")
continue
}
- if err := r.createOrUpdateExternalSecret(ctx, &clusterExternalSecret, namespace, esName, clusterExternalSecret.Spec.ExternalSecretMetadata); err != nil {
+ if err := r.createOrUpdateExternalSecret(ctx, clusterExternalSecret, namespace, esName, clusterExternalSecret.Spec.ExternalSecretMetadata); err != nil {
log.Error(err, "failed to create or update external secret")
failedNamespaces[namespace.Name] = err
continue
@@ -144,19 +174,33 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
provisionedNamespaces = append(provisionedNamespaces, namespace.Name)
}
+ return provisionedNamespaces
+}
- condition := NewClusterExternalSecretCondition(failedNamespaces)
- SetClusterExternalSecretCondition(&clusterExternalSecret, *condition)
-
- clusterExternalSecret.Status.FailedNamespaces = toNamespaceFailures(failedNamespaces)
- sort.Strings(provisionedNamespaces)
- clusterExternalSecret.Status.ProvisionedNamespaces = provisionedNamespaces
+func (r *Reconciler) removeOldSecrets(ctx context.Context, log logr.Logger, clusterExternalSecret *esv1beta1.ClusterExternalSecret, prevName string) error {
+ var (
+ failedNamespaces = map[string]error{}
+ lastErr error
+ )
+ for _, ns := range clusterExternalSecret.Status.ProvisionedNamespaces {
+ if err := r.deleteExternalSecret(ctx, prevName, clusterExternalSecret.Name, ns); err != nil {
+ log.Error(err, "could not delete ExternalSecret")
+ failedNamespaces[ns] = err
+ lastErr = err
+ }
+ }
+ if len(failedNamespaces) > 0 {
+ condition := NewClusterExternalSecretCondition(failedNamespaces)
+ SetClusterExternalSecretCondition(clusterExternalSecret, *condition)
+ clusterExternalSecret.Status.FailedNamespaces = toNamespaceFailures(failedNamespaces)
+ return lastErr
+ }
- return ctrl.Result{RequeueAfter: refreshInt}, nil
+ return nil
}
func (r *Reconciler) getTargetNamespaces(ctx context.Context, ces *esv1beta1.ClusterExternalSecret) ([]v1.Namespace, error) {
- selectors := []*metav1.LabelSelector{}
+ var selectors []*metav1.LabelSelector //nolint:prealloc // ces.Spec.NamespaceSelector might be empty.
if s := ces.Spec.NamespaceSelector; s != nil {
selectors = append(selectors, s)
}
@@ -325,9 +369,13 @@ func (r *Reconciler) findObjectsForNamespace(ctx context.Context, namespace clie
return []reconcile.Request{}
}
+ return r.queueRequestsForItem(&clusterExternalSecrets, namespace)
+}
+
+func (r *Reconciler) queueRequestsForItem(clusterExternalSecrets *esv1beta1.ClusterExternalSecretList, namespace client.Object) []reconcile.Request {
var requests []reconcile.Request
for i := range clusterExternalSecrets.Items {
- clusterExternalSecret := &clusterExternalSecrets.Items[i]
+ clusterExternalSecret := clusterExternalSecrets.Items[i]
var selectors []*metav1.LabelSelector
if s := clusterExternalSecret.Spec.NamespaceSelector; s != nil {
selectors = append(selectors, s)
diff --git a/pkg/controllers/clusterexternalsecret/clusterexternalsecret_controller_test.go b/pkg/controllers/clusterexternalsecret/clusterexternalsecret_controller_test.go
index c7835cbf079..e631237e87d 100644
--- a/pkg/controllers/clusterexternalsecret/clusterexternalsecret_controller_test.go
+++ b/pkg/controllers/clusterexternalsecret/clusterexternalsecret_controller_test.go
@@ -39,6 +39,17 @@ func init() {
cesmetrics.SetUpMetrics()
}
+const (
+ metadataLabelName = "kubernetes.io/metadata.name"
+ testLabelKey = "test-label-key"
+ testAnnotationKey = "test-annotation-key"
+ testLabelValue = "test-label-value"
+ testAnnotationValue = "test-annotation-value"
+ updatedTestStore = "updated-test-store"
+ noLongerMatchLabelKey = "no-longer-match-label-key"
+ noLongerMatchLabelValue = "no-longer-match-label-value"
+)
+
var (
timeout = time.Second * 10
interval = time.Millisecond * 250
@@ -158,7 +169,7 @@ var _ = Describe("ClusterExternalSecret controller", func() {
clusterExternalSecret: func(namespaces []v1.Namespace) esv1beta1.ClusterExternalSecret {
ces := defaultClusterExternalSecret()
ces.Spec.NamespaceSelector = &metav1.LabelSelector{
- MatchLabels: map[string]string{"kubernetes.io/metadata.name": namespaces[0].Name},
+ MatchLabels: map[string]string{metadataLabelName: namespaces[0].Name},
}
return *ces
},
@@ -199,7 +210,7 @@ var _ = Describe("ClusterExternalSecret controller", func() {
clusterExternalSecret: func(namespaces []v1.Namespace) esv1beta1.ClusterExternalSecret {
ces := defaultClusterExternalSecret()
ces.Spec.NamespaceSelector = &metav1.LabelSelector{
- MatchLabels: map[string]string{"kubernetes.io/metadata.name": namespaces[0].Name},
+ MatchLabels: map[string]string{metadataLabelName: namespaces[0].Name},
}
ces.Spec.ExternalSecretName = "test-es"
ces.Spec.ExternalSecretMetadata = esv1beta1.ExternalSecretMetadata{
@@ -247,7 +258,7 @@ var _ = Describe("ClusterExternalSecret controller", func() {
clusterExternalSecret: func(namespaces []v1.Namespace) esv1beta1.ClusterExternalSecret {
ces := defaultClusterExternalSecret()
ces.Spec.NamespaceSelector = &metav1.LabelSelector{
- MatchLabels: map[string]string{"kubernetes.io/metadata.name": namespaces[0].Name},
+ MatchLabels: map[string]string{metadataLabelName: namespaces[0].Name},
}
ces.Spec.ExternalSecretName = "old-es-name"
return *ces
@@ -304,7 +315,7 @@ var _ = Describe("ClusterExternalSecret controller", func() {
clusterExternalSecret: func(namespaces []v1.Namespace) esv1beta1.ClusterExternalSecret {
ces := defaultClusterExternalSecret()
ces.Spec.NamespaceSelector = &metav1.LabelSelector{
- MatchLabels: map[string]string{"kubernetes.io/metadata.name": namespaces[0].Name},
+ MatchLabels: map[string]string{metadataLabelName: namespaces[0].Name},
}
return *ces
},
@@ -321,19 +332,19 @@ var _ = Describe("ClusterExternalSecret controller", func() {
copied := created.DeepCopy()
copied.Spec.ExternalSecretMetadata = esv1beta1.ExternalSecretMetadata{
- Labels: map[string]string{"test-label-key": "test-label-value"},
- Annotations: map[string]string{"test-annotation-key": "test-annotation-value"},
+ Labels: map[string]string{testLabelKey: testLabelValue},
+ Annotations: map[string]string{testAnnotationKey: testAnnotationValue},
}
- copied.Spec.ExternalSecretSpec.SecretStoreRef.Name = "updated-test-store" //nolint:goconst
+ copied.Spec.ExternalSecretSpec.SecretStoreRef.Name = updatedTestStore
Expect(k8sClient.Patch(ctx, copied, crclient.MergeFrom(created.DeepCopy()))).ShouldNot(HaveOccurred())
},
expectedClusterExternalSecret: func(namespaces []v1.Namespace, created esv1beta1.ClusterExternalSecret) esv1beta1.ClusterExternalSecret {
updatedSpec := created.Spec.DeepCopy()
updatedSpec.ExternalSecretMetadata = esv1beta1.ExternalSecretMetadata{
- Labels: map[string]string{"test-label-key": "test-label-value"},
- Annotations: map[string]string{"test-annotation-key": "test-annotation-value"},
+ Labels: map[string]string{testLabelKey: testLabelValue},
+ Annotations: map[string]string{testAnnotationKey: testAnnotationValue},
}
- updatedSpec.ExternalSecretSpec.SecretStoreRef.Name = "updated-test-store"
+ updatedSpec.ExternalSecretSpec.SecretStoreRef.Name = updatedTestStore
return esv1beta1.ClusterExternalSecret{
ObjectMeta: metav1.ObjectMeta{
@@ -354,15 +365,15 @@ var _ = Describe("ClusterExternalSecret controller", func() {
},
expectedExternalSecrets: func(namespaces []v1.Namespace, created esv1beta1.ClusterExternalSecret) []esv1beta1.ExternalSecret {
updatedSpec := created.Spec.ExternalSecretSpec.DeepCopy()
- updatedSpec.SecretStoreRef.Name = "updated-test-store"
+ updatedSpec.SecretStoreRef.Name = updatedTestStore
return []esv1beta1.ExternalSecret{
{
ObjectMeta: metav1.ObjectMeta{
Namespace: namespaces[0].Name,
Name: created.Name,
- Labels: map[string]string{"test-label-key": "test-label-value"},
- Annotations: map[string]string{"test-annotation-key": "test-annotation-value"},
+ Labels: map[string]string{testLabelKey: testLabelValue},
+ Annotations: map[string]string{testAnnotationKey: testAnnotationValue},
},
Spec: *updatedSpec,
},
@@ -376,7 +387,7 @@ var _ = Describe("ClusterExternalSecret controller", func() {
clusterExternalSecret: func(namespaces []v1.Namespace) esv1beta1.ClusterExternalSecret {
ces := defaultClusterExternalSecret()
ces.Spec.NamespaceSelector = &metav1.LabelSelector{
- MatchLabels: map[string]string{"kubernetes.io/metadata.name": namespaces[0].Name},
+ MatchLabels: map[string]string{metadataLabelName: namespaces[0].Name},
}
es := &esv1beta1.ExternalSecret{
@@ -438,7 +449,7 @@ var _ = Describe("ClusterExternalSecret controller", func() {
clusterExternalSecret: func(namespaces []v1.Namespace) esv1beta1.ClusterExternalSecret {
ces := defaultClusterExternalSecret()
ces.Spec.NamespaceSelector = &metav1.LabelSelector{
- MatchLabels: map[string]string{"kubernetes.io/metadata.name": namespaces[0].Name},
+ MatchLabels: map[string]string{metadataLabelName: namespaces[0].Name},
}
es := &esv1beta1.ExternalSecret{
@@ -501,13 +512,13 @@ var _ = Describe("ClusterExternalSecret controller", func() {
{
ObjectMeta: metav1.ObjectMeta{
Name: randomNamespaceName(),
- Labels: map[string]string{"no-longer-match-label-key": "no-longer-match-label-value"},
+ Labels: map[string]string{noLongerMatchLabelKey: noLongerMatchLabelValue},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: randomNamespaceName(),
- Labels: map[string]string{"no-longer-match-label-key": "no-longer-match-label-value"},
+ Labels: map[string]string{noLongerMatchLabelKey: noLongerMatchLabelValue},
},
},
},
@@ -515,7 +526,7 @@ var _ = Describe("ClusterExternalSecret controller", func() {
ces := defaultClusterExternalSecret()
ces.Spec.RefreshInterval = &metav1.Duration{Duration: 100 * time.Millisecond}
ces.Spec.NamespaceSelector = &metav1.LabelSelector{
- MatchLabels: map[string]string{"no-longer-match-label-key": "no-longer-match-label-value"},
+ MatchLabels: map[string]string{noLongerMatchLabelKey: noLongerMatchLabelValue},
}
return *ces
},
@@ -646,7 +657,7 @@ var _ = Describe("ClusterExternalSecret controller", func() {
clusterExternalSecret: func(namespaces []v1.Namespace) esv1beta1.ClusterExternalSecret {
ces := defaultClusterExternalSecret()
ces.Spec.NamespaceSelector = &metav1.LabelSelector{
- MatchLabels: map[string]string{"kubernetes.io/metadata.name": "no-namespace-matches"},
+ MatchLabels: map[string]string{metadataLabelName: "no-namespace-matches"},
}
return *ces
},
diff --git a/pkg/controllers/clusterexternalsecret/suite_test.go b/pkg/controllers/clusterexternalsecret/suite_test.go
index b2e3dde8a2b..37edfdf5e05 100644
--- a/pkg/controllers/clusterexternalsecret/suite_test.go
+++ b/pkg/controllers/clusterexternalsecret/suite_test.go
@@ -31,6 +31,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/metrics/server"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ ctrlcommon "github.com/external-secrets/external-secrets/pkg/controllers/common"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@@ -90,6 +91,7 @@ var _ = BeforeSuite(func() {
RequeueInterval: time.Second,
}).SetupWithManager(k8sManager, controller.Options{
MaxConcurrentReconciles: 1,
+ RateLimiter: ctrlcommon.BuildRateLimiter(),
})
Expect(err).ToNot(HaveOccurred())
diff --git a/pkg/controllers/common/common.go b/pkg/controllers/common/common.go
new file mode 100644
index 00000000000..bb5fc3edf4e
--- /dev/null
+++ b/pkg/controllers/common/common.go
@@ -0,0 +1,112 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package common
+
+import (
+ "context"
+ "time"
+
+ "golang.org/x/time/rate"
+ corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/labels"
+ "k8s.io/apimachinery/pkg/selection"
+ "k8s.io/client-go/util/workqueue"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/cache"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/reconcile"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+)
+
+// BuildManagedSecretClient creates a new client that only sees secrets with the "managed" label.
+func BuildManagedSecretClient(mgr ctrl.Manager) (client.Client, error) {
+ // secrets we manage will have the `reconcile.external-secrets.io/managed=true` label
+ managedLabelReq, _ := labels.NewRequirement(esv1beta1.LabelManaged, selection.Equals, []string{esv1beta1.LabelManagedValue})
+ managedLabelSelector := labels.NewSelector().Add(*managedLabelReq)
+
+ // create a new cache with a label selector for managed secrets
+ // NOTE: this means that the cache/client will be unable to see secrets without the "managed" label
+ secretCacheOpts := cache.Options{
+ HTTPClient: mgr.GetHTTPClient(),
+ Scheme: mgr.GetScheme(),
+ Mapper: mgr.GetRESTMapper(),
+ ByObject: map[client.Object]cache.ByObject{
+ &corev1.Secret{}: {
+ Label: managedLabelSelector,
+ },
+ },
+ // this requires us to explicitly start an informer for each object type
+ // and helps avoid people mistakenly using the secret client for other resources
+ ReaderFailOnMissingInformer: true,
+ }
+ secretCache, err := cache.New(mgr.GetConfig(), secretCacheOpts)
+ if err != nil {
+ return nil, err
+ }
+
+ // start an informer for secrets
+ // this is required because we set ReaderFailOnMissingInformer to true
+ _, err = secretCache.GetInformer(context.Background(), &corev1.Secret{})
+ if err != nil {
+ return nil, err
+ }
+
+ // add the secret cache to the manager, so that it starts at the same time
+ err = mgr.Add(secretCache)
+ if err != nil {
+ return nil, err
+ }
+
+ // create a new client that uses the secret cache
+ secretClient, err := client.New(mgr.GetConfig(), client.Options{
+ HTTPClient: mgr.GetHTTPClient(),
+ Scheme: mgr.GetScheme(),
+ Mapper: mgr.GetRESTMapper(),
+ Cache: &client.CacheOptions{
+ Reader: secretCache,
+ },
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return secretClient, nil
+}
+
+// BuildRateLimiter creates a new rate limiter for our controllers.
+// NOTE: we dont use `DefaultTypedControllerRateLimiter` because it retries very aggressively, starting at 5ms!
+func BuildRateLimiter() workqueue.TypedRateLimiter[reconcile.Request] {
+ // exponential backoff rate limiter
+ // - this handles per-item rate limiting for ~failures~
+ // - it uses an exponential backoff strategy were: delay = baseDelay * 2^failures
+ // - graph visualization: https://www.desmos.com/calculator/fexlpdmiti
+ failureBaseDelay := 1 * time.Second
+ failureMaxDelay := 7 * time.Minute
+ failureRateLimiter := workqueue.NewTypedItemExponentialFailureRateLimiter[reconcile.Request](failureBaseDelay, failureMaxDelay)
+
+ // overall rate limiter
+ // - this handles overall rate limiting, ignoring individual items and only considering the overall rate
+ // - it implements a "token bucket" of size totalMaxBurst that is initially full,
+ // and which is refilled at rate totalEventsPerSecond tokens per second.
+ totalEventsPerSecond := 10
+ totalMaxBurst := 100
+ totalRateLimiter := &workqueue.TypedBucketRateLimiter[reconcile.Request]{
+ Limiter: rate.NewLimiter(rate.Limit(totalEventsPerSecond), totalMaxBurst),
+ }
+
+ // return the worst-case (longest) of the rate limiters for a given item
+ return workqueue.NewTypedMaxOfRateLimiter[reconcile.Request](failureRateLimiter, totalRateLimiter)
+}
diff --git a/pkg/controllers/commontest/common.go b/pkg/controllers/commontest/common.go
index 72aa47bc980..b1332deb830 100644
--- a/pkg/controllers/commontest/common.go
+++ b/pkg/controllers/commontest/common.go
@@ -19,7 +19,6 @@ import (
"fmt"
"time"
- "github.com/google/go-cmp/cmp"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/wait"
@@ -62,14 +61,12 @@ func HasOwnerRef(meta metav1.ObjectMeta, kind, name string) bool {
return false
}
-func HasFieldOwnership(meta metav1.ObjectMeta, mgr, expected string) string {
+// FirstManagedFieldForManager returns the JSON representation of the first `metadata.managedFields` entry for a given manager.
+func FirstManagedFieldForManager(meta metav1.ObjectMeta, managerName string) string {
for _, ref := range meta.ManagedFields {
- if ref.Manager == mgr {
- if diff := cmp.Diff(string(ref.FieldsV1.Raw), expected); diff != "" {
- return fmt.Sprintf("(-got, +want)\n%s", diff)
- }
- return ""
+ if ref.Manager == managerName {
+ return ref.FieldsV1.String()
}
}
- return fmt.Sprintf("No managed fields managed by %s", mgr)
+ return fmt.Sprintf("No managed fields managed by %s", managerName)
}
diff --git a/pkg/controllers/crds/crds_controller.go b/pkg/controllers/crds/crds_controller.go
index ae1eca177c8..1f77ade758f 100644
--- a/pkg/controllers/crds/crds_controller.go
+++ b/pkg/controllers/crds/crds_controller.go
@@ -29,6 +29,7 @@ import (
"net/http"
"os"
"path/filepath"
+ "slices"
"sync"
"time"
@@ -80,18 +81,26 @@ type Reconciler struct {
readyStatusMap map[string]bool
}
+type Opts struct {
+ SvcName string
+ SvcNamespace string
+ SecretName string
+ SecretNamespace string
+ Resources []string
+}
+
func New(k8sClient client.Client, scheme *runtime.Scheme, leaderChan <-chan struct{}, logger logr.Logger,
- interval time.Duration, svcName, svcNamespace, secretName, secretNamespace string, resources []string) *Reconciler {
+ interval time.Duration, opts Opts) *Reconciler {
return &Reconciler{
Client: k8sClient,
Log: logger,
Scheme: scheme,
- SvcName: svcName,
- SvcNamespace: svcNamespace,
- SecretName: secretName,
- SecretNamespace: secretNamespace,
+ SvcName: opts.SvcName,
+ SvcNamespace: opts.SvcNamespace,
+ SecretName: opts.SecretName,
+ SecretNamespace: opts.SecretNamespace,
RequeueInterval: interval,
- CrdResources: resources,
+ CrdResources: opts.Resources,
CAName: "external-secrets",
CAOrganization: "external-secrets",
leaderChan: leaderChan,
@@ -107,18 +116,9 @@ type CertInfo struct {
CAName string
}
-func contains(s []string, e string) bool {
- for _, a := range s {
- if a == e {
- return true
- }
- }
- return false
-}
-
func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
log := r.Log.WithValues("CustomResourceDefinition", req.NamespacedName)
- if contains(r.CrdResources, req.NamespacedName.Name) {
+ if slices.Contains(r.CrdResources, req.NamespacedName.Name) {
err := r.updateCRD(ctx, req)
if err != nil {
log.Error(err, "failed to inject conversion webhook")
@@ -175,10 +175,10 @@ func (r *Reconciler) checkEndpoints() error {
return err
}
if len(eps.Subsets) == 0 {
- return fmt.Errorf(errSubsetsNotReady)
+ return errors.New(errSubsetsNotReady)
}
if len(eps.Subsets[0].Addresses) == 0 {
- return fmt.Errorf(errAddressesNotReady)
+ return errors.New(errAddressesNotReady)
}
return nil
}
@@ -234,7 +234,7 @@ func injectService(crd *apiext.CustomResourceDefinition, svc types.NamespacedNam
crd.Spec.Conversion.Webhook == nil ||
crd.Spec.Conversion.Webhook.ClientConfig == nil ||
crd.Spec.Conversion.Webhook.ClientConfig.Service == nil {
- return fmt.Errorf("unexpected crd conversion webhook config")
+ return errors.New("unexpected crd conversion webhook config")
}
crd.Spec.Conversion.Webhook.ClientConfig.Service.Namespace = svc.Namespace
crd.Spec.Conversion.Webhook.ClientConfig.Service.Name = svc.Name
@@ -245,7 +245,7 @@ func injectCert(crd *apiext.CustomResourceDefinition, certPem []byte) error {
if crd.Spec.Conversion == nil ||
crd.Spec.Conversion.Webhook == nil ||
crd.Spec.Conversion.Webhook.ClientConfig == nil {
- return fmt.Errorf("unexpected crd conversion webhook config")
+ return errors.New("unexpected crd conversion webhook config")
}
crd.Spec.Conversion.Webhook.ClientConfig.CABundle = certPem
return nil
diff --git a/pkg/controllers/crds/suite_test.go b/pkg/controllers/crds/suite_test.go
index 069f7ceea18..dfbff686e53 100644
--- a/pkg/controllers/crds/suite_test.go
+++ b/pkg/controllers/crds/suite_test.go
@@ -31,6 +31,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/metrics/server"
esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ ctrlcommon "github.com/external-secrets/external-secrets/pkg/controllers/common"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@@ -81,10 +82,19 @@ var _ = BeforeSuite(func() {
leaderChan := make(chan struct{})
close(leaderChan)
rec := New(k8sClient, k8sManager.GetScheme(), leaderChan, log, time.Second*1,
- "foo", "default", "foo", "default", []string{
- "secretstores.test.io",
+ Opts{
+ SvcName: "foo",
+ SvcNamespace: "default",
+ SecretName: "foo",
+ SecretNamespace: "default",
+ Resources: []string{
+ "secretstores.test.io",
+ },
})
- rec.SetupWithManager(k8sManager, controller.Options{})
+ err = rec.SetupWithManager(k8sManager, controller.Options{
+ MaxConcurrentReconciles: 1,
+ RateLimiter: ctrlcommon.BuildRateLimiter(),
+ })
Expect(err).ToNot(HaveOccurred())
go func() {
diff --git a/pkg/controllers/externalsecret/externalsecret_controller.go b/pkg/controllers/externalsecret/externalsecret_controller.go
index dbc0ac04e9a..26c36b61e00 100644
--- a/pkg/controllers/externalsecret/externalsecret_controller.go
+++ b/pkg/controllers/externalsecret/externalsecret_controller.go
@@ -17,7 +17,10 @@ package externalsecret
import (
"context"
"encoding/json"
+ "errors"
"fmt"
+ "maps"
+ "slices"
"strings"
"time"
@@ -27,21 +30,29 @@ import (
"k8s.io/apimachinery/pkg/api/equality"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/fields"
+ "k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/record"
+ "k8s.io/utils/ptr"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
+ "sigs.k8s.io/controller-runtime/pkg/handler"
+ "sigs.k8s.io/controller-runtime/pkg/predicate"
+ "sigs.k8s.io/controller-runtime/pkg/reconcile"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
// Metrics.
"github.com/external-secrets/external-secrets/pkg/controllers/externalsecret/esmetrics"
ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
"github.com/external-secrets/external-secrets/pkg/utils"
+ "github.com/external-secrets/external-secrets/pkg/utils/resolvers"
// Loading registered generators.
_ "github.com/external-secrets/external-secrets/pkg/generator/register"
@@ -50,32 +61,73 @@ import (
)
const (
- fieldOwnerTemplate = "externalsecrets.external-secrets.io/%v"
- errGetES = "could not get ExternalSecret"
- errConvert = "could not apply conversion strategy to keys: %v"
- errDecode = "could not apply decoding strategy to %v[%d]: %v"
- errGenerate = "could not generate [%d]: %w"
- errRewrite = "could not rewrite spec.dataFrom[%d]: %v"
- errInvalidKeys = "secret keys from spec.dataFrom.%v[%d] can only have alphanumeric,'-', '_' or '.' characters. Convert them using rewrite (https://external-secrets.io/latest/guides-datafrom-rewrite)"
- errUpdateSecret = "could not update Secret"
- errPatchStatus = "unable to patch status"
- errGetExistingSecret = "could not get existing secret: %w"
- errSetCtrlReference = "could not set ExternalSecret controller reference: %w"
- errFetchTplFrom = "error fetching templateFrom data: %w"
- errGetSecretData = "could not get secret data from provider"
- errDeleteSecret = "could not delete secret"
- errApplyTemplate = "could not apply template: %w"
- errExecTpl = "could not execute template: %w"
- errInvalidCreatePolicy = "invalid creationPolicy=%s. Can not delete secret i do not own"
- errPolicyMergeNotFound = "the desired secret %s was not found. With creationPolicy=Merge the secret won't be created"
- errPolicyMergeGetSecret = "unable to get secret %s: %w"
- errPolicyMergeMutate = "unable to mutate secret %s: %w"
- errPolicyMergePatch = "unable to patch secret %s: %w"
+ fieldOwnerTemplate = "externalsecrets.external-secrets.io/%v"
+
+ // condition messages for "SecretSynced" reason.
+ msgSynced = "secret synced"
+ msgSyncedRetain = "secret retained due to DeletionPolicy=Retain"
+
+ // condition messages for "SecretDeleted" reason.
+ msgDeleted = "secret deleted due to DeletionPolicy=Delete"
+
+ // condition messages for "SecretMissing" reason.
+ msgMissing = "secret will not be created due to CreationPolicy=Merge"
+
+ // condition messages for "SecretSyncedError" reason.
+ msgErrorGetSecretData = "could not get secret data from provider"
+ msgErrorDeleteSecret = "could not delete secret"
+ msgErrorDeleteOrphaned = "could not delete orphaned secrets"
+ msgErrorUpdateSecret = "could not update secret"
+ msgErrorUpdateImmutable = "could not update secret, target is immutable"
+ msgErrorBecomeOwner = "failed to take ownership of target secret"
+ msgErrorIsOwned = "target is owned by another ExternalSecret"
+
+ // log messages.
+ logErrorGetES = "unable to get ExternalSecret"
+ logErrorUpdateESStatus = "unable to update ExternalSecret status"
+ logErrorGetSecret = "unable to get Secret"
+ logErrorPatchSecret = "unable to patch Secret"
+ logErrorSecretCacheNotSynced = "controller caches for Secret are not in sync"
+ logErrorUnmanagedStore = "unable to determine if store is managed"
+
+ // error formats.
+ errConvert = "error applying conversion strategy %s to keys: %w"
+ errRewrite = "error applying rewrite to keys: %w"
+ errDecode = "error applying decoding strategy %s to data: %w"
+ errGenerate = "error using generator: %w"
+ errInvalidKeys = "invalid secret keys (TIP: use rewrite or conversionStrategy to change keys): %w"
+ errFetchTplFrom = "error fetching templateFrom data: %w"
+ errApplyTemplate = "could not apply template: %w"
+ errExecTpl = "could not execute template: %w"
+ errMutate = "unable to mutate secret %s: %w"
+ errUpdate = "unable to update secret %s: %w"
+ errUpdateNotFound = "unable to update secret %s: not found"
+ errDeleteCreatePolicy = "unable to delete secret %s: creationPolicy=%s is not Owner"
+ errSecretCachesNotSynced = "controller caches for secret %s are not in sync"
+
+ // event messages.
+ eventCreated = "secret created"
+ eventUpdated = "secret updated"
+ eventDeleted = "secret deleted due to DeletionPolicy=Delete"
+ eventDeletedOrphaned = "secret deleted because it was orphaned"
+ eventMissingProviderSecret = "secret does not exist at provider using spec.dataFrom[%d]"
+ eventMissingProviderSecretKey = "secret does not exist at provider using spec.dataFrom[%d] (key=%s)"
)
+// these errors are explicitly defined so we can detect them with `errors.Is()`.
+var (
+ ErrSecretImmutable = fmt.Errorf("secret is immutable")
+ ErrSecretIsOwned = fmt.Errorf("secret is owned by another ExternalSecret")
+ ErrSecretSetCtrlRef = fmt.Errorf("could not set controller reference on secret")
+ ErrSecretRemoveCtrlRef = fmt.Errorf("could not remove controller reference on secret")
+)
+
+const indexESTargetSecretNameField = ".metadata.targetSecretName"
+
// Reconciler reconciles a ExternalSecret object.
type Reconciler struct {
client.Client
+ SecretClient client.Client
Log logr.Logger
Scheme *runtime.Scheme
RestConfig *rest.Config
@@ -89,7 +141,7 @@ type Reconciler struct {
// Reconcile implements the main reconciliation loop
// for watched objects (ExternalSecret, ClusterSecretStore and SecretStore),
// and updates/creates a Kubernetes secret based on them.
-func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
+func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (result ctrl.Result, err error) {
log := r.Log.WithValues("ExternalSecret", req.NamespacedName)
resourceLabels := ctrlmetrics.RefineNonConditionMetricLabels(map[string]string{"name": req.Name, "namespace": req.Namespace})
@@ -103,11 +155,13 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
esmetrics.GetCounterVec(esmetrics.SyncCallsKey).With(resourceLabels).Inc()
}()
- var externalSecret esv1beta1.ExternalSecret
- err := r.Get(ctx, req.NamespacedName, &externalSecret)
-
+ externalSecret := &esv1beta1.ExternalSecret{}
+ err = r.Get(ctx, req.NamespacedName, externalSecret)
if err != nil {
if apierrors.IsNotFound(err) {
+ // NOTE: this does not actually set the condition on the ExternalSecret, because it does not exist
+ // this is a hack to disable metrics for deleted ExternalSecrets, see:
+ // https://github.com/external-secrets/external-secrets/pull/612
conditionSynced := NewExternalSecretCondition(esv1beta1.ExternalSecretDeleted, v1.ConditionFalse, esv1beta1.ConditionReasonSecretDeleted, "Secret was deleted")
SetExternalSecretCondition(&esv1beta1.ExternalSecret{
ObjectMeta: metav1.ObjectMeta{
@@ -119,95 +173,159 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
return ctrl.Result{}, nil
}
- log.Error(err, errGetES)
+ log.Error(err, logErrorGetES)
syncCallsError.With(resourceLabels).Inc()
-
return ctrl.Result{}, err
}
- timeSinceLastRefresh := 0 * time.Second
- if !externalSecret.Status.RefreshTime.IsZero() {
- timeSinceLastRefresh = time.Since(externalSecret.Status.RefreshTime.Time)
- }
-
// skip reconciliation if deletion timestamp is set on external secret
- if externalSecret.DeletionTimestamp != nil {
- log.Info("skipping as it is in deletion")
+ if !externalSecret.GetDeletionTimestamp().IsZero() {
+ log.V(1).Info("skipping ExternalSecret, it is marked for deletion")
return ctrl.Result{}, nil
}
// if extended metrics is enabled, refine the time series vector
resourceLabels = ctrlmetrics.RefineLabels(resourceLabels, externalSecret.Labels)
+ // skip this ExternalSecret if it uses a ClusterSecretStore and the feature is disabled
if shouldSkipClusterSecretStore(r, externalSecret) {
- log.Info("skipping cluster secret store as it is disabled")
+ log.V(1).Info("skipping ExternalSecret, ClusterSecretStore feature is disabled")
return ctrl.Result{}, nil
}
- // skip when pointing to an unmanaged store
+ // skip this ExternalSecret if it uses any SecretStore not managed by this controller
skip, err := shouldSkipUnmanagedStore(ctx, req.Namespace, r, externalSecret)
+ if err != nil {
+ log.Error(err, logErrorUnmanagedStore)
+ syncCallsError.With(resourceLabels).Inc()
+ return ctrl.Result{}, err
+ }
if skip {
- log.Info("skipping unmanaged store as it points to a unmanaged controllerClass")
+ log.V(1).Info("skipping ExternalSecret, uses unmanaged SecretStore")
return ctrl.Result{}, nil
}
- refreshInt := r.RequeueInterval
- if externalSecret.Spec.RefreshInterval != nil {
- refreshInt = externalSecret.Spec.RefreshInterval.Duration
- }
-
- // Target Secret Name should default to the ExternalSecret name if not explicitly specified
+ // the target secret name defaults to the ExternalSecret name, if not explicitly set
secretName := externalSecret.Spec.Target.Name
if secretName == "" {
- secretName = externalSecret.ObjectMeta.Name
+ secretName = externalSecret.Name
+ }
+
+ // fetch the existing secret (from the partial cache)
+ // - please note that the ~partial cache~ is different from the ~full cache~
+ // so there can be race conditions between the two caches
+ // - the WatchesMetadata(v1.Secret{}) in SetupWithManager() is using the partial cache
+ // so we might receive a reconcile request before the full cache is updated
+ // - furthermore, when `--enable-managed-secrets-caching` is true, the full cache
+ // will ONLY include secrets with the "managed" label, so we cant use the full cache
+ // to reliably determine if a secret exists or not
+ secretPartial := &metav1.PartialObjectMetadata{}
+ secretPartial.SetGroupVersionKind(v1.SchemeGroupVersion.WithKind("Secret"))
+ err = r.Get(ctx, client.ObjectKey{Name: secretName, Namespace: externalSecret.Namespace}, secretPartial)
+ if err != nil && !apierrors.IsNotFound(err) {
+ log.Error(err, logErrorGetSecret, "secretName", secretName, "secretNamespace", externalSecret.Namespace)
+ syncCallsError.With(resourceLabels).Inc()
+ return ctrl.Result{}, err
}
- // fetch external secret, we need to ensure that it exists, and it's hashmap corresponds
- var existingSecret v1.Secret
- err = r.Get(ctx, types.NamespacedName{
- Name: secretName,
- Namespace: externalSecret.Namespace,
- }, &existingSecret)
+ // if the secret exists but does not have the "managed" label, add the label
+ // using a PATCH so it is visible in the cache, then requeue immediately
+ if secretPartial.UID != "" && secretPartial.Labels[esv1beta1.LabelManaged] != esv1beta1.LabelManagedValue {
+ fqdn := fmt.Sprintf(fieldOwnerTemplate, externalSecret.Name)
+ patch := client.MergeFrom(secretPartial.DeepCopy())
+ if secretPartial.Labels == nil {
+ secretPartial.Labels = make(map[string]string)
+ }
+ secretPartial.Labels[esv1beta1.LabelManaged] = esv1beta1.LabelManagedValue
+ err = r.Patch(ctx, secretPartial, patch, client.FieldOwner(fqdn))
+ if err != nil {
+ log.Error(err, logErrorPatchSecret, "secretName", secretName, "secretNamespace", externalSecret.Namespace)
+ syncCallsError.With(resourceLabels).Inc()
+ return ctrl.Result{}, err
+ }
+ return ctrl.Result{Requeue: true}, nil
+ }
+
+ // fetch existing secret (from the full cache)
+ // NOTE: we are using the `r.SecretClient` which we only use for managed secrets.
+ // when `enableManagedSecretsCache` is true, this is a cached client that only sees our managed secrets,
+ // otherwise it will be the normal controller-runtime client which may be cached or make direct API calls,
+ // depending on if `enabledSecretCache` is true or false.
+ existingSecret := &v1.Secret{}
+ err = r.SecretClient.Get(ctx, client.ObjectKey{Name: secretName, Namespace: externalSecret.Namespace}, existingSecret)
if err != nil && !apierrors.IsNotFound(err) {
- log.Error(err, errGetExistingSecret)
+ log.Error(err, logErrorGetSecret, "secretName", secretName, "secretNamespace", externalSecret.Namespace)
+ syncCallsError.With(resourceLabels).Inc()
return ctrl.Result{}, err
}
- // refresh should be skipped if
- // 1. resource generation hasn't changed
- // 2. refresh interval is 0
- // 3. if we're still within refresh-interval
- if !shouldRefresh(externalSecret) && isSecretValid(existingSecret) {
- refreshInt = (externalSecret.Spec.RefreshInterval.Duration - timeSinceLastRefresh) + 5*time.Second
- log.V(1).Info("skipping refresh", "rv", getResourceVersion(externalSecret), "nr", refreshInt.Seconds())
- return ctrl.Result{RequeueAfter: refreshInt}, nil
+ // ensure the full cache is up-to-date
+ // NOTE: this prevents race conditions between the partial and full cache.
+ // we return an error so we get an exponential backoff if we end up looping,
+ // for example, during high cluster load and frequent updates to the target secret by other controllers.
+ if secretPartial.UID != existingSecret.UID || secretPartial.ResourceVersion != existingSecret.ResourceVersion {
+ err = fmt.Errorf(errSecretCachesNotSynced, secretName)
+ log.Error(err, logErrorSecretCacheNotSynced, "secretName", secretName, "secretNamespace", externalSecret.Namespace)
+ syncCallsError.With(resourceLabels).Inc()
+ return ctrl.Result{}, err
}
- if !shouldReconcile(externalSecret) {
- log.V(1).Info("stopping reconciling", "rv", getResourceVersion(externalSecret))
- return ctrl.Result{}, nil
+
+ // refresh will be skipped if ALL the following conditions are met:
+ // 1. refresh interval is not 0
+ // 2. resource generation of the ExternalSecret has not changed
+ // 3. the last refresh time of the ExternalSecret is within the refresh interval
+ // 4. the target secret is valid:
+ // - it exists
+ // - it has the correct "managed" label
+ // - it has the correct "data-hash" annotation
+ if !shouldRefresh(externalSecret) && isSecretValid(existingSecret) {
+ log.V(1).Info("skipping refresh")
+ return r.getRequeueResult(externalSecret), nil
}
- // patch status when done processing
- p := client.MergeFrom(externalSecret.DeepCopy())
+ // update status of the ExternalSecret when this function returns, if needed.
+ // NOTE: we use the ability of deferred functions to update named return values `result` and `err`
+ // NOTE: we dereference the DeepCopy of the status field because status fields are NOT pointers,
+ // so otherwise the `equality.Semantic.DeepEqual` will always return false.
+ currentStatus := *externalSecret.Status.DeepCopy()
defer func() {
- err = r.Status().Patch(ctx, &externalSecret, p)
- if err != nil {
- log.Error(err, errPatchStatus)
+ // if the status has not changed, we don't need to update it
+ if equality.Semantic.DeepEqual(currentStatus, externalSecret.Status) {
+ return
}
- }()
- secret := &v1.Secret{
- ObjectMeta: metav1.ObjectMeta{
- Name: secretName,
- Namespace: externalSecret.Namespace,
- },
- Immutable: &externalSecret.Spec.Target.Immutable,
- Data: make(map[string][]byte),
- }
+ // update the status of the ExternalSecret, storing any error in a new variable
+ // if there was no new error, we don't need to change the `result` or `err` values
+ updateErr := r.Status().Update(ctx, externalSecret)
+ if updateErr == nil {
+ return
+ }
+
+ // if we got an update conflict, we should requeue immediately
+ if apierrors.IsConflict(updateErr) {
+ log.V(1).Info("conflict while updating status, will requeue")
+
+ // we only explicitly request a requeue if the main function did not return an `err`.
+ // otherwise, we get an annoying log saying that results are ignored when there is an error,
+ // as errors are always retried.
+ if err == nil {
+ result = ctrl.Result{Requeue: true}
+ }
+ return
+ }
- dataMap, err := r.getProviderSecretData(ctx, &externalSecret)
+ // for other errors, log and update the `err` variable if there is no error already
+ // so the reconciler will requeue the request
+ log.Error(updateErr, logErrorUpdateESStatus)
+ if err == nil {
+ err = updateErr
+ }
+ }()
+
+ // retrieve the provider secret data.
+ dataMap, err := r.getProviderSecretData(ctx, externalSecret)
if err != nil {
- r.markAsFailed(log, errGetSecretData, err, &externalSecret, syncCallsError.With(resourceLabels))
+ r.markAsFailed(msgErrorGetSecretData, err, externalSecret, syncCallsError.With(resourceLabels))
return ctrl.Result{}, err
}
@@ -216,230 +334,404 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
switch externalSecret.Spec.Target.DeletionPolicy {
// delete secret and return early.
case esv1beta1.DeletionPolicyDelete:
- // safeguard that we only can delete secrets we own
- // this is also implemented in the es validation webhook
- if externalSecret.Spec.Target.CreationPolicy != esv1beta1.CreatePolicyOwner {
- err := fmt.Errorf(errInvalidCreatePolicy, externalSecret.Spec.Target.CreationPolicy)
- r.markAsFailed(log, errDeleteSecret, err, &externalSecret, syncCallsError.With(resourceLabels))
- return ctrl.Result{}, err
+ // safeguard that we only can delete secrets we own.
+ // this is also implemented in the es validation webhook.
+ // NOTE: this error cant be fixed by retrying so we don't return an error (which would requeue immediately)
+ creationPolicy := externalSecret.Spec.Target.CreationPolicy
+ if creationPolicy != esv1beta1.CreatePolicyOwner {
+ err = fmt.Errorf(errDeleteCreatePolicy, secretName, creationPolicy)
+ r.markAsFailed(msgErrorDeleteSecret, err, externalSecret, syncCallsError.With(resourceLabels))
+ return ctrl.Result{}, nil
}
- if err := r.Delete(ctx, secret); err != nil && !apierrors.IsNotFound(err) {
- r.markAsFailed(log, errDeleteSecret, err, &externalSecret, syncCallsError.With(resourceLabels))
- return ctrl.Result{}, err
+ // delete the secret, if it exists
+ if existingSecret.UID != "" {
+ err = r.Delete(ctx, existingSecret)
+ if err != nil && !apierrors.IsNotFound(err) {
+ r.markAsFailed(msgErrorDeleteSecret, err, externalSecret, syncCallsError.With(resourceLabels))
+ return ctrl.Result{}, err
+ }
+ r.recorder.Event(externalSecret, v1.EventTypeNormal, esv1beta1.ReasonDeleted, eventDeleted)
}
- conditionSynced := NewExternalSecretCondition(esv1beta1.ExternalSecretReady, v1.ConditionTrue, esv1beta1.ConditionReasonSecretDeleted, "secret deleted due to DeletionPolicy")
- SetExternalSecretCondition(&externalSecret, *conditionSynced)
- return ctrl.Result{RequeueAfter: refreshInt}, nil
+ r.markAsDone(externalSecret, start, log, esv1beta1.ConditionReasonSecretDeleted, msgDeleted)
+ return r.getRequeueResult(externalSecret), nil
// In case provider secrets don't exist the kubernetes secret will be kept as-is.
case esv1beta1.DeletionPolicyRetain:
- r.markAsDone(&externalSecret, start, log)
- return ctrl.Result{RequeueAfter: refreshInt}, nil
+ r.markAsDone(externalSecret, start, log, esv1beta1.ConditionReasonSecretSynced, msgSyncedRetain)
+ return r.getRequeueResult(externalSecret), nil
// noop, handled below
case esv1beta1.DeletionPolicyMerge:
}
}
- mutationFunc := func() error {
+ // mutationFunc is a function which can be applied to a secret to make it match the desired state.
+ mutationFunc := func(secret *v1.Secret) error {
+ // get information about the current owner of the secret
+ // - we ignore the API version as it can change over time
+ // - we ignore the UID for consistency with the SetControllerReference function
+ currentOwner := metav1.GetControllerOf(secret)
+ ownerIsESKind := false
+ ownerIsCurrentES := false
+ if currentOwner != nil {
+ currentOwnerGK := schema.FromAPIVersionAndKind(currentOwner.APIVersion, currentOwner.Kind).GroupKind()
+ ownerIsESKind = currentOwnerGK.String() == esv1beta1.ExtSecretGroupKind
+ ownerIsCurrentES = ownerIsESKind && currentOwner.Name == externalSecret.Name
+ }
+
+ // if another ExternalSecret is the owner, we should return an error
+ // otherwise the controller will fight with itself to update the secret.
+ // note, this does not prevent other controllers from owning the secret.
+ if ownerIsESKind && !ownerIsCurrentES {
+ return fmt.Errorf("%w: %s", ErrSecretIsOwned, currentOwner.Name)
+ }
+
+ // if the CreationPolicy is Owner, we should set ourselves as the owner of the secret
if externalSecret.Spec.Target.CreationPolicy == esv1beta1.CreatePolicyOwner {
- err = controllerutil.SetControllerReference(&externalSecret, &secret.ObjectMeta, r.Scheme)
+ err = controllerutil.SetControllerReference(externalSecret, secret, r.Scheme)
+ if err != nil {
+ return fmt.Errorf("%w: %w", ErrSecretSetCtrlRef, err)
+ }
+ }
+
+ // if the creation policy is not Owner, we should remove ourselves as the owner
+ // this could happen if the creation policy was changed after the secret was created
+ if externalSecret.Spec.Target.CreationPolicy != esv1beta1.CreatePolicyOwner && ownerIsCurrentES {
+ err = controllerutil.RemoveControllerReference(externalSecret, secret, r.Scheme)
if err != nil {
- return fmt.Errorf(errSetCtrlReference, err)
+ return fmt.Errorf("%w: %w", ErrSecretRemoveCtrlRef, err)
}
}
+
+ // initialize maps within the secret so it's safe to set values
+ if secret.Annotations == nil {
+ secret.Annotations = make(map[string]string)
+ }
+ if secret.Labels == nil {
+ secret.Labels = make(map[string]string)
+ }
if secret.Data == nil {
secret.Data = make(map[string][]byte)
}
- // diff existing keys
- keys, err := getManagedDataKeys(&existingSecret, externalSecret.Name)
+
+ // get the list of keys that are managed by this ExternalSecret
+ keys, err := getManagedDataKeys(secret, externalSecret.Name)
if err != nil {
return err
}
- // Sanitize data map for any updates on the ES
+
+ // remove any data keys that are managed by this ExternalSecret, so we can re-add them
+ // this ensures keys added by templates are not left behind when they are removed from the template
for _, key := range keys {
- if dataMap[key] == nil {
- secret.Data[key] = nil
- // Sanitizing any templated / updated keys
- delete(secret.Data, key)
- }
+ delete(secret.Data, key)
}
- err = r.applyTemplate(ctx, &externalSecret, secret, dataMap)
+
+ // WARNING: this will remove any labels or annotations managed by this ExternalSecret
+ // so any updates to labels and annotations should be done AFTER this point
+ err = r.applyTemplate(ctx, externalSecret, secret, dataMap)
if err != nil {
return fmt.Errorf(errApplyTemplate, err)
}
+
+ // set the immutable flag on the secret if requested by the ExternalSecret
+ if externalSecret.Spec.Target.Immutable {
+ secret.Immutable = ptr.To(true)
+ }
+
+ // we also use a label to keep track of the owner of the secret
+ // this lets us remove secrets that are no longer needed if the target secret name changes
if externalSecret.Spec.Target.CreationPolicy == esv1beta1.CreatePolicyOwner {
lblValue := utils.ObjectHash(fmt.Sprintf("%v/%v", externalSecret.Namespace, externalSecret.Name))
secret.Labels[esv1beta1.LabelOwner] = lblValue
+ } else {
+ // the label should not be set if the creation policy is not Owner
+ delete(secret.Labels, esv1beta1.LabelOwner)
}
- secret.Annotations[esv1beta1.AnnotationDataHash] = r.computeDataHashAnnotation(&existingSecret, secret)
+ secret.Labels[esv1beta1.LabelManaged] = esv1beta1.LabelManagedValue
+ secret.Annotations[esv1beta1.AnnotationDataHash] = utils.ObjectHash(secret.Data)
return nil
}
- switch externalSecret.Spec.Target.CreationPolicy { //nolint:exhaustive
- case esv1beta1.CreatePolicyMerge:
- err = r.patchSecret(ctx, secret, mutationFunc, &externalSecret)
- if err == nil {
- externalSecret.Status.Binding = v1.LocalObjectReference{Name: secret.Name}
- }
+ switch externalSecret.Spec.Target.CreationPolicy {
case esv1beta1.CreatePolicyNone:
- log.V(1).Info("secret creation skipped due to creationPolicy=None")
+ log.V(1).Info("secret creation skipped due to CreationPolicy=None")
err = nil
- default:
- var created bool
- created, err = r.createOrUpdateSecret(ctx, secret, mutationFunc, &externalSecret)
- if err == nil {
- externalSecret.Status.Binding = v1.LocalObjectReference{Name: secret.Name}
- }
- // cleanup orphaned secrets
- if created {
- delErr := deleteOrphanedSecrets(ctx, r.Client, &externalSecret)
- if delErr != nil {
- msg := fmt.Sprintf("failed to clean up orphaned secrets: %v", delErr)
- r.markAsFailed(log, msg, delErr, &externalSecret, syncCallsError.With(resourceLabels))
- return ctrl.Result{}, delErr
- }
+ case esv1beta1.CreatePolicyMerge:
+ // update the secret, if it exists
+ if existingSecret.UID != "" {
+ err = r.updateSecret(ctx, existingSecret, mutationFunc, externalSecret, secretName)
+ } else {
+ // if the secret does not exist, we wait until the next refresh interval
+ // rather than returning an error which would requeue immediately
+ r.markAsDone(externalSecret, start, log, esv1beta1.ConditionReasonSecretMissing, msgMissing)
+ return r.getRequeueResult(externalSecret), nil
+ }
+ case esv1beta1.CreatePolicyOrphan:
+ // create the secret, if it does not exist
+ if existingSecret.UID == "" {
+ err = r.createSecret(ctx, mutationFunc, externalSecret, secretName)
+ } else {
+ // if the secret exists, we should update it
+ err = r.updateSecret(ctx, existingSecret, mutationFunc, externalSecret, secretName)
+ }
+ case esv1beta1.CreatePolicyOwner:
+ // we may have orphaned secrets to clean up,
+ // for example, if the target secret name was changed
+ err = r.deleteOrphanedSecrets(ctx, externalSecret, secretName)
+ if err != nil {
+ r.markAsFailed(msgErrorDeleteOrphaned, err, externalSecret, syncCallsError.With(resourceLabels))
+ return ctrl.Result{}, err
}
- }
+ // create the secret, if it does not exist
+ if existingSecret.UID == "" {
+ err = r.createSecret(ctx, mutationFunc, externalSecret, secretName)
+ } else {
+ // if the secret exists, we should update it
+ err = r.updateSecret(ctx, existingSecret, mutationFunc, externalSecret, secretName)
+ }
+ }
if err != nil {
- r.markAsFailed(log, errUpdateSecret, err, &externalSecret, syncCallsError.With(resourceLabels))
+ // if we got an update conflict, we should requeue immediately
+ if apierrors.IsConflict(err) {
+ log.V(1).Info("conflict while updating secret, will requeue")
+ return ctrl.Result{Requeue: true}, nil
+ }
+
+ // detect errors indicating that we failed to set ourselves as the owner of the secret
+ // NOTE: this error cant be fixed by retrying so we don't return an error (which would requeue immediately)
+ if errors.Is(err, ErrSecretSetCtrlRef) {
+ r.markAsFailed(msgErrorBecomeOwner, err, externalSecret, syncCallsError.With(resourceLabels))
+ return ctrl.Result{}, nil
+ }
+
+ // detect errors indicating that the secret has another ExternalSecret as owner
+ // NOTE: this error cant be fixed by retrying so we don't return an error (which would requeue immediately)
+ if errors.Is(err, ErrSecretIsOwned) {
+ r.markAsFailed(msgErrorIsOwned, err, externalSecret, syncCallsError.With(resourceLabels))
+ return ctrl.Result{}, nil
+ }
+
+ // detect errors indicating that the secret is immutable
+ // NOTE: this error cant be fixed by retrying so we don't return an error (which would requeue immediately)
+ if errors.Is(err, ErrSecretImmutable) {
+ r.markAsFailed(msgErrorUpdateImmutable, err, externalSecret, syncCallsError.With(resourceLabels))
+ return ctrl.Result{}, nil
+ }
+
+ r.markAsFailed(msgErrorUpdateSecret, err, externalSecret, syncCallsError.With(resourceLabels))
return ctrl.Result{}, err
}
- r.markAsDone(&externalSecret, start, log)
+ r.markAsDone(externalSecret, start, log, esv1beta1.ConditionReasonSecretSynced, msgSynced)
+ return r.getRequeueResult(externalSecret), nil
+}
+
+// getRequeueResult create a result with requeueAfter based on the ExternalSecret refresh interval.
+func (r *Reconciler) getRequeueResult(externalSecret *esv1beta1.ExternalSecret) ctrl.Result {
+ // default to the global requeue interval
+ // note, this will never be used because the CRD has a default value of 1 hour
+ refreshInterval := r.RequeueInterval
+ if externalSecret.Spec.RefreshInterval != nil {
+ refreshInterval = externalSecret.Spec.RefreshInterval.Duration
+ }
+
+ // if the refresh interval is <= 0, we should not requeue
+ if refreshInterval <= 0 {
+ return ctrl.Result{}
+ }
+
+ // if the last refresh time is not set, requeue after the refresh interval
+ // note, this should not happen, as we only call this function on ExternalSecrets
+ // that have been reconciled at least once
+ if externalSecret.Status.RefreshTime.IsZero() {
+ return ctrl.Result{RequeueAfter: refreshInterval}
+ }
- return ctrl.Result{
- RequeueAfter: refreshInt,
- }, nil
+ timeSinceLastRefresh := time.Since(externalSecret.Status.RefreshTime.Time)
+
+ // if the last refresh time is in the future, we should requeue immediately
+ // note, this should not happen, as we always refresh an ExternalSecret
+ // that has a last refresh time in the future
+ if timeSinceLastRefresh < 0 {
+ return ctrl.Result{Requeue: true}
+ }
+
+ // if there is time remaining, requeue after the remaining time
+ if timeSinceLastRefresh < refreshInterval {
+ return ctrl.Result{RequeueAfter: refreshInterval - timeSinceLastRefresh}
+ }
+
+ // otherwise, requeue immediately
+ return ctrl.Result{Requeue: true}
}
-func (r *Reconciler) markAsDone(externalSecret *esv1beta1.ExternalSecret, start time.Time, log logr.Logger) {
- conditionSynced := NewExternalSecretCondition(esv1beta1.ExternalSecretReady, v1.ConditionTrue, esv1beta1.ConditionReasonSecretSynced, "Secret was synced")
- currCond := GetExternalSecretCondition(externalSecret.Status, esv1beta1.ExternalSecretReady)
- SetExternalSecretCondition(externalSecret, *conditionSynced)
+func (r *Reconciler) markAsDone(externalSecret *esv1beta1.ExternalSecret, start time.Time, log logr.Logger, reason, msg string) {
+ oldReadyCondition := GetExternalSecretCondition(externalSecret.Status, esv1beta1.ExternalSecretReady)
+ newReadyCondition := NewExternalSecretCondition(esv1beta1.ExternalSecretReady, v1.ConditionTrue, reason, msg)
+ SetExternalSecretCondition(externalSecret, *newReadyCondition)
+
externalSecret.Status.RefreshTime = metav1.NewTime(start)
- externalSecret.Status.SyncedResourceVersion = getResourceVersion(*externalSecret)
- if currCond == nil || currCond.Status != conditionSynced.Status {
- log.Info("reconciled secret") // Log once if on success in any verbosity
+ externalSecret.Status.SyncedResourceVersion = getResourceVersion(externalSecret)
+
+ // if the status or reason has changed, log at the appropriate verbosity level
+ if oldReadyCondition == nil || oldReadyCondition.Status != newReadyCondition.Status || oldReadyCondition.Reason != newReadyCondition.Reason {
+ if newReadyCondition.Reason == esv1beta1.ConditionReasonSecretDeleted {
+ log.Info("deleted secret")
+ } else {
+ log.Info("reconciled secret")
+ }
} else {
- log.V(1).Info("reconciled secret") // Log all reconciliation cycles if higher verbosity applied
+ log.V(1).Info("reconciled secret")
}
}
-func (r *Reconciler) markAsFailed(log logr.Logger, msg string, err error, externalSecret *esv1beta1.ExternalSecret, counter prometheus.Counter) {
- log.Error(err, msg)
+func (r *Reconciler) markAsFailed(msg string, err error, externalSecret *esv1beta1.ExternalSecret, counter prometheus.Counter) {
r.recorder.Event(externalSecret, v1.EventTypeWarning, esv1beta1.ReasonUpdateFailed, err.Error())
conditionSynced := NewExternalSecretCondition(esv1beta1.ExternalSecretReady, v1.ConditionFalse, esv1beta1.ConditionReasonSecretSyncedError, msg)
SetExternalSecretCondition(externalSecret, *conditionSynced)
counter.Inc()
}
-func deleteOrphanedSecrets(ctx context.Context, cl client.Client, externalSecret *esv1beta1.ExternalSecret) error {
- secretList := v1.SecretList{}
- lblValue := utils.ObjectHash(fmt.Sprintf("%v/%v", externalSecret.Namespace, externalSecret.Name))
- ls := &metav1.LabelSelector{
- MatchLabels: map[string]string{
- esv1beta1.LabelOwner: lblValue,
- },
- }
- labelSelector, err := metav1.LabelSelectorAsSelector(ls)
- if err != nil {
- return err
+func (r *Reconciler) deleteOrphanedSecrets(ctx context.Context, externalSecret *esv1beta1.ExternalSecret, secretName string) error {
+ ownerLabel := utils.ObjectHash(fmt.Sprintf("%v/%v", externalSecret.Namespace, externalSecret.Name))
+
+ // we use a PartialObjectMetadataList to avoid loading the full secret objects
+ // and because the Secrets partials are always cached due to WatchesMetadata() in SetupWithManager()
+ secretListPartial := &metav1.PartialObjectMetadataList{}
+ secretListPartial.SetGroupVersionKind(v1.SchemeGroupVersion.WithKind("SecretList"))
+ listOpts := &client.ListOptions{
+ LabelSelector: labels.SelectorFromSet(map[string]string{
+ esv1beta1.LabelOwner: ownerLabel,
+ }),
+ Namespace: externalSecret.Namespace,
}
- err = cl.List(ctx, &secretList, &client.ListOptions{LabelSelector: labelSelector})
- if err != nil {
+ if err := r.List(ctx, secretListPartial, listOpts); err != nil {
return err
}
- for key, secret := range secretList.Items {
- if externalSecret.Spec.Target.Name != "" && secret.Name != externalSecret.Spec.Target.Name {
- err = cl.Delete(ctx, &secretList.Items[key])
- if err != nil {
+
+ // delete all secrets that are not the target secret
+ for _, secretPartial := range secretListPartial.Items {
+ if secretPartial.GetName() != secretName {
+ err := r.Delete(ctx, &secretPartial)
+ if err != nil && !apierrors.IsNotFound(err) {
return err
}
+ r.recorder.Event(externalSecret, v1.EventTypeNormal, esv1beta1.ReasonDeleted, eventDeletedOrphaned)
}
}
+
return nil
}
-func (r *Reconciler) createOrUpdateSecret(ctx context.Context, secret *v1.Secret, mutationFunc func() error, es *esv1beta1.ExternalSecret) (bool, error) {
+// createSecret creates a new secret with the given mutation function.
+func (r *Reconciler) createSecret(ctx context.Context, mutationFunc func(secret *v1.Secret) error, es *esv1beta1.ExternalSecret, secretName string) error {
fqdn := fmt.Sprintf(fieldOwnerTemplate, es.Name)
- key := client.ObjectKeyFromObject(secret)
- if err := r.Client.Get(ctx, key, secret); err != nil {
- if !apierrors.IsNotFound(err) {
- return false, err
- }
- if err := mutationFunc(); err != nil {
- return false, err
- }
- // Setting Field Owner even for CreationPolicy==Create
- if err := r.Client.Create(ctx, secret, client.FieldOwner(fqdn)); err != nil {
- return false, err
- }
- r.recorder.Event(es, v1.EventTypeNormal, esv1beta1.ReasonCreated, "Created Secret")
- return true, nil
- }
- existing := secret.DeepCopyObject()
- if err := mutationFunc(); err != nil {
- return false, err
+ // define and mutate the new secret
+ newSecret := &v1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: secretName,
+ Namespace: es.Namespace,
+ },
+ Data: make(map[string][]byte),
}
-
- if equality.Semantic.DeepEqual(existing, secret) {
- return false, nil
+ if err := mutationFunc(newSecret); err != nil {
+ return err
}
- if err := r.Client.Update(ctx, secret, client.FieldOwner(fqdn)); err != nil {
- return false, err
+ // note, we set field owner even for Create
+ if err := r.Create(ctx, newSecret, client.FieldOwner(fqdn)); err != nil {
+ return err
}
- r.recorder.Event(es, v1.EventTypeNormal, esv1beta1.ReasonUpdated, "Updated Secret")
- return false, nil
+
+ // set the binding reference to the secret
+ // https://github.com/external-secrets/external-secrets/pull/2263
+ es.Status.Binding = v1.LocalObjectReference{Name: newSecret.Name}
+
+ r.recorder.Event(es, v1.EventTypeNormal, esv1beta1.ReasonCreated, eventCreated)
+ return nil
}
-func (r *Reconciler) patchSecret(ctx context.Context, secret *v1.Secret, mutationFunc func() error, es *esv1beta1.ExternalSecret) error {
+func (r *Reconciler) updateSecret(ctx context.Context, existingSecret *v1.Secret, mutationFunc func(secret *v1.Secret) error, es *esv1beta1.ExternalSecret, secretName string) error {
fqdn := fmt.Sprintf(fieldOwnerTemplate, es.Name)
- err := r.Client.Get(ctx, client.ObjectKeyFromObject(secret), secret.DeepCopy())
- if apierrors.IsNotFound(err) {
- return fmt.Errorf(errPolicyMergeNotFound, secret.Name)
- }
- if err != nil {
- return fmt.Errorf(errPolicyMergeGetSecret, secret.Name, err)
- }
- existing := secret.DeepCopyObject()
- if err = mutationFunc(); err != nil {
- return fmt.Errorf(errPolicyMergeMutate, secret.Name, err)
+ // fail if the secret does not exist
+ // this should never happen because we check this before calling this function
+ if existingSecret.UID == "" {
+ return fmt.Errorf(errUpdateNotFound, secretName)
}
- // GVK is missing in the Secret, see:
- // https://github.com/kubernetes-sigs/controller-runtime/issues/526
- // https://github.com/kubernetes-sigs/controller-runtime/issues/1517
- // https://github.com/kubernetes/kubernetes/issues/80609
- // we need to manually set it before doing a Patch() as it depends on the GVK
- gvks, unversioned, err := r.Scheme.ObjectKinds(secret)
- if err != nil {
- return err
- }
- if !unversioned && len(gvks) == 1 {
- secret.SetGroupVersionKind(gvks[0])
+ // set the binding reference to the secret
+ // https://github.com/external-secrets/external-secrets/pull/2263
+ es.Status.Binding = v1.LocalObjectReference{Name: secretName}
+
+ // mutate a copy of the existing secret with the mutation function
+ updatedSecret := existingSecret.DeepCopy()
+ if err := mutationFunc(updatedSecret); err != nil {
+ return fmt.Errorf(errMutate, updatedSecret.Name, err)
}
- if equality.Semantic.DeepEqual(existing, secret) {
+ // if the secret does not need to be updated, return early
+ if equality.Semantic.DeepEqual(existingSecret, updatedSecret) {
return nil
}
- // Cleaning up Managed fields manually as to keep patch coherence
- secret.ObjectMeta.ManagedFields = nil
- // we're not able to resolve conflicts so we force ownership
- // see: https://kubernetes.io/docs/reference/using-api/server-side-apply/#using-server-side-apply-in-a-controller
- if err := r.Client.Patch(ctx, secret, client.Apply, client.FieldOwner(fqdn), client.ForceOwnership); err != nil {
- return fmt.Errorf(errPolicyMergePatch, secret.Name, err)
+
+ // if the existing secret is immutable, we can only update the object metadata
+ if ptr.Deref(existingSecret.Immutable, false) {
+ // check if the metadata was changed
+ metadataChanged := !equality.Semantic.DeepEqual(existingSecret.ObjectMeta, updatedSecret.ObjectMeta)
+
+ // check if the immutable data/type was changed
+ var dataChanged bool
+ if metadataChanged {
+ // update the `existingSecret` object with the metadata from `updatedSecret`
+ // this lets us compare the objects to see if the immutable data/type was changed
+ existingSecret.ObjectMeta = *updatedSecret.ObjectMeta.DeepCopy()
+ dataChanged = !equality.Semantic.DeepEqual(existingSecret, updatedSecret)
+
+ // because we use labels and annotations to keep track of the secret,
+ // we need to update the metadata, regardless of if the immutable data was changed
+ // NOTE: we are using the `existingSecret` object here, as we ONLY want to update the metadata,
+ // and we previously copied the metadata from the `updatedSecret` object
+ if err := r.Update(ctx, existingSecret, client.FieldOwner(fqdn)); err != nil {
+ // if we get a conflict, we should return early to requeue immediately
+ // note, we don't wrap this error so we can handle it in the caller
+ if apierrors.IsConflict(err) {
+ return err
+ }
+ return fmt.Errorf(errUpdate, existingSecret.Name, err)
+ }
+ } else {
+ // we know there was some change in the secret (or we would have returned early)
+ // we know the metadata was NOT changed (metadataChanged == false)
+ // so, the only thing that could have changed is the immutable data/type fields
+ dataChanged = true
+ }
+
+ // if the immutable data was changed, we should return an error
+ if dataChanged {
+ return fmt.Errorf(errUpdate, existingSecret.Name, ErrSecretImmutable)
+ }
}
- r.recorder.Event(es, v1.EventTypeNormal, esv1beta1.ReasonUpdated, "Updated Secret")
+
+ // update the secret
+ if err := r.Update(ctx, updatedSecret, client.FieldOwner(fqdn)); err != nil {
+ // if we get a conflict, we should return early to requeue immediately
+ // note, we don't wrap this error so we can handle it in the caller
+ if apierrors.IsConflict(err) {
+ return err
+ }
+ return fmt.Errorf(errUpdate, updatedSecret.Name, err)
+ }
+
+ r.recorder.Event(es, v1.EventTypeNormal, esv1beta1.ReasonUpdated, eventUpdated)
return nil
}
+// getManagedDataKeys returns the list of data keys in a secret which are managed by a specified owner.
func getManagedDataKeys(secret *v1.Secret, fieldOwner string) ([]string, error) {
return getManagedFieldKeys(secret, fieldOwner, func(fields map[string]any) []string {
dataFields := fields["f:data"]
@@ -450,11 +742,8 @@ func getManagedDataKeys(secret *v1.Secret, fieldOwner string) ([]string, error)
if !ok {
return nil
}
- var keys []string
- for k := range df {
- keys = append(keys, k)
- }
- return keys
+
+ return slices.Collect(maps.Keys(df))
})
}
@@ -484,29 +773,31 @@ func getManagedFieldKeys(
return keys, nil
}
-func getResourceVersion(es esv1beta1.ExternalSecret) string {
+func getResourceVersion(es *esv1beta1.ExternalSecret) string {
return fmt.Sprintf("%d-%s", es.ObjectMeta.GetGeneration(), hashMeta(es.ObjectMeta))
}
+// hashMeta returns a consistent hash of the `metadata.labels` and `metadata.annotations` fields of the given object.
func hashMeta(m metav1.ObjectMeta) string {
type meta struct {
annotations map[string]string
labels map[string]string
}
- return utils.ObjectHash(meta{
+ objectMeta := meta{
annotations: m.Annotations,
labels: m.Labels,
- })
+ }
+ return utils.ObjectHash(objectMeta)
}
-func shouldSkipClusterSecretStore(r *Reconciler, es esv1beta1.ExternalSecret) bool {
+func shouldSkipClusterSecretStore(r *Reconciler, es *esv1beta1.ExternalSecret) bool {
return !r.ClusterSecretStoreEnabled && es.Spec.SecretStoreRef.Kind == esv1beta1.ClusterSecretStoreKind
}
// shouldSkipUnmanagedStore iterates over all secretStore references in the externalSecret spec,
// fetches the store and evaluates the controllerClass property.
// Returns true if any storeRef points to store with a non-matching controllerClass.
-func shouldSkipUnmanagedStore(ctx context.Context, namespace string, r *Reconciler, es esv1beta1.ExternalSecret) (bool, error) {
+func shouldSkipUnmanagedStore(ctx context.Context, namespace string, r *Reconciler, es *esv1beta1.ExternalSecret) (bool, error) {
var storeList []esv1beta1.SecretStoreRef
if es.Spec.SecretStoreRef.Name != "" {
@@ -526,11 +817,19 @@ func shouldSkipUnmanagedStore(ctx context.Context, namespace string, r *Reconcil
// verify that generator's controllerClass matches
if ref.SourceRef != nil && ref.SourceRef.GeneratorRef != nil {
- genDef, err := r.getGeneratorDefinition(ctx, namespace, ref.SourceRef.GeneratorRef)
+ _, obj, err := resolvers.GeneratorRef(ctx, r.Client, r.Scheme, namespace, ref.SourceRef.GeneratorRef)
if err != nil {
+ if apierrors.IsNotFound(err) {
+ // skip non-existent generators
+ continue
+ }
+ if errors.Is(err, resolvers.ErrUnableToGetGenerator) {
+ // skip generators that we can't get (e.g. due to being invalid)
+ continue
+ }
return false, err
}
- skipGenerator, err := shouldSkipGenerator(r, genDef)
+ skipGenerator, err := shouldSkipGenerator(r, obj)
if err != nil {
return false, err
}
@@ -551,11 +850,15 @@ func shouldSkipUnmanagedStore(ctx context.Context, namespace string, r *Reconcil
namespace = ""
}
- err := r.Client.Get(ctx, types.NamespacedName{
+ err := r.Get(ctx, types.NamespacedName{
Name: ref.Name,
Namespace: namespace,
}, store)
if err != nil {
+ if apierrors.IsNotFound(err) {
+ // skip non-existent stores
+ continue
+ }
return false, err
}
class := store.GetSpec().Controller
@@ -566,71 +869,108 @@ func shouldSkipUnmanagedStore(ctx context.Context, namespace string, r *Reconcil
return false, nil
}
-func shouldRefresh(es esv1beta1.ExternalSecret) bool {
- // refresh if resource version changed
+func shouldRefresh(es *esv1beta1.ExternalSecret) bool {
+ // if the refresh interval is 0, and we have synced previously, we should not refresh
+ if es.Spec.RefreshInterval.Duration <= 0 && es.Status.SyncedResourceVersion != "" {
+ return false
+ }
+
+ // if the ExternalSecret has been updated, we should refresh
if es.Status.SyncedResourceVersion != getResourceVersion(es) {
return true
}
- // skip refresh if refresh interval is 0
- if es.Spec.RefreshInterval.Duration == 0 && es.Status.SyncedResourceVersion != "" {
- return false
- }
+ // if the last refresh time is zero, we should refresh
if es.Status.RefreshTime.IsZero() {
return true
}
- return es.Status.RefreshTime.Add(es.Spec.RefreshInterval.Duration).Before(time.Now())
-}
-func shouldReconcile(es esv1beta1.ExternalSecret) bool {
- if es.Spec.Target.Immutable && hasSyncedCondition(es) {
- return false
+ // if the last refresh time is in the future, we should refresh
+ if es.Status.RefreshTime.Time.After(time.Now()) {
+ return true
}
- return true
-}
-func hasSyncedCondition(es esv1beta1.ExternalSecret) bool {
- for _, condition := range es.Status.Conditions {
- if condition.Reason == "SecretSynced" {
- return true
- }
- }
- return false
+ // if the last refresh time + refresh interval is before now, we should refresh
+ return es.Status.RefreshTime.Add(es.Spec.RefreshInterval.Duration).Before(time.Now())
}
// isSecretValid checks if the secret exists, and it's data is consistent with the calculated hash.
-func isSecretValid(existingSecret v1.Secret) bool {
- // if target secret doesn't exist, or annotations as not set, we need to refresh
- if existingSecret.UID == "" || existingSecret.Annotations == nil {
+func isSecretValid(existingSecret *v1.Secret) bool {
+ // if target secret doesn't exist, we need to refresh
+ if existingSecret.UID == "" {
return false
}
- // if the calculated hash is different from the calculation, then it's invalid
- if existingSecret.Annotations[esv1beta1.AnnotationDataHash] != utils.ObjectHash(existingSecret.Data) {
+ // if the managed label is missing or incorrect, then it's invalid
+ if existingSecret.Labels[esv1beta1.LabelManaged] != esv1beta1.LabelManagedValue {
return false
}
- return true
-}
-// computeDataHashAnnotation generate a hash of the secret data combining the old key with the new keys to add or override.
-func (r *Reconciler) computeDataHashAnnotation(existing, secret *v1.Secret) string {
- data := make(map[string][]byte)
- for k, v := range existing.Data {
- data[k] = v
- }
- for k, v := range secret.Data {
- data[k] = v
+ // if the data-hash annotation is missing or incorrect, then it's invalid
+ // this is how we know if the data has chanced since we last updated the secret
+ if existingSecret.Annotations[esv1beta1.AnnotationDataHash] != utils.ObjectHash(existingSecret.Data) {
+ return false
}
- return utils.ObjectHash(data)
+
+ return true
}
// SetupWithManager returns a new controller builder that will be started by the provided Manager.
func (r *Reconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options) error {
r.recorder = mgr.GetEventRecorderFor("external-secrets")
+ // index ExternalSecrets based on the target secret name,
+ // this lets us quickly find all ExternalSecrets which target a specific Secret
+ if err := mgr.GetFieldIndexer().IndexField(context.Background(), &esv1beta1.ExternalSecret{}, indexESTargetSecretNameField, func(obj client.Object) []string {
+ es := obj.(*esv1beta1.ExternalSecret)
+ // if the target name is set, use that as the index
+ if es.Spec.Target.Name != "" {
+ return []string{es.Spec.Target.Name}
+ }
+ // otherwise, use the ExternalSecret name
+ return []string{es.Name}
+ }); err != nil {
+ return err
+ }
+
+ // predicate function to ignore secret events unless they have the "managed" label
+ secretHasESLabel := predicate.NewPredicateFuncs(func(object client.Object) bool {
+ value, hasLabel := object.GetLabels()[esv1beta1.LabelManaged]
+ return hasLabel && value == esv1beta1.LabelManagedValue
+ })
+
return ctrl.NewControllerManagedBy(mgr).
WithOptions(opts).
For(&esv1beta1.ExternalSecret{}).
- Owns(&v1.Secret{}, builder.OnlyMetadata).
+ // we cant use Owns(), as we don't set ownerReferences when the creationPolicy is not Owner.
+ // we use WatchesMetadata() to reduce memory usage, as otherwise we have to process full secret objects.
+ WatchesMetadata(
+ &v1.Secret{},
+ handler.EnqueueRequestsFromMapFunc(r.findObjectsForSecret),
+ builder.WithPredicates(predicate.ResourceVersionChangedPredicate{}, secretHasESLabel),
+ ).
Complete(r)
}
+
+func (r *Reconciler) findObjectsForSecret(ctx context.Context, secret client.Object) []reconcile.Request {
+ externalSecretsList := &esv1beta1.ExternalSecretList{}
+ listOps := &client.ListOptions{
+ FieldSelector: fields.OneTermEqualSelector(indexESTargetSecretNameField, secret.GetName()),
+ Namespace: secret.GetNamespace(),
+ }
+ err := r.List(ctx, externalSecretsList, listOps)
+ if err != nil {
+ return []reconcile.Request{}
+ }
+
+ requests := make([]reconcile.Request, len(externalSecretsList.Items))
+ for i, item := range externalSecretsList.Items {
+ requests[i] = reconcile.Request{
+ NamespacedName: types.NamespacedName{
+ Name: item.GetName(),
+ Namespace: item.GetNamespace(),
+ },
+ }
+ }
+ return requests
+}
diff --git a/pkg/controllers/externalsecret/externalsecret_controller_secret.go b/pkg/controllers/externalsecret/externalsecret_controller_secret.go
index 119e73162ee..1fb31c8abb1 100644
--- a/pkg/controllers/externalsecret/externalsecret_controller_secret.go
+++ b/pkg/controllers/externalsecret/externalsecret_controller_secret.go
@@ -22,17 +22,12 @@ import (
v1 "k8s.io/api/core/v1"
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
- metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/runtime/schema"
- "k8s.io/client-go/discovery"
- "k8s.io/client-go/dynamic"
- "k8s.io/client-go/restmapper"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
- // Loading registered providers.
"github.com/external-secrets/external-secrets/pkg/controllers/secretstore"
"github.com/external-secrets/external-secrets/pkg/utils"
+ "github.com/external-secrets/external-secrets/pkg/utils/resolvers"
// Loading registered generators.
_ "github.com/external-secrets/external-secrets/pkg/generator/register"
@@ -54,55 +49,68 @@ func (r *Reconciler) getProviderSecretData(ctx context.Context, externalSecret *
var err error
if remoteRef.Find != nil {
- secretMap, err = r.handleFindAllSecrets(ctx, externalSecret, remoteRef, mgr, i)
+ secretMap, err = r.handleFindAllSecrets(ctx, externalSecret, remoteRef, mgr)
+ if err != nil {
+ err = fmt.Errorf("error processing spec.dataFrom[%d].find, err: %w", i, err)
+ }
} else if remoteRef.Extract != nil {
- secretMap, err = r.handleExtractSecrets(ctx, externalSecret, remoteRef, mgr, i)
+ secretMap, err = r.handleExtractSecrets(ctx, externalSecret, remoteRef, mgr)
+ if err != nil {
+ err = fmt.Errorf("error processing spec.dataFrom[%d].extract, err: %w", i, err)
+ }
} else if remoteRef.SourceRef != nil && remoteRef.SourceRef.GeneratorRef != nil {
- secretMap, err = r.handleGenerateSecrets(ctx, externalSecret.Namespace, remoteRef, i)
+ secretMap, err = r.handleGenerateSecrets(ctx, externalSecret.Namespace, remoteRef)
+ if err != nil {
+ err = fmt.Errorf("error processing spec.dataFrom[%d].sourceRef.generatorRef, err: %w", i, err)
+ }
}
+
if errors.Is(err, esv1beta1.NoSecretErr) && externalSecret.Spec.Target.DeletionPolicy != esv1beta1.DeletionPolicyRetain {
- r.recorder.Event(
- externalSecret,
- v1.EventTypeNormal,
- esv1beta1.ReasonDeleted,
- fmt.Sprintf("secret does not exist at provider using .dataFrom[%d]", i),
- )
+ r.recorder.Eventf(externalSecret, v1.EventTypeNormal, esv1beta1.ReasonMissingProviderSecret, eventMissingProviderSecret, i)
continue
}
if err != nil {
return nil, err
}
+
providerData = utils.MergeByteMap(providerData, secretMap)
}
for i, secretRef := range externalSecret.Spec.Data {
- err := r.handleSecretData(ctx, i, *externalSecret, secretRef, providerData, mgr)
+ err := r.handleSecretData(ctx, *externalSecret, secretRef, providerData, mgr)
if errors.Is(err, esv1beta1.NoSecretErr) && externalSecret.Spec.Target.DeletionPolicy != esv1beta1.DeletionPolicyRetain {
- r.recorder.Event(externalSecret, v1.EventTypeNormal, esv1beta1.ReasonDeleted, fmt.Sprintf("secret does not exist at provider using .data[%d] key=%s", i, secretRef.RemoteRef.Key))
+ r.recorder.Eventf(externalSecret, v1.EventTypeNormal, esv1beta1.ReasonMissingProviderSecret, eventMissingProviderSecretKey, i, secretRef.RemoteRef.Key)
continue
}
if err != nil {
- return nil, fmt.Errorf("error retrieving secret at .data[%d], key: %s, err: %w", i, secretRef.RemoteRef.Key, err)
+ return nil, fmt.Errorf("error processing spec.data[%d] (key: %s), err: %w", i, secretRef.RemoteRef.Key, err)
}
}
return providerData, nil
}
-func (r *Reconciler) handleSecretData(ctx context.Context, i int, externalSecret esv1beta1.ExternalSecret, secretRef esv1beta1.ExternalSecretData, providerData map[string][]byte, cmgr *secretstore.Manager) error {
+func (r *Reconciler) handleSecretData(ctx context.Context, externalSecret esv1beta1.ExternalSecret, secretRef esv1beta1.ExternalSecretData, providerData map[string][]byte, cmgr *secretstore.Manager) error {
client, err := cmgr.Get(ctx, externalSecret.Spec.SecretStoreRef, externalSecret.Namespace, toStoreGenSourceRef(secretRef.SourceRef))
if err != nil {
return err
}
+
+ // get a single secret from the store
secretData, err := client.GetSecret(ctx, secretRef.RemoteRef)
if err != nil {
return err
}
+
+ // decode the secret if needed
secretData, err = utils.Decode(secretRef.RemoteRef.DecodingStrategy, secretData)
if err != nil {
- return fmt.Errorf(errDecode, "spec.data", i, err)
+ return fmt.Errorf(errDecode, secretRef.RemoteRef.DecodingStrategy, err)
}
+
+ // store the secret data
providerData[secretRef.SecretKey] = secretData
+
return nil
}
@@ -115,128 +123,106 @@ func toStoreGenSourceRef(ref *esv1beta1.StoreSourceRef) *esv1beta1.StoreGenerato
}
}
-func (r *Reconciler) handleGenerateSecrets(ctx context.Context, namespace string, remoteRef esv1beta1.ExternalSecretDataFromRemoteRef, i int) (map[string][]byte, error) {
- genDef, err := r.getGeneratorDefinition(ctx, namespace, remoteRef.SourceRef.GeneratorRef)
+func (r *Reconciler) handleGenerateSecrets(ctx context.Context, namespace string, remoteRef esv1beta1.ExternalSecretDataFromRemoteRef) (map[string][]byte, error) {
+ gen, obj, err := resolvers.GeneratorRef(ctx, r.Client, r.Scheme, namespace, remoteRef.SourceRef.GeneratorRef)
if err != nil {
return nil, err
}
- gen, err := genv1alpha1.GetGenerator(genDef)
- if err != nil {
- return nil, err
- }
- secretMap, err := gen.Generate(ctx, genDef, r.Client, namespace)
+
+ // use the generator
+ secretMap, err := gen.Generate(ctx, obj, r.Client, namespace)
if err != nil {
- return nil, fmt.Errorf(errGenerate, i, err)
+ return nil, fmt.Errorf(errGenerate, err)
}
+
+ // rewrite the keys if needed
secretMap, err = utils.RewriteMap(remoteRef.Rewrite, secretMap)
if err != nil {
- return nil, fmt.Errorf(errRewrite, i, err)
+ return nil, fmt.Errorf(errRewrite, err)
}
- if !utils.ValidateKeys(secretMap) {
- return nil, fmt.Errorf(errInvalidKeys, "generator", i)
- }
- return secretMap, err
-}
-// getGeneratorDefinition returns the generator JSON for a given sourceRef
-// when it uses a generatorRef it fetches the resource and returns the JSON.
-func (r *Reconciler) getGeneratorDefinition(ctx context.Context, namespace string, generatorRef *esv1beta1.GeneratorRef) (*apiextensions.JSON, error) {
- // client-go dynamic client needs a GVR to fetch the resource
- // But we only have the GVK in our generatorRef.
- //
- // TODO: there is no need to discover the GroupVersionResource
- // this should be cached.
- c := discovery.NewDiscoveryClientForConfigOrDie(r.RestConfig)
- groupResources, err := restmapper.GetAPIGroupResources(c)
+ // validate the keys
+ err = utils.ValidateKeys(secretMap)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf(errInvalidKeys, err)
}
- gv, err := schema.ParseGroupVersion(generatorRef.APIVersion)
- if err != nil {
- return nil, err
- }
- mapper := restmapper.NewDiscoveryRESTMapper(groupResources)
- mapping, err := mapper.RESTMapping(schema.GroupKind{
- Group: gv.Group,
- Kind: generatorRef.Kind,
- })
- if err != nil {
- return nil, err
- }
- d, err := dynamic.NewForConfig(r.RestConfig)
- if err != nil {
- return nil, err
- }
- res, err := d.Resource(mapping.Resource).
- Namespace(namespace).
- Get(ctx, generatorRef.Name, metav1.GetOptions{})
- if err != nil {
- return nil, err
- }
- jsonRes, err := res.MarshalJSON()
- if err != nil {
- return nil, err
- }
- return &apiextensions.JSON{Raw: jsonRes}, nil
+ return secretMap, err
}
-func (r *Reconciler) handleExtractSecrets(ctx context.Context, externalSecret *esv1beta1.ExternalSecret, remoteRef esv1beta1.ExternalSecretDataFromRemoteRef, cmgr *secretstore.Manager, i int) (map[string][]byte, error) {
+func (r *Reconciler) handleExtractSecrets(ctx context.Context, externalSecret *esv1beta1.ExternalSecret, remoteRef esv1beta1.ExternalSecretDataFromRemoteRef, cmgr *secretstore.Manager) (map[string][]byte, error) {
client, err := cmgr.Get(ctx, externalSecret.Spec.SecretStoreRef, externalSecret.Namespace, remoteRef.SourceRef)
if err != nil {
return nil, err
}
+
+ // get multiple secrets from the store
secretMap, err := client.GetSecretMap(ctx, *remoteRef.Extract)
if err != nil {
return nil, err
}
+
+ // rewrite the keys if needed
secretMap, err = utils.RewriteMap(remoteRef.Rewrite, secretMap)
if err != nil {
- return nil, fmt.Errorf(errRewrite, i, err)
+ return nil, fmt.Errorf(errRewrite, err)
}
if len(remoteRef.Rewrite) == 0 {
secretMap, err = utils.ConvertKeys(remoteRef.Extract.ConversionStrategy, secretMap)
if err != nil {
- return nil, fmt.Errorf(errConvert, err)
+ return nil, fmt.Errorf(errConvert, remoteRef.Extract.ConversionStrategy, err)
}
}
- if !utils.ValidateKeys(secretMap) {
- return nil, fmt.Errorf(errInvalidKeys, "extract", i)
+
+ // validate the keys
+ err = utils.ValidateKeys(secretMap)
+ if err != nil {
+ return nil, fmt.Errorf(errInvalidKeys, err)
}
+
+ // decode the secrets if needed
secretMap, err = utils.DecodeMap(remoteRef.Extract.DecodingStrategy, secretMap)
if err != nil {
- return nil, fmt.Errorf(errDecode, "spec.dataFrom", i, err)
+ return nil, fmt.Errorf(errDecode, remoteRef.Extract.DecodingStrategy, err)
}
+
return secretMap, err
}
-func (r *Reconciler) handleFindAllSecrets(ctx context.Context, externalSecret *esv1beta1.ExternalSecret, remoteRef esv1beta1.ExternalSecretDataFromRemoteRef, cmgr *secretstore.Manager, i int) (map[string][]byte, error) {
+func (r *Reconciler) handleFindAllSecrets(ctx context.Context, externalSecret *esv1beta1.ExternalSecret, remoteRef esv1beta1.ExternalSecretDataFromRemoteRef, cmgr *secretstore.Manager) (map[string][]byte, error) {
client, err := cmgr.Get(ctx, externalSecret.Spec.SecretStoreRef, externalSecret.Namespace, remoteRef.SourceRef)
if err != nil {
return nil, err
}
+
+ // get all secrets from the store that match the selector
secretMap, err := client.GetAllSecrets(ctx, *remoteRef.Find)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("error getting all secrets: %w", err)
}
+
+ // rewrite the keys if needed
secretMap, err = utils.RewriteMap(remoteRef.Rewrite, secretMap)
if err != nil {
- return nil, fmt.Errorf(errRewrite, i, err)
+ return nil, fmt.Errorf(errRewrite, err)
}
if len(remoteRef.Rewrite) == 0 {
- // ConversionStrategy is deprecated. Use RewriteMap instead.
- r.recorder.Event(externalSecret, v1.EventTypeWarning, esv1beta1.ReasonDeprecated, fmt.Sprintf("dataFrom[%d].find.conversionStrategy=%v is deprecated and will be removed in further releases. Use dataFrom.rewrite instead", i, remoteRef.Find.ConversionStrategy))
secretMap, err = utils.ConvertKeys(remoteRef.Find.ConversionStrategy, secretMap)
if err != nil {
- return nil, fmt.Errorf(errConvert, err)
+ return nil, fmt.Errorf(errConvert, remoteRef.Find.ConversionStrategy, err)
}
}
- if !utils.ValidateKeys(secretMap) {
- return nil, fmt.Errorf(errInvalidKeys, "find", i)
+
+ // validate the keys
+ err = utils.ValidateKeys(secretMap)
+ if err != nil {
+ return nil, fmt.Errorf(errInvalidKeys, err)
}
+
+ // decode the secrets if needed
secretMap, err = utils.DecodeMap(remoteRef.Find.DecodingStrategy, secretMap)
if err != nil {
- return nil, fmt.Errorf(errDecode, "spec.dataFrom", i, err)
+ return nil, fmt.Errorf(errDecode, remoteRef.Find.DecodingStrategy, err)
}
return secretMap, err
}
diff --git a/pkg/controllers/externalsecret/externalsecret_controller_template.go b/pkg/controllers/externalsecret/externalsecret_controller_template.go
index fa540a64540..cbbbf28547d 100644
--- a/pkg/controllers/externalsecret/externalsecret_controller_template.go
+++ b/pkg/controllers/externalsecret/externalsecret_controller_template.go
@@ -17,6 +17,7 @@ package externalsecret
import (
"context"
"fmt"
+ "maps"
v1 "k8s.io/api/core/v1"
@@ -30,24 +31,37 @@ import (
// merge template in the following order:
// * template.Data (highest precedence)
-// * template.templateFrom
-// * secret via es.data or es.dataFrom.
+// * template.TemplateFrom
+// * secret via es.data or es.dataFrom (if template.MergePolicy is Merge, or there is no template)
+// * existing secret keys (if CreationPolicy is Merge).
func (r *Reconciler) applyTemplate(ctx context.Context, es *esv1beta1.ExternalSecret, secret *v1.Secret, dataMap map[string][]byte) error {
+ // update metadata (labels, annotations) of the secret
if err := setMetadata(secret, es); err != nil {
return err
}
+ // we only keep existing keys if creation policy is Merge, otherwise we clear the secret
+ if es.Spec.Target.CreationPolicy != esv1beta1.CreatePolicyMerge {
+ secret.Data = make(map[string][]byte)
+ }
+
// no template: copy data and return
if es.Spec.Target.Template == nil {
- secret.Data = dataMap
+ maps.Insert(secret.Data, maps.All(dataMap))
return nil
}
- // Merge Policy should merge secrets
- if es.Spec.Target.Template.MergePolicy == esv1beta1.MergePolicyMerge {
- for k, v := range dataMap {
- secret.Data[k] = v
- }
+
+ // set the secret type if it is defined in the template, otherwise keep the existing type
+ if es.Spec.Target.Template.Type != "" {
+ secret.Type = es.Spec.Target.Template.Type
}
+
+ // when TemplateMergePolicy is Merge, or there is no data template, we include the keys from `dataMap`
+ noTemplate := len(es.Spec.Target.Template.Data) == 0 && len(es.Spec.Target.Template.TemplateFrom) == 0
+ if es.Spec.Target.Template.MergePolicy == esv1beta1.MergePolicyMerge || noTemplate {
+ maps.Insert(secret.Data, maps.All(dataMap))
+ }
+
execute, err := template.EngineForVersion(es.Spec.Target.Template.EngineVersion)
if err != nil {
return err
@@ -59,45 +73,50 @@ func (r *Reconciler) applyTemplate(ctx context.Context, es *esv1beta1.ExternalSe
DataMap: dataMap,
Exec: execute,
}
+
// apply templates defined in template.templateFrom
err = p.MergeTemplateFrom(ctx, es.Namespace, es.Spec.Target.Template)
if err != nil {
return fmt.Errorf(errFetchTplFrom, err)
}
- // explicitly defined template.Data takes precedence over templateFrom
+
+ // apply data templates
+ // NOTE: explicitly defined template.data templates take precedence over templateFrom
err = p.MergeMap(es.Spec.Target.Template.Data, esv1beta1.TemplateTargetData)
if err != nil {
return fmt.Errorf(errExecTpl, err)
}
- // get template data for labels
+ // apply templates for labels
+ // NOTE: this only works for v2 templates
err = p.MergeMap(es.Spec.Target.Template.Metadata.Labels, esv1beta1.TemplateTargetLabels)
if err != nil {
return fmt.Errorf(errExecTpl, err)
}
- // get template data for annotations
+
+ // apply template for annotations
+ // NOTE: this only works for v2 templates
err = p.MergeMap(es.Spec.Target.Template.Metadata.Annotations, esv1beta1.TemplateTargetAnnotations)
if err != nil {
return fmt.Errorf(errExecTpl, err)
}
- // if no data was provided by template fallback
- // to value from the provider
- if len(es.Spec.Target.Template.Data) == 0 && len(es.Spec.Target.Template.TemplateFrom) == 0 {
- secret.Data = dataMap
- }
+
return nil
}
// setMetadata sets Labels and Annotations to the given secret.
func setMetadata(secret *v1.Secret, es *esv1beta1.ExternalSecret) error {
+ // ensure that Labels and Annotations are not nil
+ // so it is safe to merge them
if secret.Labels == nil {
secret.Labels = make(map[string]string)
}
if secret.Annotations == nil {
secret.Annotations = make(map[string]string)
}
- // Clean up Labels and Annotations added by the operator
- // so that it won't leave outdated ones
+
+ // remove any existing labels managed by this external secret
+ // this is to ensure that we don't have any stale labels
labelKeys, err := templating.GetManagedLabelKeys(secret, es.Name)
if err != nil {
return err
@@ -105,7 +124,6 @@ func setMetadata(secret *v1.Secret, es *esv1beta1.ExternalSecret) error {
for _, key := range labelKeys {
delete(secret.ObjectMeta.Labels, key)
}
-
annotationKeys, err := templating.GetManagedAnnotationKeys(secret, es.Name)
if err != nil {
return err
@@ -114,13 +132,14 @@ func setMetadata(secret *v1.Secret, es *esv1beta1.ExternalSecret) error {
delete(secret.ObjectMeta.Annotations, key)
}
+ // if no template is defined, copy labels and annotations from the ExternalSecret
if es.Spec.Target.Template == nil {
utils.MergeStringMap(secret.ObjectMeta.Labels, es.ObjectMeta.Labels)
utils.MergeStringMap(secret.ObjectMeta.Annotations, es.ObjectMeta.Annotations)
return nil
}
- secret.Type = es.Spec.Target.Template.Type
+ // copy labels and annotations from the template
utils.MergeStringMap(secret.ObjectMeta.Labels, es.Spec.Target.Template.Metadata.Labels)
utils.MergeStringMap(secret.ObjectMeta.Annotations, es.Spec.Target.Template.Metadata.Annotations)
return nil
diff --git a/pkg/controllers/externalsecret/externalsecret_controller_test.go b/pkg/controllers/externalsecret/externalsecret_controller_test.go
index e3c07848615..44e8f8fc6e9 100644
--- a/pkg/controllers/externalsecret/externalsecret_controller_test.go
+++ b/pkg/controllers/externalsecret/externalsecret_controller_test.go
@@ -18,6 +18,7 @@ import (
"bytes"
"context"
"encoding/json"
+ "errors"
"fmt"
"os"
"strconv"
@@ -25,6 +26,7 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
+ "github.com/onsi/gomega/format"
"github.com/prometheus/client_golang/prometheus"
dto "github.com/prometheus/client_model/go"
v1 "k8s.io/api/core/v1"
@@ -46,6 +48,15 @@ import (
. "github.com/onsi/gomega"
)
+const (
+ labelKey = "label-key"
+ labelValue = "label-value"
+ annotationKey = "annotation-key"
+ annotationValue = "annotation-value"
+ existingLabelKey = "existing-label-key"
+ existingLabelValue = "existing-label-value"
+)
+
var (
fakeProvider *fake.Client
metric dto.Metric
@@ -88,30 +99,23 @@ var _ = Describe("Kind=secret existence logic", func() {
}
type testCase struct {
Name string
- Input v1.Secret
+ Input *v1.Secret
ExpectedOutput bool
}
tests := []testCase{
{
Name: "Should not be valid in case of missing uid",
- Input: v1.Secret{},
+ Input: &v1.Secret{},
ExpectedOutput: false,
},
{
Name: "A nil annotation should not be valid",
- Input: v1.Secret{
+ Input: &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
- UID: "xxx",
- Annotations: map[string]string{},
- },
- },
- ExpectedOutput: false,
- },
- {
- Name: "A nil annotation should not be valid",
- Input: v1.Secret{
- ObjectMeta: metav1.ObjectMeta{
- UID: "xxx",
+ UID: "xxx",
+ Labels: map[string]string{
+ esv1beta1.LabelManaged: esv1beta1.LabelManagedValue,
+ },
Annotations: map[string]string{},
},
},
@@ -119,9 +123,12 @@ var _ = Describe("Kind=secret existence logic", func() {
},
{
Name: "An invalid annotation hash should not be valid",
- Input: v1.Secret{
+ Input: &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
UID: "xxx",
+ Labels: map[string]string{
+ esv1beta1.LabelManaged: esv1beta1.LabelManagedValue,
+ },
Annotations: map[string]string{
esv1beta1.AnnotationDataHash: "xxxxxx",
},
@@ -130,10 +137,13 @@ var _ = Describe("Kind=secret existence logic", func() {
ExpectedOutput: false,
},
{
- Name: "A valid config map should return true",
- Input: v1.Secret{
+ Name: "A valid secret should return true",
+ Input: &v1.Secret{
ObjectMeta: metav1.ObjectMeta{
UID: "xxx",
+ Labels: map[string]string{
+ esv1beta1.LabelManaged: esv1beta1.LabelManagedValue,
+ },
Annotations: map[string]string{
esv1beta1.AnnotationDataHash: utils.ObjectHash(validData),
},
@@ -319,16 +329,16 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
// should be copied over to the Kind=Secret
syncLabelsAnnotations := func(tc *testCase) {
tc.externalSecret.ObjectMeta.Labels = map[string]string{
- "label-key": "label-value",
+ labelKey: labelValue,
}
tc.externalSecret.ObjectMeta.Annotations = map[string]string{
- "annotation-key": "annotation-value",
+ annotationKey: annotationValue,
}
fakeProvider.WithGetSecret([]byte(secretVal), nil)
tc.checkSecret = func(es *esv1beta1.ExternalSecret, secret *v1.Secret) {
- Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue("label-key", "label-value"))
- Expect(secret.ObjectMeta.Annotations).To(HaveKeyWithValue("annotation-key", "annotation-value"))
+ Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue(labelKey, labelValue))
+ Expect(secret.ObjectMeta.Annotations).To(HaveKeyWithValue(annotationKey, annotationValue))
// ownerRef must not be set!
Expect(ctest.HasOwnerRef(secret.ObjectMeta, "ExternalSecret", ExternalSecretName)).To(BeTrue())
@@ -339,10 +349,10 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
// should be merged to the Secret if exists
mergeLabelsAnnotations := func(tc *testCase) {
tc.externalSecret.ObjectMeta.Labels = map[string]string{
- "label-key": "label-value",
+ labelKey: labelValue,
}
tc.externalSecret.ObjectMeta.Annotations = map[string]string{
- "annotation-key": "annotation-value",
+ annotationKey: annotationValue,
}
fakeProvider.WithGetSecret([]byte(secretVal), nil)
// Create a secret owned by another entity to test if the pre-existing metadata is preserved
@@ -351,7 +361,7 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
Name: ExternalSecretTargetSecretName,
Namespace: ExternalSecretNamespace,
Labels: map[string]string{
- "existing-label-key": "existing-label-value",
+ existingLabelKey: existingLabelValue,
},
Annotations: map[string]string{
"existing-annotation-key": "existing-annotation-value",
@@ -360,19 +370,19 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
}, client.FieldOwner(FakeManager))).To(Succeed())
tc.checkSecret = func(es *esv1beta1.ExternalSecret, secret *v1.Secret) {
- Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue("label-key", "label-value"))
- Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue("existing-label-key", "existing-label-value"))
- Expect(secret.ObjectMeta.Annotations).To(HaveKeyWithValue("annotation-key", "annotation-value"))
+ Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue(labelKey, labelValue))
+ Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue(existingLabelKey, existingLabelValue))
+ Expect(secret.ObjectMeta.Annotations).To(HaveKeyWithValue(annotationKey, annotationValue))
Expect(secret.ObjectMeta.Annotations).To(HaveKeyWithValue("existing-annotation-key", "existing-annotation-value"))
}
}
removeOutdatedLabelsAnnotations := func(tc *testCase) {
tc.externalSecret.ObjectMeta.Labels = map[string]string{
- "label-key": "label-value",
+ labelKey: labelValue,
}
tc.externalSecret.ObjectMeta.Annotations = map[string]string{
- "annotation-key": "annotation-value",
+ annotationKey: annotationValue,
}
fakeProvider.WithGetSecret([]byte(secretVal), nil)
// Create a secret owned by the operator to test if the outdated pre-existing metadata is removed
@@ -381,7 +391,7 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
Name: ExternalSecretTargetSecretName,
Namespace: ExternalSecretNamespace,
Labels: map[string]string{
- "existing-label-key": "existing-label-value",
+ existingLabelKey: existingLabelValue,
},
Annotations: map[string]string{
"existing-annotation-key": "existing-annotation-value",
@@ -390,9 +400,9 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
}, client.FieldOwner(ExternalSecretFQDN))).To(Succeed())
tc.checkSecret = func(es *esv1beta1.ExternalSecret, secret *v1.Secret) {
- Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue("label-key", "label-value"))
- Expect(secret.ObjectMeta.Labels).NotTo(HaveKeyWithValue("existing-label-key", "existing-label-value"))
- Expect(secret.ObjectMeta.Annotations).To(HaveKeyWithValue("annotation-key", "annotation-value"))
+ Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue(labelKey, labelValue))
+ Expect(secret.ObjectMeta.Labels).NotTo(HaveKeyWithValue(existingLabelKey, existingLabelValue))
+ Expect(secret.ObjectMeta.Annotations).To(HaveKeyWithValue(annotationKey, annotationValue))
Expect(secret.ObjectMeta.Annotations).NotTo(HaveKeyWithValue("existing-annotation-key", "existing-annotation-value"))
}
}
@@ -431,7 +441,7 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
Name: ExternalSecretTargetSecretName,
Namespace: ExternalSecretNamespace,
Labels: map[string]string{
- "existing-label-key": "existing-label-value",
+ existingLabelKey: existingLabelValue,
},
Annotations: map[string]string{
"existing-annotation-key": "existing-annotation-value",
@@ -448,9 +458,10 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
Expect(string(secret.Data[existingKey])).To(Equal(existingVal))
Expect(string(secret.Data[targetProp])).To(Equal(secretVal))
- Expect(secret.ObjectMeta.Labels).To(HaveLen(2))
- Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue("existing-label-key", "existing-label-value"))
+ Expect(secret.ObjectMeta.Labels).To(HaveLen(3))
+ Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue(existingLabelKey, existingLabelValue))
Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue("es-label-key", "es-label-value"))
+ Expect(secret.ObjectMeta.Labels).To(HaveKeyWithValue(esv1beta1.LabelManaged, esv1beta1.LabelManagedValue))
Expect(secret.ObjectMeta.Annotations).To(HaveLen(3))
Expect(secret.ObjectMeta.Annotations).To(HaveKeyWithValue("existing-annotation-key", "existing-annotation-value"))
@@ -459,12 +470,48 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
Expect(ctest.HasOwnerRef(secret.ObjectMeta, "ExternalSecret", ExternalSecretFQDN)).To(BeFalse())
Expect(secret.ObjectMeta.ManagedFields).To(HaveLen(2))
- Expect(ctest.HasFieldOwnership(
- secret.ObjectMeta,
- ExternalSecretFQDN,
- fmt.Sprintf(`{"f:data":{"f:targetProperty":{}},"f:immutable":{},"f:metadata":{"f:annotations":{"f:es-annotation-key":{},"f:%s":{}},"f:labels":{"f:es-label-key":{}}}}`, esv1beta1.AnnotationDataHash)),
- ).To(BeEmpty())
- Expect(ctest.HasFieldOwnership(secret.ObjectMeta, FakeManager, `{"f:data":{".":{},"f:pre-existing-key":{}},"f:metadata":{"f:annotations":{".":{},"f:existing-annotation-key":{}},"f:labels":{".":{},"f:existing-label-key":{}}},"f:type":{}}`)).To(BeEmpty())
+ oldCharactersAroundMismatchToInclude := format.CharactersAroundMismatchToInclude
+ format.CharactersAroundMismatchToInclude = 10
+ Expect(ctest.FirstManagedFieldForManager(secret.ObjectMeta, ExternalSecretFQDN)).To(
+ Equal(fmt.Sprintf(`{"f:data":{"f:targetProperty":{}},"f:metadata":{"f:annotations":{"f:es-annotation-key":{},"f:%s":{}},"f:labels":{"f:es-label-key":{},"f:%s":{}}}}`, esv1beta1.AnnotationDataHash, esv1beta1.LabelManaged)),
+ )
+ Expect(ctest.FirstManagedFieldForManager(secret.ObjectMeta, FakeManager)).To(
+ Equal(`{"f:data":{".":{},"f:pre-existing-key":{}},"f:metadata":{"f:annotations":{".":{},"f:existing-annotation-key":{}},"f:labels":{".":{},"f:existing-label-key":{}}},"f:type":{}}`),
+ )
+ format.CharactersAroundMismatchToInclude = oldCharactersAroundMismatchToInclude
+ }
+ }
+
+ mergeWithSecretUpdate := func(tc *testCase) {
+ const secretVal = "someValue"
+ tc.externalSecret.Spec.Target.CreationPolicy = esv1beta1.CreatePolicyMerge
+ tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Hour}
+
+ Expect(k8sClient.Create(context.Background(), &v1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: ExternalSecretTargetSecretName,
+ Namespace: ExternalSecretNamespace,
+ },
+ Data: map[string][]byte{
+ existingKey: []byte(existingVal),
+ },
+ }, client.FieldOwner(FakeManager))).To(Succeed())
+
+ fakeProvider.WithGetSecret([]byte(secretVal), nil)
+ tc.checkSecret = func(es *esv1beta1.ExternalSecret, secret *v1.Secret) {
+ // Overwrite the secret value to check if the change kicks reconciliation and overwrites it again
+ Expect(k8sClient.Update(context.Background(), &v1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: ExternalSecretTargetSecretName,
+ Namespace: ExternalSecretNamespace,
+ },
+ Data: map[string][]byte{
+ existingKey: []byte("differentValue"),
+ },
+ }, client.FieldOwner(FakeManager))).To(Succeed())
+
+ Expect(string(secret.Data[existingKey])).To(Equal(existingVal))
+ Expect(string(secret.Data[targetProp])).To(Equal(secretVal))
}
}
@@ -514,8 +561,18 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
fakeProvider.WithGetSecret([]byte(secretVal), nil)
tc.checkCondition = func(es *esv1beta1.ExternalSecret) bool {
- cond := GetExternalSecretCondition(es.Status, esv1beta1.ExternalSecretReady)
- if cond == nil || cond.Status != v1.ConditionFalse || cond.Reason != esv1beta1.ConditionReasonSecretSyncedError {
+ expected := []esv1beta1.ExternalSecretStatusCondition{
+ {
+ Type: esv1beta1.ExternalSecretReady,
+ Status: v1.ConditionTrue,
+ Reason: esv1beta1.ConditionReasonSecretMissing,
+ Message: msgMissing,
+ },
+ }
+
+ opts := cmpopts.IgnoreFields(esv1beta1.ExternalSecretStatusCondition{}, "LastTransitionTime")
+ if diff := cmp.Diff(expected, es.Status.Conditions, opts); diff != "" {
+ GinkgoLogr.Info("(-got, +want)\n%s", "diff", diff)
return false
}
return true
@@ -524,10 +581,10 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
Eventually(func() bool {
Expect(testSyncCallsError.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metric)).To(Succeed())
Expect(testExternalSecretReconcileDuration.WithLabelValues(ExternalSecretName, ExternalSecretNamespace).Write(&metricDuration)).To(Succeed())
- return metric.GetCounter().GetValue() >= 2.0 && metricDuration.GetGauge().GetValue() > 0.0
+ return metric.GetCounter().GetValue() == 0 && metricDuration.GetGauge().GetValue() > 0.0
}, timeout, interval).Should(BeTrue())
- Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1beta1.ExternalSecretReady, v1.ConditionFalse, 1.0)).To(BeTrue())
- Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1beta1.ExternalSecretReady, v1.ConditionTrue, 0.0)).To(BeTrue())
+ Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1beta1.ExternalSecretReady, v1.ConditionFalse, 0.0)).To(BeTrue())
+ Expect(externalSecretConditionShouldBe(ExternalSecretName, ExternalSecretNamespace, esv1beta1.ExternalSecretReady, v1.ConditionTrue, 1.0)).To(BeTrue())
}
}
@@ -557,11 +614,12 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
// check owner/managedFields
Expect(ctest.HasOwnerRef(secret.ObjectMeta, "ExternalSecret", ExternalSecretFQDN)).To(BeFalse())
Expect(secret.ObjectMeta.ManagedFields).To(HaveLen(2))
- Expect(ctest.HasFieldOwnership(
- secret.ObjectMeta,
- ExternalSecretFQDN,
- fmt.Sprintf("{\"f:data\":{\"f:targetProperty\":{}},\"f:immutable\":{},\"f:metadata\":{\"f:annotations\":{\"f:%s\":{}}}}", esv1beta1.AnnotationDataHash)),
- ).To(BeEmpty())
+ oldCharactersAroundMismatchToInclude := format.CharactersAroundMismatchToInclude
+ format.CharactersAroundMismatchToInclude = 10
+ Expect(ctest.FirstManagedFieldForManager(secret.ObjectMeta, ExternalSecretFQDN)).To(
+ Equal(fmt.Sprintf(`{"f:data":{"f:targetProperty":{}},"f:metadata":{"f:annotations":{".":{},"f:%s":{}},"f:labels":{".":{},"f:%s":{}}}}`, esv1beta1.AnnotationDataHash, esv1beta1.LabelManaged)),
+ )
+ format.CharactersAroundMismatchToInclude = oldCharactersAroundMismatchToInclude
}
}
@@ -601,6 +659,45 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
Expect(string(secret.Data[secretKey])).To(Equal(secretVal))
}
}
+ syncWithClusterGeneratorRef := func(tc *testCase) {
+ const secretKey = "somekey2"
+ const secretVal = "someValue2"
+ Expect(k8sClient.Create(context.Background(), &genv1alpha1.ClusterGenerator{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "mytestfake",
+ },
+ Spec: genv1alpha1.ClusterGeneratorSpec{
+ Kind: "Fake",
+ Generator: genv1alpha1.GeneratorSpec{
+ FakeSpec: &genv1alpha1.FakeSpec{
+ Data: map[string]string{
+ secretKey: secretVal,
+ },
+ },
+ },
+ },
+ })).To(Succeed())
+
+ // reset secretStoreRef
+ tc.externalSecret.Spec.SecretStoreRef = esv1beta1.SecretStoreRef{}
+ tc.externalSecret.Spec.Data = nil
+ tc.externalSecret.Spec.DataFrom = []esv1beta1.ExternalSecretDataFromRemoteRef{
+ {
+ SourceRef: &esv1beta1.StoreGeneratorSourceRef{
+ GeneratorRef: &esv1beta1.GeneratorRef{
+ APIVersion: genv1alpha1.Group + "/" + genv1alpha1.Version,
+ Kind: "ClusterGenerator",
+ Name: "mytestfake",
+ },
+ },
+ },
+ }
+
+ tc.checkSecret = func(es *esv1beta1.ExternalSecret, secret *v1.Secret) {
+ // check values
+ Expect(string(secret.Data[secretKey])).To(Equal(secretVal))
+ }
+ }
deleteOrphanedSecrets := func(tc *testCase) {
tc.checkSecret = func(es *esv1beta1.ExternalSecret, secret *v1.Secret) {
@@ -1360,7 +1457,7 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
Type: esv1beta1.ExternalSecretReady,
Status: v1.ConditionTrue,
Reason: esv1beta1.ConditionReasonSecretSynced,
- Message: "Secret was synced",
+ Message: msgSyncedRetain,
},
}
@@ -1691,7 +1788,7 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
// a error condition must be set.
providerErrCondition := func(tc *testCase) {
const secretVal = "foobar"
- fakeProvider.WithGetSecret(nil, fmt.Errorf("boom"))
+ fakeProvider.WithGetSecret(nil, errors.New("boom"))
tc.externalSecret.Spec.RefreshInterval = &metav1.Duration{Duration: time.Millisecond * 100}
tc.checkCondition = func(es *esv1beta1.ExternalSecret) bool {
cond := GetExternalSecretCondition(es.Status, esv1beta1.ExternalSecretReady)
@@ -1754,7 +1851,7 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
storeConstructErrCondition := func(tc *testCase) {
fakeProvider.WithNew(func(context.Context, esv1beta1.GenericStore, client.Client,
string) (esv1beta1.SecretsClient, error) {
- return nil, fmt.Errorf("artificial constructor error")
+ return nil, errors.New("artificial constructor error")
})
tc.checkCondition = func(es *esv1beta1.ExternalSecret) bool {
// condition must be false
@@ -1807,7 +1904,7 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
&Reconciler{
ClusterSecretStoreEnabled: false,
},
- *tc.externalSecret,
+ tc.externalSecret,
)).To(BeTrue())
tc.checkCondition = func(es *esv1beta1.ExternalSecret) bool {
@@ -2226,10 +2323,12 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
Entry("should removed outdated labels and annotations", removeOutdatedLabelsAnnotations),
Entry("should set prometheus counters", checkPrometheusCounters),
Entry("should merge with existing secret using creationPolicy=Merge", mergeWithSecret),
+ Entry("should kick reconciliation when secret changes using creationPolicy=Merge", mergeWithSecretUpdate),
Entry("should error if secret doesn't exist when using creationPolicy=Merge", mergeWithSecretErr),
Entry("should not resolve conflicts with creationPolicy=Merge", mergeWithConflict),
Entry("should not update unchanged secret using creationPolicy=Merge", mergeWithSecretNoChange),
Entry("should not delete pre-existing secret with creationPolicy=Orphan", createSecretPolicyOrphan),
+ Entry("should sync cluster generator ref", syncWithClusterGeneratorRef),
Entry("should sync with generatorRef", syncWithGeneratorRef),
Entry("should not process generatorRef with mismatching controller field", ignoreMismatchControllerForGeneratorRef),
Entry("should sync with multiple secret stores via sourceRef", syncWithMultipleSecretStores),
@@ -2280,14 +2379,17 @@ var _ = Describe("ExternalSecret controller", Serial, func() {
var _ = Describe("ExternalSecret refresh logic", func() {
Context("secret refresh", func() {
It("should refresh when resource version does not match", func() {
- Expect(shouldRefresh(esv1beta1.ExternalSecret{
+ Expect(shouldRefresh(&esv1beta1.ExternalSecret{
+ Spec: esv1beta1.ExternalSecretSpec{
+ RefreshInterval: &metav1.Duration{Duration: time.Minute},
+ },
Status: esv1beta1.ExternalSecretStatus{
SyncedResourceVersion: "some resource version",
},
})).To(BeTrue())
})
It("should refresh when labels change", func() {
- es := esv1beta1.ExternalSecret{
+ es := &esv1beta1.ExternalSecret{
ObjectMeta: metav1.ObjectMeta{
Generation: 1,
Labels: map[string]string{
@@ -2311,7 +2413,7 @@ var _ = Describe("ExternalSecret refresh logic", func() {
})
It("should refresh when annotations change", func() {
- es := esv1beta1.ExternalSecret{
+ es := &esv1beta1.ExternalSecret{
ObjectMeta: metav1.ObjectMeta{
Generation: 1,
Annotations: map[string]string{
@@ -2335,12 +2437,12 @@ var _ = Describe("ExternalSecret refresh logic", func() {
})
It("should refresh when generation has changed", func() {
- es := esv1beta1.ExternalSecret{
+ es := &esv1beta1.ExternalSecret{
ObjectMeta: metav1.ObjectMeta{
Generation: 1,
},
Spec: esv1beta1.ExternalSecretSpec{
- RefreshInterval: &metav1.Duration{Duration: 0},
+ RefreshInterval: &metav1.Duration{Duration: time.Minute},
},
Status: esv1beta1.ExternalSecretStatus{
RefreshTime: metav1.Now(),
@@ -2355,7 +2457,7 @@ var _ = Describe("ExternalSecret refresh logic", func() {
})
It("should skip refresh when refreshInterval is 0", func() {
- es := esv1beta1.ExternalSecret{
+ es := &esv1beta1.ExternalSecret{
ObjectMeta: metav1.ObjectMeta{
Generation: 1,
},
@@ -2370,7 +2472,7 @@ var _ = Describe("ExternalSecret refresh logic", func() {
})
It("should refresh when refresh interval has passed", func() {
- es := esv1beta1.ExternalSecret{
+ es := &esv1beta1.ExternalSecret{
ObjectMeta: metav1.ObjectMeta{
Generation: 1,
},
@@ -2387,7 +2489,7 @@ var _ = Describe("ExternalSecret refresh logic", func() {
})
It("should refresh when no refresh time was set", func() {
- es := esv1beta1.ExternalSecret{
+ es := &esv1beta1.ExternalSecret{
ObjectMeta: metav1.ObjectMeta{
Generation: 1,
},
@@ -2467,43 +2569,6 @@ var _ = Describe("ExternalSecret refresh logic", func() {
})
})
-var _ = Describe("Controller Reconcile logic", func() {
- Context("controller reconcile", func() {
- It("should reconcile when resource is not synced", func() {
- Expect(shouldReconcile(esv1beta1.ExternalSecret{
- Status: esv1beta1.ExternalSecretStatus{
- SyncedResourceVersion: "some resource version",
- Conditions: []esv1beta1.ExternalSecretStatusCondition{{Reason: "NotASecretSynced"}},
- },
- })).To(BeTrue())
- })
-
- It("should reconcile when secret isn't immutable", func() {
- Expect(shouldReconcile(esv1beta1.ExternalSecret{
- Spec: esv1beta1.ExternalSecretSpec{
- Target: esv1beta1.ExternalSecretTarget{
- Immutable: false,
- },
- },
- })).To(BeTrue())
- })
-
- It("should not reconcile if secret is immutable and has synced condition", func() {
- Expect(shouldReconcile(esv1beta1.ExternalSecret{
- Spec: esv1beta1.ExternalSecretSpec{
- Target: esv1beta1.ExternalSecretTarget{
- Immutable: true,
- },
- },
- Status: esv1beta1.ExternalSecretStatus{
- SyncedResourceVersion: "some resource version",
- Conditions: []esv1beta1.ExternalSecretStatusCondition{{Reason: "SecretSynced"}},
- },
- })).To(BeFalse())
- })
- })
-})
-
func externalSecretConditionShouldBe(name, ns string, ct esv1beta1.ExternalSecretConditionType, cs v1.ConditionStatus, v float64) bool {
return Eventually(func() float64 {
Expect(testExternalSecretCondition.WithLabelValues(name, ns, string(ct), string(cs)).Write(&metric)).To(Succeed())
diff --git a/pkg/controllers/externalsecret/suite_test.go b/pkg/controllers/externalsecret/suite_test.go
index 8ed93d9dad5..87de93fd739 100644
--- a/pkg/controllers/externalsecret/suite_test.go
+++ b/pkg/controllers/externalsecret/suite_test.go
@@ -21,6 +21,7 @@ import (
"time"
"go.uber.org/zap/zapcore"
+ v1 "k8s.io/api/core/v1"
"k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
@@ -33,6 +34,7 @@ import (
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
+ ctrlcommon "github.com/external-secrets/external-secrets/pkg/controllers/common"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@@ -80,6 +82,17 @@ var _ = BeforeSuite(func() {
Metrics: server.Options{
BindAddress: "0", // avoid port collision when testing
},
+ Client: client.Options{
+ Cache: &client.CacheOptions{
+ // the client creates a ListWatch for resources that are requested with .Get() or .List()
+ // we disable caching in the production code, so we disable it here as well for consistency
+ // see: https://github.com/external-secrets/external-secrets/issues/721
+ DisableFor: []client.Object{
+ &v1.Secret{},
+ &v1.ConfigMap{},
+ },
+ },
+ },
})
Expect(err).ToNot(HaveOccurred())
@@ -89,8 +102,14 @@ var _ = BeforeSuite(func() {
Expect(k8sClient).ToNot(BeNil())
Expect(err).ToNot(HaveOccurred())
+ // by default, we use a separate cached client for secrets that are managed by the controller
+ // so we should test under the same conditions
+ secretClient, err := ctrlcommon.BuildManagedSecretClient(k8sManager)
+ Expect(err).ToNot(HaveOccurred())
+
err = (&Reconciler{
- Client: k8sClient,
+ Client: k8sManager.GetClient(),
+ SecretClient: secretClient,
RestConfig: cfg,
Scheme: k8sManager.GetScheme(),
Log: ctrl.Log.WithName("controllers").WithName("ExternalSecrets"),
@@ -98,6 +117,7 @@ var _ = BeforeSuite(func() {
ClusterSecretStoreEnabled: true,
}).SetupWithManager(k8sManager, controller.Options{
MaxConcurrentReconciles: 1,
+ RateLimiter: ctrlcommon.BuildRateLimiter(),
})
Expect(err).ToNot(HaveOccurred())
diff --git a/pkg/controllers/pushsecret/pushsecret_controller.go b/pkg/controllers/pushsecret/pushsecret_controller.go
index 227d13704a3..f5c562ed542 100644
--- a/pkg/controllers/pushsecret/pushsecret_controller.go
+++ b/pkg/controllers/pushsecret/pushsecret_controller.go
@@ -18,6 +18,7 @@ import (
"context"
"errors"
"fmt"
+ "maps"
"strings"
"time"
@@ -27,9 +28,11 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
+ "k8s.io/client-go/rest"
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
@@ -39,17 +42,22 @@ import (
"github.com/external-secrets/external-secrets/pkg/controllers/secretstore"
"github.com/external-secrets/external-secrets/pkg/provider/util/locks"
"github.com/external-secrets/external-secrets/pkg/utils"
+ "github.com/external-secrets/external-secrets/pkg/utils/resolvers"
+
+ // load generators.
+ _ "github.com/external-secrets/external-secrets/pkg/generator/register"
)
const (
- errFailedGetSecret = "could not get source secret"
- errPatchStatus = "error merging"
- errGetSecretStore = "could not get SecretStore %q, %w"
- errGetClusterSecretStore = "could not get ClusterSecretStore %q, %w"
- errSetSecretFailed = "could not write remote ref %v to target secretstore %v: %v"
- errFailedSetSecret = "set secret failed: %v"
- errConvert = "could not apply conversion strategy to keys: %v"
- pushSecretFinalizer = "pushsecret.externalsecrets.io/finalizer"
+ errFailedGetSecret = "could not get source secret"
+ errPatchStatus = "error merging"
+ errGetSecretStore = "could not get SecretStore %q, %w"
+ errGetClusterSecretStore = "could not get ClusterSecretStore %q, %w"
+ errSetSecretFailed = "could not write remote ref %v to target secretstore %v: %v"
+ errFailedSetSecret = "set secret failed: %v"
+ errConvert = "could not apply conversion strategy to keys: %v"
+ pushSecretFinalizer = "pushsecret.externalsecrets.io/finalizer"
+ errCloudNotUpdateFinalizer = "could not update finalizers: %w"
)
type Reconciler struct {
@@ -57,14 +65,16 @@ type Reconciler struct {
Log logr.Logger
Scheme *runtime.Scheme
recorder record.EventRecorder
+ RestConfig *rest.Config
RequeueInterval time.Duration
ControllerClass string
}
-func (r *Reconciler) SetupWithManager(mgr ctrl.Manager) error {
+func (r *Reconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options) error {
r.recorder = mgr.GetEventRecorderFor("pushsecret")
return ctrl.NewControllerManagedBy(mgr).
+ WithOptions(opts).
For(&esapi.PushSecret{}).
Complete(r)
}
@@ -112,7 +122,7 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
if !controllerutil.ContainsFinalizer(&ps, pushSecretFinalizer) {
controllerutil.AddFinalizer(&ps, pushSecretFinalizer)
if err := r.Client.Update(ctx, &ps, &client.UpdateOptions{}); err != nil {
- return ctrl.Result{}, fmt.Errorf("could not update finalizers: %w", err)
+ return ctrl.Result{}, fmt.Errorf(errCloudNotUpdateFinalizer, err)
}
return ctrl.Result{}, nil
@@ -130,17 +140,23 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
controllerutil.RemoveFinalizer(&ps, pushSecretFinalizer)
if err := r.Client.Update(ctx, &ps, &client.UpdateOptions{}); err != nil {
- return ctrl.Result{}, fmt.Errorf("could not update finalizers: %w", err)
+ return ctrl.Result{}, fmt.Errorf(errCloudNotUpdateFinalizer, err)
}
return ctrl.Result{}, nil
}
}
case esapi.PushSecretDeletionPolicyNone:
+ if controllerutil.ContainsFinalizer(&ps, pushSecretFinalizer) {
+ controllerutil.RemoveFinalizer(&ps, pushSecretFinalizer)
+ if err := r.Client.Update(ctx, &ps, &client.UpdateOptions{}); err != nil {
+ return ctrl.Result{}, fmt.Errorf(errCloudNotUpdateFinalizer, err)
+ }
+ }
default:
}
- secret, err := r.GetSecret(ctx, ps)
+ secret, err := r.resolveSecret(ctx, ps)
if err != nil {
r.markAsFailed(errFailedGetSecret, &ps, nil)
@@ -157,6 +173,16 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
return ctrl.Result{}, err
}
+ secretStores, err = removeUnmanagedStores(ctx, req.Namespace, r, secretStores)
+ if err != nil {
+ r.markAsFailed(err.Error(), &ps, nil)
+ return ctrl.Result{}, err
+ }
+ // if no stores are managed by this controller
+ if len(secretStores) == 0 {
+ return ctrl.Result{}, nil
+ }
+
syncedSecrets, err := r.PushSecretToProviders(ctx, secretStores, ps, secret, mgr)
if err != nil {
if errors.Is(err, locks.ErrConflict) {
@@ -218,9 +244,7 @@ func mergeSecretState(newMap, old esapi.SyncedPushSecretsMap) esapi.SyncedPushSe
if !ok {
out[k] = make(map[string]esapi.PushSecretData)
}
- for kk, vv := range v {
- out[k][kk] = vv
- }
+ maps.Insert(out[k], maps.All(v))
}
return out
}
@@ -331,14 +355,38 @@ func secretKeyExists(key string, secret *v1.Secret) bool {
return key == "" || ok
}
-func (r *Reconciler) GetSecret(ctx context.Context, ps esapi.PushSecret) (*v1.Secret, error) {
- secretName := types.NamespacedName{Name: ps.Spec.Selector.Secret.Name, Namespace: ps.Namespace}
- secret := &v1.Secret{}
- err := r.Client.Get(ctx, secretName, secret)
+func (r *Reconciler) resolveSecret(ctx context.Context, ps esapi.PushSecret) (*v1.Secret, error) {
+ if ps.Spec.Selector.Secret != nil {
+ secretName := types.NamespacedName{Name: ps.Spec.Selector.Secret.Name, Namespace: ps.Namespace}
+ secret := &v1.Secret{}
+ err := r.Client.Get(ctx, secretName, secret)
+ if err != nil {
+ return nil, err
+ }
+ return secret, nil
+ }
+ if ps.Spec.Selector.GeneratorRef != nil {
+ return r.resolveSecretFromGenerator(ctx, ps.Namespace, ps.Spec.Selector.GeneratorRef)
+ }
+ return nil, errors.New("no secret selector provided")
+}
+
+func (r *Reconciler) resolveSecretFromGenerator(ctx context.Context, namespace string, generatorRef *v1beta1.GeneratorRef) (*v1.Secret, error) {
+ gen, obj, err := resolvers.GeneratorRef(ctx, r.Client, r.Scheme, namespace, generatorRef)
+ if err != nil {
+ return nil, fmt.Errorf("unable to resolve generator: %w", err)
+ }
+ secretMap, err := gen.Generate(ctx, obj, r.Client, namespace)
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("unable to generate: %w", err)
}
- return secret, nil
+ return &v1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "___generated-secret",
+ Namespace: namespace,
+ },
+ Data: secretMap,
+ }, err
}
func (r *Reconciler) GetSecretStores(ctx context.Context, ps esapi.PushSecret) (map[esapi.PushSecretStoreRef]v1beta1.GenericStore, error) {
@@ -389,7 +437,7 @@ func (r *Reconciler) GetSecretStores(ctx context.Context, ps esapi.PushSecret) (
func (r *Reconciler) getSecretStoreFromName(ctx context.Context, refStore esapi.PushSecretStoreRef, ns string) (v1beta1.GenericStore, error) {
if refStore.Name == "" {
- return nil, fmt.Errorf("refStore Name must be provided")
+ return nil, errors.New("refStore Name must be provided")
}
ref := types.NamespacedName{
Name: refStore.Name,
@@ -465,3 +513,32 @@ func statusRef(ref v1beta1.PushSecretData) string {
}
return ref.GetRemoteKey()
}
+
+// removeUnmanagedStores iterates over all SecretStore references and evaluates the controllerClass property.
+// Returns a map containing only managed stores.
+func removeUnmanagedStores(ctx context.Context, namespace string, r *Reconciler, ss map[esapi.PushSecretStoreRef]v1beta1.GenericStore) (map[esapi.PushSecretStoreRef]v1beta1.GenericStore, error) {
+ for ref := range ss {
+ var store v1beta1.GenericStore
+ switch ref.Kind {
+ case v1beta1.SecretStoreKind:
+ store = &v1beta1.SecretStore{}
+ case v1beta1.ClusterSecretStoreKind:
+ store = &v1beta1.ClusterSecretStore{}
+ namespace = ""
+ }
+ err := r.Client.Get(ctx, types.NamespacedName{
+ Name: ref.Name,
+ Namespace: namespace,
+ }, store)
+
+ if err != nil {
+ return ss, err
+ }
+
+ class := store.GetSpec().Controller
+ if class != "" && class != r.ControllerClass {
+ delete(ss, ref)
+ }
+ }
+ return ss, nil
+}
diff --git a/pkg/controllers/pushsecret/pushsecret_controller_test.go b/pkg/controllers/pushsecret/pushsecret_controller_test.go
index 7ce34f8adf8..263307c9236 100644
--- a/pkg/controllers/pushsecret/pushsecret_controller_test.go
+++ b/pkg/controllers/pushsecret/pushsecret_controller_test.go
@@ -17,6 +17,7 @@ package pushsecret
import (
"bytes"
"context"
+ "errors"
"fmt"
"os"
"strconv"
@@ -29,6 +30,7 @@ import (
"github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
"github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
ctest "github.com/external-secrets/external-secrets/pkg/controllers/commontest"
"github.com/external-secrets/external-secrets/pkg/controllers/pushsecret/psmetrics"
"github.com/external-secrets/external-secrets/pkg/provider/testing/fake"
@@ -44,10 +46,14 @@ var (
)
type testCase struct {
- store v1beta1.GenericStore
- pushsecret *v1alpha1.PushSecret
- secret *v1.Secret
- assert func(pushsecret *v1alpha1.PushSecret, secret *v1.Secret) bool
+ store v1beta1.GenericStore
+ managedStore1 v1beta1.GenericStore
+ managedStore2 v1beta1.GenericStore
+ unmanagedStore1 v1beta1.GenericStore
+ unmanagedStore2 v1beta1.GenericStore
+ pushsecret *v1alpha1.PushSecret
+ secret *v1.Secret
+ assert func(pushsecret *v1alpha1.PushSecret, secret *v1.Secret) bool
}
func init() {
@@ -59,6 +65,7 @@ func init() {
}
func checkCondition(status v1alpha1.PushSecretStatus, cond v1alpha1.PushSecretStatusCondition) bool {
+ fmt.Printf("status: %+v\ncond: %+v\n", status.Conditions, cond)
for _, condition := range status.Conditions {
if condition.Message == cond.Message &&
condition.Reason == cond.Reason &&
@@ -72,9 +79,9 @@ func checkCondition(status v1alpha1.PushSecretStatus, cond v1alpha1.PushSecretSt
type testTweaks func(*testCase)
-var _ = Describe("ExternalSecret controller", func() {
+var _ = Describe("PushSecret controller", func() {
const (
- PushSecretName = "test-es"
+ PushSecretName = "test-ps"
PushSecretStore = "test-store"
SecretName = "test-secret"
)
@@ -93,6 +100,21 @@ var _ = Describe("ExternalSecret controller", func() {
PushSecretNamespace, err = ctest.CreateNamespace("test-ns", k8sClient)
Expect(err).ToNot(HaveOccurred())
fakeProvider.Reset()
+
+ Expect(k8sClient.Create(context.Background(), &genv1alpha1.Fake{
+ TypeMeta: metav1.TypeMeta{
+ Kind: "Fake",
+ APIVersion: "generators.external-secrets.io/v1alpha1",
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "test",
+ Namespace: PushSecretNamespace,
+ },
+ Spec: genv1alpha1.FakeSpec{
+ Data: map[string]string{
+ "key": "foo-bar-from-generator",
+ },
+ }})).ToNot(HaveOccurred())
})
AfterEach(func() {
@@ -103,7 +125,6 @@ var _ = Describe("ExternalSecret controller", func() {
},
})
// give a time for reconciler to remove finalizers before removing SecretStores
- // TODO: Secret Stores should have finalizers bound to PushSecrets if DeletionPolicy == Delete
time.Sleep(2 * time.Second)
k8sClient.Delete(context.Background(), &v1beta1.SecretStore{
ObjectMeta: metav1.ObjectMeta{
@@ -156,7 +177,7 @@ var _ = Describe("ExternalSecret controller", func() {
},
},
Selector: v1alpha1.PushSecretSelector{
- Secret: v1alpha1.PushSecretSecret{
+ Secret: &v1alpha1.PushSecretSecret{
Name: SecretName,
},
},
@@ -345,7 +366,7 @@ var _ = Describe("ExternalSecret controller", func() {
return nil
}
fakeProvider.SecretExistsFn = func(ctx context.Context, ref v1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf("don't know")
+ return false, errors.New("don't know")
}
tc.pushsecret.Spec.UpdatePolicy = v1alpha1.PushSecretUpdatePolicyIfNotExists
initialValue := fakeProvider.SetSecretArgs[tc.pushsecret.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
@@ -389,7 +410,7 @@ var _ = Describe("ExternalSecret controller", func() {
},
},
Selector: v1alpha1.PushSecretSelector{
- Secret: v1alpha1.PushSecretSecret{
+ Secret: &v1alpha1.PushSecretSecret{
Name: SecretName,
},
},
@@ -453,7 +474,7 @@ var _ = Describe("ExternalSecret controller", func() {
},
},
Selector: v1alpha1.PushSecretSelector{
- Secret: v1alpha1.PushSecretSecret{
+ Secret: &v1alpha1.PushSecretSecret{
Name: SecretName,
},
},
@@ -489,12 +510,66 @@ var _ = Describe("ExternalSecret controller", func() {
return true
}
}
+
+ // if PushSecret's DeletionPolicy is cleared, it should delete successfully
+ syncChangePolicyAndDeleteSuccessfully := func(tc *testCase) {
+ fakeProvider.SetSecretFn = func() error {
+ return nil
+ }
+ tc.pushsecret = &v1alpha1.PushSecret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: PushSecretName,
+ Namespace: PushSecretNamespace,
+ },
+ Spec: v1alpha1.PushSecretSpec{
+ DeletionPolicy: v1alpha1.PushSecretDeletionPolicyDelete,
+ SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
+ {
+ Name: PushSecretStore,
+ Kind: "SecretStore",
+ },
+ },
+ Selector: v1alpha1.PushSecretSelector{
+ Secret: &v1alpha1.PushSecretSecret{
+ Name: SecretName,
+ },
+ },
+ Data: []v1alpha1.PushSecretData{
+ {
+ Match: v1alpha1.PushSecretMatch{
+ SecretKey: defaultKey,
+ RemoteRef: v1alpha1.PushSecretRemoteRef{
+ RemoteKey: defaultPath,
+ },
+ },
+ },
+ },
+ },
+ }
+ tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
+ ps.Spec.DeletionPolicy = v1alpha1.PushSecretDeletionPolicyNone
+ updatedPS := &v1alpha1.PushSecret{}
+ Expect(k8sClient.Update(context.Background(), ps, &client.UpdateOptions{})).Should(Succeed())
+ Expect(k8sClient.Delete(context.Background(), ps, &client.DeleteOptions{})).Should(Succeed())
+ Eventually(func() bool {
+ psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
+ By("checking if Get PushSecret returns not found")
+ err := k8sClient.Get(context.Background(), psKey, updatedPS)
+ if err != nil && client.IgnoreNotFound(err) == nil {
+ return true
+ }
+ return false
+ }, time.Second*10, time.Second).Should(BeTrue())
+ return true
+ }
+ }
+
failDelete := func(tc *testCase) {
fakeProvider.SetSecretFn = func() error {
return nil
}
fakeProvider.DeleteSecretFn = func() error {
- return fmt.Errorf("Nope")
+ return errors.New("Nope")
}
tc.pushsecret = &v1alpha1.PushSecret{
ObjectMeta: metav1.ObjectMeta{
@@ -510,7 +585,7 @@ var _ = Describe("ExternalSecret controller", func() {
},
},
Selector: v1alpha1.PushSecretSelector{
- Secret: v1alpha1.PushSecretSecret{
+ Secret: &v1alpha1.PushSecretSecret{
Name: SecretName,
},
},
@@ -552,7 +627,7 @@ var _ = Describe("ExternalSecret controller", func() {
return nil
}
fakeProvider.DeleteSecretFn = func() error {
- return fmt.Errorf("boom")
+ return errors.New("boom")
}
tc.pushsecret.Spec.DeletionPolicy = v1alpha1.PushSecretDeletionPolicyDelete
tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
@@ -656,7 +731,7 @@ var _ = Describe("ExternalSecret controller", func() {
},
},
Selector: v1alpha1.PushSecretSelector{
- Secret: v1alpha1.PushSecretSecret{
+ Secret: &v1alpha1.PushSecretSecret{
Name: SecretName,
},
},
@@ -666,7 +741,7 @@ var _ = Describe("ExternalSecret controller", func() {
Match: v1alpha1.PushSecretMatch{
SecretKey: "some-array[0].entity",
RemoteRef: v1alpha1.PushSecretRemoteRef{
- RemoteKey: "path/to/key",
+ RemoteKey: defaultPath,
},
},
},
@@ -718,10 +793,11 @@ var _ = Describe("ExternalSecret controller", func() {
},
},
Kind: "SecretStore",
+ Name: PushSecretStore,
},
},
Selector: v1alpha1.PushSecretSelector{
- Secret: v1alpha1.PushSecretSecret{
+ Secret: &v1alpha1.PushSecretSecret{
Name: SecretName,
},
},
@@ -800,6 +876,28 @@ var _ = Describe("ExternalSecret controller", func() {
return bytes.Equal(secretValue, providerValue) && checkCondition(ps.Status, expected)
}
}
+
+ syncWithGenerator := func(tc *testCase) {
+ fakeProvider.SetSecretFn = func() error {
+ return nil
+ }
+ tc.pushsecret.Spec.Selector.Secret = nil
+ tc.pushsecret.Spec.Selector.GeneratorRef = &v1beta1.GeneratorRef{
+ APIVersion: "generators.external-secrets.io/v1alpha1",
+ Kind: "Fake",
+ Name: "test",
+ }
+ tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
+ providerValue := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey].Value
+ expected := v1alpha1.PushSecretStatusCondition{
+ Type: v1alpha1.PushSecretReady,
+ Status: v1.ConditionTrue,
+ Reason: v1alpha1.ReasonSynced,
+ Message: "PushSecret synced successfully",
+ }
+ return bytes.Equal([]byte("foo-bar-from-generator"), providerValue) && checkCondition(ps.Status, expected)
+ }
+ }
// if target Secret name is not specified it should use the ExternalSecret name.
syncWithClusterStoreMatchingLabels := func(tc *testCase) {
fakeProvider.SetSecretFn = func() error {
@@ -819,10 +917,11 @@ var _ = Describe("ExternalSecret controller", func() {
},
},
Kind: "ClusterSecretStore",
+ Name: PushSecretStore,
},
},
Selector: v1alpha1.PushSecretSelector{
- Secret: v1alpha1.PushSecretSecret{
+ Secret: &v1alpha1.PushSecretSecret{
Name: SecretName,
},
},
@@ -930,10 +1029,11 @@ var _ = Describe("ExternalSecret controller", func() {
}
return checkCondition(ps.Status, expected)
}
- } // if target Secret name is not specified it should use the ExternalSecret name.
+ }
+ // if target Secret name is not specified it should use the ExternalSecret name.
setSecretFail := func(tc *testCase) {
fakeProvider.SetSecretFn = func() error {
- return fmt.Errorf("boom")
+ return errors.New("boom")
}
tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
expected := v1alpha1.PushSecretStatusCondition{
@@ -948,7 +1048,7 @@ var _ = Describe("ExternalSecret controller", func() {
// if target Secret name is not specified it should use the ExternalSecret name.
newClientFail := func(tc *testCase) {
fakeProvider.NewFn = func(context.Context, v1beta1.GenericStore, client.Client, string) (v1beta1.SecretsClient, error) {
- return nil, fmt.Errorf("boom")
+ return nil, errors.New("boom")
}
tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
expected := v1alpha1.PushSecretStatusCondition{
@@ -960,6 +1060,7 @@ var _ = Describe("ExternalSecret controller", func() {
return checkCondition(ps.Status, expected)
}
}
+
DescribeTable("When reconciling a PushSecret",
func(tweaks ...testTweaks) {
tc := makeDefaultTestcase()
@@ -977,7 +1078,7 @@ var _ = Describe("ExternalSecret controller", func() {
if tc.pushsecret != nil {
Expect(k8sClient.Create(ctx, tc.pushsecret)).Should(Succeed())
}
- time.Sleep(2 * time.Second)
+ time.Sleep(2 * time.Second) // prevents race conditions during tests causing failures
psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
createdPS := &v1alpha1.PushSecret{}
By("checking the pushSecret condition")
@@ -998,12 +1099,14 @@ var _ = Describe("ExternalSecret controller", func() {
Entry("should sync with template", syncSuccessfullyWithTemplate),
Entry("should sync with conversion strategy", syncSuccessfullyWithConversionStrategy),
Entry("should delete if DeletionPolicy=Delete", syncAndDeleteSuccessfully),
+ Entry("should delete after DeletionPolicy changed from Delete to None", syncChangePolicyAndDeleteSuccessfully),
Entry("should track deletion tasks if Delete fails", failDelete),
Entry("should track deleted stores if Delete fails", failDeleteStore),
Entry("should delete all secrets if SecretStore changes", deleteWholeStore),
Entry("should sync to stores matching labels", syncMatchingLabels),
Entry("should sync with ClusterStore", syncWithClusterStore),
Entry("should sync with ClusterStore matching labels", syncWithClusterStoreMatchingLabels),
+ Entry("should sync with Generator", syncWithGenerator),
Entry("should fail if Secret is not created", failNoSecret),
Entry("should fail if Secret Key does not exist", failNoSecretKey),
Entry("should fail if SetSecret fails", setSecretFail),
@@ -1012,3 +1115,309 @@ var _ = Describe("ExternalSecret controller", func() {
Entry("should fail if NewClient fails", newClientFail),
)
})
+
+var _ = Describe("PushSecret Controller Un/Managed Stores", func() {
+ const (
+ PushSecretName = "test-ps"
+ ManagedPushSecretStore1 = "test-managed-store-1"
+ ManagedPushSecretStore2 = "test-managed-store-2"
+ UnmanagedPushSecretStore1 = "test-unmanaged-store-1"
+ UnmanagedPushSecretStore2 = "test-unmanaged-store-2"
+ SecretName = "test-secret"
+ )
+
+ var PushSecretNamespace string
+ PushSecretStores := []string{ManagedPushSecretStore1, ManagedPushSecretStore2, UnmanagedPushSecretStore1, UnmanagedPushSecretStore2}
+
+ // if we are in debug and need to increase the timeout for testing, we can do so by using an env var
+ if customTimeout := os.Getenv("TEST_CUSTOM_TIMEOUT_SEC"); customTimeout != "" {
+ if t, err := strconv.Atoi(customTimeout); err == nil {
+ timeout = time.Second * time.Duration(t)
+ }
+ }
+
+ BeforeEach(func() {
+ var err error
+ PushSecretNamespace, err = ctest.CreateNamespace("test-ns", k8sClient)
+ Expect(err).ToNot(HaveOccurred())
+ fakeProvider.Reset()
+ })
+
+ AfterEach(func() {
+ k8sClient.Delete(context.Background(), &v1alpha1.PushSecret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: PushSecretName,
+ Namespace: PushSecretNamespace,
+ },
+ })
+ // give a time for reconciler to remove finalizers before removing SecretStores
+ // TODO: Secret Stores should have finalizers bound to PushSecrets if DeletionPolicy == Delete
+ time.Sleep(2 * time.Second)
+ for _, psstore := range PushSecretStores {
+ k8sClient.Delete(context.Background(), &v1beta1.SecretStore{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: psstore,
+ Namespace: PushSecretNamespace,
+ },
+ })
+ k8sClient.Delete(context.Background(), &v1beta1.ClusterSecretStore{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: psstore,
+ },
+ })
+ }
+ k8sClient.Delete(context.Background(), &v1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: SecretName,
+ Namespace: PushSecretNamespace,
+ },
+ })
+ Expect(k8sClient.Delete(context.Background(), &v1.Namespace{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: PushSecretNamespace,
+ },
+ })).To(Succeed())
+ })
+
+ const (
+ defaultKey = "key"
+ defaultVal = "value"
+ defaultPath = "path/to/key"
+ )
+
+ makeDefaultTestcase := func() *testCase {
+ return &testCase{
+ pushsecret: &v1alpha1.PushSecret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: PushSecretName,
+ Namespace: PushSecretNamespace,
+ },
+ Spec: v1alpha1.PushSecretSpec{
+ SecretStoreRefs: []v1alpha1.PushSecretStoreRef{
+ {
+ Name: ManagedPushSecretStore1,
+ Kind: "SecretStore",
+ },
+ },
+ Selector: v1alpha1.PushSecretSelector{
+ Secret: &v1alpha1.PushSecretSecret{
+ Name: SecretName,
+ },
+ },
+ Data: []v1alpha1.PushSecretData{
+ {
+ Match: v1alpha1.PushSecretMatch{
+ SecretKey: defaultKey,
+ RemoteRef: v1alpha1.PushSecretRemoteRef{
+ RemoteKey: defaultPath,
+ },
+ },
+ },
+ },
+ },
+ },
+ secret: &v1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: SecretName,
+ Namespace: PushSecretNamespace,
+ },
+ Data: map[string][]byte{
+ defaultKey: []byte(defaultVal),
+ },
+ },
+ managedStore1: &v1beta1.SecretStore{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: ManagedPushSecretStore1,
+ Namespace: PushSecretNamespace,
+ },
+ TypeMeta: metav1.TypeMeta{
+ Kind: "SecretStore",
+ },
+ Spec: v1beta1.SecretStoreSpec{
+ Provider: &v1beta1.SecretStoreProvider{
+ Fake: &v1beta1.FakeProvider{
+ Data: []v1beta1.FakeProviderData{},
+ },
+ },
+ },
+ },
+ managedStore2: &v1beta1.SecretStore{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: ManagedPushSecretStore2,
+ Namespace: PushSecretNamespace,
+ },
+ TypeMeta: metav1.TypeMeta{
+ Kind: "SecretStore",
+ },
+ Spec: v1beta1.SecretStoreSpec{
+ Provider: &v1beta1.SecretStoreProvider{
+ Fake: &v1beta1.FakeProvider{
+ Data: []v1beta1.FakeProviderData{},
+ },
+ },
+ },
+ },
+ unmanagedStore1: &v1beta1.SecretStore{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: UnmanagedPushSecretStore1,
+ Namespace: PushSecretNamespace,
+ },
+ TypeMeta: metav1.TypeMeta{
+ Kind: "SecretStore",
+ },
+ Spec: v1beta1.SecretStoreSpec{
+ Provider: &v1beta1.SecretStoreProvider{
+ Fake: &v1beta1.FakeProvider{
+ Data: []v1beta1.FakeProviderData{},
+ },
+ },
+ Controller: "not-managed",
+ },
+ },
+ unmanagedStore2: &v1beta1.SecretStore{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: UnmanagedPushSecretStore2,
+ Namespace: PushSecretNamespace,
+ },
+ TypeMeta: metav1.TypeMeta{
+ Kind: "SecretStore",
+ },
+ Spec: v1beta1.SecretStoreSpec{
+ Provider: &v1beta1.SecretStoreProvider{
+ Fake: &v1beta1.FakeProvider{
+ Data: []v1beta1.FakeProviderData{},
+ },
+ },
+ Controller: "not-managed",
+ },
+ },
+ }
+ }
+
+ multipleManagedStoresSyncsSuccessfully := func(tc *testCase) {
+ fakeProvider.SetSecretFn = func() error {
+ return nil
+ }
+
+ tc.pushsecret.Spec.SecretStoreRefs = append(tc.pushsecret.Spec.SecretStoreRefs,
+ v1alpha1.PushSecretStoreRef{
+ Name: ManagedPushSecretStore2,
+ Kind: "SecretStore",
+ },
+ )
+
+ tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
+ Eventually(func() bool {
+ By("checking if Provider value got updated")
+ secretValue := secret.Data[defaultKey]
+ providerValue, ok := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
+ if !ok {
+ return false
+ }
+ got := providerValue.Value
+ return bytes.Equal(got, secretValue)
+ }, time.Second*10, time.Second).Should(BeTrue())
+ return true
+ }
+ }
+
+ skipUnmanagedStores := func(tc *testCase) {
+ tc.pushsecret.Spec.SecretStoreRefs = []v1alpha1.PushSecretStoreRef{
+ {
+ Name: UnmanagedPushSecretStore1,
+ Kind: "SecretStore",
+ },
+ {
+ Name: UnmanagedPushSecretStore2,
+ Kind: "SecretStore",
+ },
+ }
+
+ tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
+ return len(ps.Status.Conditions) == 0
+ }
+ }
+
+ warnUnmanagedStoresAndSyncManagedStores := func(tc *testCase) {
+ fakeProvider.SetSecretFn = func() error {
+ return nil
+ }
+
+ tc.pushsecret.Spec.SecretStoreRefs = []v1alpha1.PushSecretStoreRef{
+ {
+ Name: ManagedPushSecretStore1,
+ Kind: "SecretStore",
+ },
+ {
+ Name: ManagedPushSecretStore2,
+ Kind: "SecretStore",
+ },
+ {
+ Name: UnmanagedPushSecretStore1,
+ Kind: "SecretStore",
+ },
+ {
+ Name: UnmanagedPushSecretStore2,
+ Kind: "SecretStore",
+ },
+ }
+
+ tc.assert = func(ps *v1alpha1.PushSecret, secret *v1.Secret) bool {
+ Eventually(func() bool {
+ By("checking if Provider value got updated")
+ secretValue := secret.Data[defaultKey]
+ providerValue, ok := fakeProvider.SetSecretArgs[ps.Spec.Data[0].Match.RemoteRef.RemoteKey]
+ if !ok {
+ return false
+ }
+ got := providerValue.Value
+ return bytes.Equal(got, secretValue)
+ }, time.Second*10, time.Second).Should(BeTrue())
+ return true
+ }
+ }
+
+ DescribeTable("When reconciling a PushSecret with multiple secret stores",
+ func(tweaks ...testTweaks) {
+ tc := makeDefaultTestcase()
+ for _, tweak := range tweaks {
+ tweak(tc)
+ }
+ ctx := context.Background()
+ By("creating secret stores, a secret and a pushsecret")
+ if tc.managedStore1 != nil {
+ Expect(k8sClient.Create(ctx, tc.managedStore1)).To(Succeed())
+ }
+ if tc.managedStore2 != nil {
+ Expect(k8sClient.Create(ctx, tc.managedStore2)).To(Succeed())
+ }
+ if tc.unmanagedStore1 != nil {
+ Expect(k8sClient.Create(ctx, tc.unmanagedStore1)).To(Succeed())
+ }
+ if tc.unmanagedStore2 != nil {
+ Expect(k8sClient.Create(ctx, tc.unmanagedStore2)).To(Succeed())
+ }
+ if tc.secret != nil {
+ Expect(k8sClient.Create(ctx, tc.secret)).To(Succeed())
+ }
+ if tc.pushsecret != nil {
+ Expect(k8sClient.Create(ctx, tc.pushsecret)).Should(Succeed())
+ }
+ time.Sleep(2 * time.Second) // prevents race conditions during tests causing failures
+ psKey := types.NamespacedName{Name: PushSecretName, Namespace: PushSecretNamespace}
+ createdPS := &v1alpha1.PushSecret{}
+ By("checking the pushSecret condition")
+ Eventually(func() bool {
+ err := k8sClient.Get(ctx, psKey, createdPS)
+ if err != nil {
+ return false
+ }
+ return tc.assert(createdPS, tc.secret)
+ }, timeout, interval).Should(BeTrue())
+ // this must be optional so we can test faulty es configuration
+ },
+ Entry("should sync successfully if there are multiple managed stores", multipleManagedStoresSyncsSuccessfully),
+ Entry("should skip unmanaged stores", skipUnmanagedStores),
+ Entry("should skip unmanaged stores and sync managed stores", warnUnmanagedStoresAndSyncManagedStores),
+ )
+})
diff --git a/pkg/controllers/pushsecret/suite_test.go b/pkg/controllers/pushsecret/suite_test.go
index 48ce7b03e67..97ee73e7ec9 100644
--- a/pkg/controllers/pushsecret/suite_test.go
+++ b/pkg/controllers/pushsecret/suite_test.go
@@ -25,6 +25,7 @@ import (
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/controller"
"sigs.k8s.io/controller-runtime/pkg/envtest"
logf "sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/log/zap"
@@ -32,6 +33,8 @@ import (
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
+ ctrlcommon "github.com/external-secrets/external-secrets/pkg/controllers/common"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@@ -72,6 +75,8 @@ var _ = BeforeSuite(func() {
Expect(err).NotTo(HaveOccurred())
err = esv1alpha1.AddToScheme(scheme.Scheme)
Expect(err).NotTo(HaveOccurred())
+ err = genv1alpha1.AddToScheme(scheme.Scheme)
+ Expect(err).NotTo(HaveOccurred())
k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{
Scheme: scheme.Scheme,
@@ -90,9 +95,13 @@ var _ = BeforeSuite(func() {
err = (&Reconciler{
Client: k8sClient,
Scheme: k8sManager.GetScheme(),
- Log: ctrl.Log.WithName("controllers").WithName("ExternalSecrets"),
+ Log: ctrl.Log.WithName("controllers").WithName("PushSecret"),
+ RestConfig: cfg,
RequeueInterval: time.Second,
- }).SetupWithManager(k8sManager)
+ }).SetupWithManager(k8sManager, controller.Options{
+ MaxConcurrentReconciles: 1,
+ RateLimiter: ctrlcommon.BuildRateLimiter(),
+ })
Expect(err).ToNot(HaveOccurred())
go func() {
diff --git a/pkg/controllers/secretstore/client_manager.go b/pkg/controllers/secretstore/client_manager.go
index a87cfa7e2dd..077380e92fa 100644
--- a/pkg/controllers/secretstore/client_manager.go
+++ b/pkg/controllers/secretstore/client_manager.go
@@ -16,7 +16,9 @@ package secretstore
import (
"context"
+ "errors"
"fmt"
+ "regexp"
"strings"
"github.com/go-logr/logr"
@@ -33,7 +35,7 @@ import (
const (
errGetClusterSecretStore = "could not get ClusterSecretStore %q, %w"
errGetSecretStore = "could not get SecretStore %q, %w"
- errSecretStoreNotReady = "the desired SecretStore %s is not ready"
+ errSecretStoreNotReady = "%s %q is not ready"
errClusterStoreMismatch = "using cluster store %q is not allowed from namespace %q: denied by spec.condition"
)
@@ -113,7 +115,7 @@ func (m *Manager) Get(ctx context.Context, storeRef esv1beta1.SecretStoreRef, na
}
// check if store should be handled by this controller instance
if !ShouldProcessStore(store, m.controllerClass) {
- return nil, fmt.Errorf("can not reference unmanaged store")
+ return nil, errors.New("can not reference unmanaged store")
}
// when using ClusterSecretStore, validate the ClusterSecretStore namespace conditions
shouldProcess, err := m.shouldProcessSecret(store, namespace)
@@ -245,6 +247,18 @@ func (m *Manager) shouldProcessSecret(store esv1beta1.GenericStore, ns string) (
return true, nil
}
}
+
+ for _, reg := range condition.NamespaceRegexes {
+ match, err := regexp.MatchString(reg, ns)
+ if err != nil {
+ // Should not happen since store validation already verified the regexes.
+ return false, fmt.Errorf("failed to compile regex %v: %w", reg, err)
+ }
+
+ if match {
+ return true, nil
+ }
+ }
}
return false, nil
@@ -257,7 +271,7 @@ func assertStoreIsUsable(store esv1beta1.GenericStore) error {
}
condition := GetSecretStoreCondition(store.GetStatus(), esv1beta1.SecretStoreReady)
if condition == nil || condition.Status != v1.ConditionTrue {
- return fmt.Errorf(errSecretStoreNotReady, store.GetName())
+ return fmt.Errorf(errSecretStoreNotReady, store.GetKind(), store.GetName())
}
return nil
}
diff --git a/pkg/controllers/secretstore/client_manager_test.go b/pkg/controllers/secretstore/client_manager_test.go
index d5a10f3adb2..5038b29682f 100644
--- a/pkg/controllers/secretstore/client_manager_test.go
+++ b/pkg/controllers/secretstore/client_manager_test.go
@@ -25,6 +25,7 @@ import (
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"sigs.k8s.io/controller-runtime/pkg/client"
fakeclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
@@ -35,9 +36,13 @@ import (
func TestManagerGet(t *testing.T) {
scheme := runtime.NewScheme()
- _ = clientgoscheme.AddToScheme(scheme)
- _ = esv1beta1.AddToScheme(scheme)
- _ = apiextensionsv1.AddToScheme(scheme)
+
+ // add kubernetes schemes
+ utilruntime.Must(clientgoscheme.AddToScheme(scheme))
+ utilruntime.Must(apiextensionsv1.AddToScheme(scheme))
+
+ // add external-secrets schemes
+ utilruntime.Must(esv1beta1.AddToScheme(scheme))
// We have a test provider to control
// the behavior of the NewClient func.
@@ -310,6 +315,100 @@ func TestManagerGet(t *testing.T) {
}
}
+func TestShouldProcessSecret(t *testing.T) {
+ scheme := runtime.NewScheme()
+
+ // add kubernetes schemes
+ utilruntime.Must(clientgoscheme.AddToScheme(scheme))
+ utilruntime.Must(apiextensionsv1.AddToScheme(scheme))
+
+ // add external-secrets schemes
+ utilruntime.Must(esv1beta1.AddToScheme(scheme))
+
+ testNamespace := "test-a"
+ testCases := []struct {
+ name string
+ conditions []esv1beta1.ClusterSecretStoreCondition
+ namespace *corev1.Namespace
+ wantErr string
+ want bool
+ }{
+ {
+ name: "processes a regex condition",
+ conditions: []esv1beta1.ClusterSecretStoreCondition{
+ {
+ NamespaceRegexes: []string{`test-*`},
+ },
+ },
+ namespace: &corev1.Namespace{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: testNamespace,
+ },
+ },
+ want: true,
+ },
+ {
+ name: "process multiple regexes",
+ conditions: []esv1beta1.ClusterSecretStoreCondition{
+ {
+ NamespaceRegexes: []string{`nope`, `test-*`},
+ },
+ },
+ namespace: &corev1.Namespace{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: testNamespace,
+ },
+ },
+ want: true,
+ },
+ {
+ name: "shouldn't process if nothing matches",
+ conditions: []esv1beta1.ClusterSecretStoreCondition{
+ {
+ NamespaceRegexes: []string{`nope`},
+ },
+ },
+ namespace: &corev1.Namespace{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: testNamespace,
+ },
+ },
+ want: false,
+ },
+ }
+
+ for _, tt := range testCases {
+ t.Run(tt.name, func(t *testing.T) {
+ fakeSpec := esv1beta1.SecretStoreSpec{
+ Conditions: tt.conditions,
+ }
+
+ defaultStore := &esv1beta1.ClusterSecretStore{
+ TypeMeta: metav1.TypeMeta{Kind: esv1beta1.ClusterSecretStoreKind},
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
+ Namespace: tt.namespace.Name,
+ },
+ Spec: fakeSpec,
+ }
+
+ client := fakeclient.NewClientBuilder().WithScheme(scheme).WithObjects(defaultStore, tt.namespace).Build()
+ clientMap := make(map[clientKey]*clientVal)
+ mgr := &Manager{
+ log: logr.Discard(),
+ client: client,
+ enableFloodgate: true,
+ clientMap: clientMap,
+ }
+
+ got, err := mgr.shouldProcessSecret(defaultStore, tt.namespace.Name)
+ require.NoError(t, err)
+
+ assert.Equal(t, tt.want, got)
+ })
+ }
+}
+
type WrapProvider struct {
newClientFunc func(
context.Context,
diff --git a/pkg/controllers/secretstore/clustersecretstore_controller.go b/pkg/controllers/secretstore/clustersecretstore_controller.go
index d7a769ce2d7..1725586ea6c 100644
--- a/pkg/controllers/secretstore/clustersecretstore_controller.go
+++ b/pkg/controllers/secretstore/clustersecretstore_controller.go
@@ -24,6 +24,7 @@ import (
"k8s.io/client-go/tools/record"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/controller"
esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
@@ -62,14 +63,20 @@ func (r *ClusterStoreReconciler) Reconcile(ctx context.Context, req ctrl.Request
return ctrl.Result{}, err
}
- return reconcile(ctx, req, &css, r.Client, log, r.ControllerClass, cssmetrics.GetGaugeVec, r.recorder, r.RequeueInterval)
+ return reconcile(ctx, req, &css, r.Client, log, Opts{
+ ControllerClass: r.ControllerClass,
+ GaugeVecGetter: cssmetrics.GetGaugeVec,
+ Recorder: r.recorder,
+ RequeueInterval: r.RequeueInterval,
+ })
}
// SetupWithManager returns a new controller builder that will be started by the provided Manager.
-func (r *ClusterStoreReconciler) SetupWithManager(mgr ctrl.Manager) error {
+func (r *ClusterStoreReconciler) SetupWithManager(mgr ctrl.Manager, opts controller.Options) error {
r.recorder = mgr.GetEventRecorderFor("cluster-secret-store")
return ctrl.NewControllerManagedBy(mgr).
+ WithOptions(opts).
For(&esapi.ClusterSecretStore{}).
Complete(r)
}
diff --git a/pkg/controllers/secretstore/common.go b/pkg/controllers/secretstore/common.go
index 53f74c73933..538b7fdb00c 100644
--- a/pkg/controllers/secretstore/common.go
+++ b/pkg/controllers/secretstore/common.go
@@ -30,24 +30,30 @@ import (
)
const (
- errStoreProvider = "could not get store provider: %w"
errStoreClient = "could not get provider client: %w"
errValidationFailed = "could not validate provider: %w"
errPatchStatus = "unable to patch status: %w"
errUnableCreateClient = "unable to create client"
- errUnableValidateStore = "unable to validate store"
- errUnableGetProvider = "unable to get store provider"
+ errUnableValidateStore = "unable to validate store: %s"
msgStoreValidated = "store validated"
)
-func reconcile(ctx context.Context, req ctrl.Request, ss esapi.GenericStore, cl client.Client, log logr.Logger,
- controllerClass string, gaugeVecGetter metrics.GaugeVevGetter, recorder record.EventRecorder, requeueInterval time.Duration) (ctrl.Result, error) {
- if !ShouldProcessStore(ss, controllerClass) {
+type Opts struct {
+ ControllerClass string
+ GaugeVecGetter metrics.GaugeVevGetter
+ Recorder record.EventRecorder
+ RequeueInterval time.Duration
+}
+
+func reconcile(ctx context.Context, req ctrl.Request, ss esapi.GenericStore, cl client.Client, log logr.Logger, opts Opts) (ctrl.Result, error) {
+ if !ShouldProcessStore(ss, opts.ControllerClass) {
log.V(1).Info("skip store")
return ctrl.Result{}, nil
}
+ requeueInterval := opts.RequeueInterval
+
if ss.GetSpec().RefreshInterval != 0 {
requeueInterval = time.Second * time.Duration(ss.GetSpec().RefreshInterval)
}
@@ -64,7 +70,7 @@ func reconcile(ctx context.Context, req ctrl.Request, ss esapi.GenericStore, cl
// validateStore modifies the store conditions
// we have to patch the status
log.V(1).Info("validating")
- err := validateStore(ctx, req.Namespace, controllerClass, ss, cl, gaugeVecGetter, recorder)
+ err := validateStore(ctx, req.Namespace, opts.ControllerClass, ss, cl, opts.GaugeVecGetter, opts.Recorder)
if err != nil {
log.Error(err, "unable to validate store")
return ctrl.Result{}, err
@@ -79,9 +85,9 @@ func reconcile(ctx context.Context, req ctrl.Request, ss esapi.GenericStore, cl
}
ss.SetStatus(capStatus)
- recorder.Event(ss, v1.EventTypeNormal, esapi.ReasonStoreValid, msgStoreValidated)
+ opts.Recorder.Event(ss, v1.EventTypeNormal, esapi.ReasonStoreValid, msgStoreValidated)
cond := NewSecretStoreCondition(esapi.SecretStoreReady, v1.ConditionTrue, esapi.ReasonStoreValid, msgStoreValidated)
- SetExternalSecretCondition(ss, *cond, gaugeVecGetter)
+ SetExternalSecretCondition(ss, *cond, opts.GaugeVecGetter)
return ctrl.Result{
RequeueAfter: requeueInterval,
@@ -103,7 +109,7 @@ func validateStore(ctx context.Context, namespace, controllerClass string, store
}
validationResult, err := cl.Validate()
if err != nil && validationResult != esapi.ValidationResultUnknown {
- cond := NewSecretStoreCondition(esapi.SecretStoreReady, v1.ConditionFalse, esapi.ReasonValidationFailed, errUnableValidateStore)
+ cond := NewSecretStoreCondition(esapi.SecretStoreReady, v1.ConditionFalse, esapi.ReasonValidationFailed, fmt.Sprintf(errUnableValidateStore, err))
SetExternalSecretCondition(store, *cond, gaugeVecGetter)
recorder.Event(store, v1.EventTypeWarning, esapi.ReasonValidationFailed, err.Error())
return fmt.Errorf(errValidationFailed, err)
diff --git a/pkg/controllers/secretstore/common_test.go b/pkg/controllers/secretstore/common_test.go
index fb2a1718866..28bc7990992 100644
--- a/pkg/controllers/secretstore/common_test.go
+++ b/pkg/controllers/secretstore/common_test.go
@@ -44,7 +44,7 @@ var _ = Describe("SecretStore reconcile", func() {
Expect(k8sClient.Delete(context.Background(), test.store)).ToNot(HaveOccurred())
})
- // a invalid provider config should be reflected
+ // an invalid provider config should be reflected
// in the store status condition
invalidProvider := func(tc *testCase) {
tc.assert = func() {
diff --git a/pkg/controllers/secretstore/secretstore_controller.go b/pkg/controllers/secretstore/secretstore_controller.go
index 5fa0654d65a..ad46f5ab2c2 100644
--- a/pkg/controllers/secretstore/secretstore_controller.go
+++ b/pkg/controllers/secretstore/secretstore_controller.go
@@ -63,7 +63,12 @@ func (r *StoreReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl
return ctrl.Result{}, err
}
- return reconcile(ctx, req, &ss, r.Client, log, r.ControllerClass, ssmetrics.GetGaugeVec, r.recorder, r.RequeueInterval)
+ return reconcile(ctx, req, &ss, r.Client, log, Opts{
+ ControllerClass: r.ControllerClass,
+ GaugeVecGetter: ssmetrics.GetGaugeVec,
+ Recorder: r.recorder,
+ RequeueInterval: r.RequeueInterval,
+ })
}
// SetupWithManager returns a new controller builder that will be started by the provided Manager.
diff --git a/pkg/controllers/secretstore/suite_test.go b/pkg/controllers/secretstore/suite_test.go
index 2e984bd7e9b..a4254ed0025 100644
--- a/pkg/controllers/secretstore/suite_test.go
+++ b/pkg/controllers/secretstore/suite_test.go
@@ -30,6 +30,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/metrics/server"
esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ ctrlcommon "github.com/external-secrets/external-secrets/pkg/controllers/common"
ctrlmetrics "github.com/external-secrets/external-secrets/pkg/controllers/metrics"
"github.com/external-secrets/external-secrets/pkg/controllers/secretstore/cssmetrics"
"github.com/external-secrets/external-secrets/pkg/controllers/secretstore/ssmetrics"
@@ -85,7 +86,10 @@ var _ = BeforeSuite(func() {
Scheme: k8sManager.GetScheme(),
Log: ctrl.Log.WithName("controllers").WithName("SecretStore"),
ControllerClass: defaultControllerClass,
- }).SetupWithManager(k8sManager, controller.Options{})
+ }).SetupWithManager(k8sManager, controller.Options{
+ MaxConcurrentReconciles: 1,
+ RateLimiter: ctrlcommon.BuildRateLimiter(),
+ })
Expect(err).ToNot(HaveOccurred())
err = (&ClusterStoreReconciler{
@@ -93,7 +97,10 @@ var _ = BeforeSuite(func() {
Scheme: k8sManager.GetScheme(),
ControllerClass: defaultControllerClass,
Log: ctrl.Log.WithName("controllers").WithName("ClusterSecretStore"),
- }).SetupWithManager(k8sManager)
+ }).SetupWithManager(k8sManager, controller.Options{
+ MaxConcurrentReconciles: 1,
+ RateLimiter: ctrlcommon.BuildRateLimiter(),
+ })
Expect(err).ToNot(HaveOccurred())
go func() {
diff --git a/pkg/controllers/templating/parser.go b/pkg/controllers/templating/parser.go
index dedc9ab3d7d..a37687c4286 100644
--- a/pkg/controllers/templating/parser.go
+++ b/pkg/controllers/templating/parser.go
@@ -41,20 +41,29 @@ type Parser struct {
DataMap map[string][]byte
Client client.Client
TargetSecret *v1.Secret
+
+ TemplateFromConfigMap *v1.ConfigMap
+ TemplateFromSecret *v1.Secret
}
func (p *Parser) MergeConfigMap(ctx context.Context, namespace string, tpl esv1beta1.TemplateFrom) error {
if tpl.ConfigMap == nil {
return nil
}
+
var cm v1.ConfigMap
- err := p.Client.Get(ctx, types.NamespacedName{
- Name: tpl.ConfigMap.Name,
- Namespace: namespace,
- }, &cm)
- if err != nil {
- return err
+ if p.TemplateFromConfigMap != nil {
+ cm = *p.TemplateFromConfigMap
+ } else {
+ err := p.Client.Get(ctx, types.NamespacedName{
+ Name: tpl.ConfigMap.Name,
+ Namespace: namespace,
+ }, &cm)
+ if err != nil {
+ return err
+ }
}
+
for _, k := range tpl.ConfigMap.Items {
val, ok := cm.Data[k.Key]
out := make(map[string][]byte)
@@ -67,7 +76,7 @@ func (p *Parser) MergeConfigMap(ctx context.Context, namespace string, tpl esv1b
case esv1beta1.TemplateScopeKeysAndValues:
out[val] = []byte(val)
}
- err = p.Exec(out, p.DataMap, k.TemplateAs, tpl.Target, p.TargetSecret)
+ err := p.Exec(out, p.DataMap, k.TemplateAs, tpl.Target, p.TargetSecret)
if err != nil {
return err
}
@@ -79,14 +88,20 @@ func (p *Parser) MergeSecret(ctx context.Context, namespace string, tpl esv1beta
if tpl.Secret == nil {
return nil
}
+
var sec v1.Secret
- err := p.Client.Get(ctx, types.NamespacedName{
- Name: tpl.Secret.Name,
- Namespace: namespace,
- }, &sec)
- if err != nil {
- return err
+ if p.TemplateFromSecret != nil {
+ sec = *p.TemplateFromSecret
+ } else {
+ err := p.Client.Get(ctx, types.NamespacedName{
+ Name: tpl.Secret.Name,
+ Namespace: namespace,
+ }, &sec)
+ if err != nil {
+ return err
+ }
}
+
for _, k := range tpl.Secret.Items {
val, ok := sec.Data[k.Key]
if !ok {
@@ -99,7 +114,7 @@ func (p *Parser) MergeSecret(ctx context.Context, namespace string, tpl esv1beta
case esv1beta1.TemplateScopeKeysAndValues:
out[string(val)] = val
}
- err = p.Exec(out, p.DataMap, k.TemplateAs, tpl.Target, p.TargetSecret)
+ err := p.Exec(out, p.DataMap, k.TemplateAs, tpl.Target, p.TargetSecret)
if err != nil {
return err
}
diff --git a/pkg/controllers/webhookconfig/suite_test.go b/pkg/controllers/webhookconfig/suite_test.go
index 76d2ef521b6..ff8b63ff452 100644
--- a/pkg/controllers/webhookconfig/suite_test.go
+++ b/pkg/controllers/webhookconfig/suite_test.go
@@ -31,6 +31,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/metrics/server"
esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ ctrlcommon "github.com/external-secrets/external-secrets/pkg/controllers/common"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
@@ -87,8 +88,17 @@ var _ = BeforeSuite(func() {
Expect(k8sClient).ToNot(BeNil())
leaderChan := make(chan struct{})
close(leaderChan)
- reconciler = New(k8sClient, k8sManager.GetScheme(), leaderChan, ctrl.Log, ctrlSvcName, ctrlSvcNamespace, ctrlSecretName, ctrlSecretNamespace, time.Second)
- reconciler.SetupWithManager(k8sManager, controller.Options{})
+ reconciler = New(k8sClient, k8sManager.GetScheme(), leaderChan, ctrl.Log, Opts{
+ SvcName: ctrlSvcName,
+ SvcNamespace: ctrlSvcNamespace,
+ SecretName: ctrlSecretName,
+ SecretNamespace: ctrlSecretNamespace,
+ RequeueInterval: time.Second,
+ })
+ err = reconciler.SetupWithManager(k8sManager, controller.Options{
+ MaxConcurrentReconciles: 1,
+ RateLimiter: ctrlcommon.BuildRateLimiter(),
+ })
Expect(err).ToNot(HaveOccurred())
go func() {
diff --git a/pkg/controllers/webhookconfig/webhookconfig.go b/pkg/controllers/webhookconfig/webhookconfig.go
index f1e42923a68..bc262458268 100644
--- a/pkg/controllers/webhookconfig/webhookconfig.go
+++ b/pkg/controllers/webhookconfig/webhookconfig.go
@@ -17,7 +17,7 @@ package webhookconfig
import (
"context"
"encoding/base64"
- "fmt"
+ "errors"
"net/http"
"strings"
"sync"
@@ -33,6 +33,8 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/controller"
+
+ "github.com/external-secrets/external-secrets/pkg/constants"
)
type Reconciler struct {
@@ -55,18 +57,24 @@ type Reconciler struct {
webhookReady bool
}
-func New(k8sClient client.Client, scheme *runtime.Scheme, leaderChan <-chan struct{},
- log logr.Logger, svcName, svcNamespace, secretName, secretNamespace string,
- requeueInterval time.Duration) *Reconciler {
+type Opts struct {
+ SvcName string
+ SvcNamespace string
+ SecretName string
+ SecretNamespace string
+ RequeueInterval time.Duration
+}
+
+func New(k8sClient client.Client, scheme *runtime.Scheme, leaderChan <-chan struct{}, log logr.Logger, opts Opts) *Reconciler {
return &Reconciler{
Client: k8sClient,
Scheme: scheme,
Log: log,
- RequeueDuration: requeueInterval,
- SvcName: svcName,
- SvcNamespace: svcNamespace,
- SecretName: secretName,
- SecretNamespace: secretNamespace,
+ RequeueDuration: opts.RequeueInterval,
+ SvcName: opts.SvcName,
+ SvcNamespace: opts.SvcNamespace,
+ SecretName: opts.SecretName,
+ SecretNamespace: opts.SecretNamespace,
leaderChan: leaderChan,
leaderElected: false,
webhookReadyMu: &sync.Mutex{},
@@ -75,9 +83,6 @@ func New(k8sClient client.Client, scheme *runtime.Scheme, leaderChan <-chan stru
}
const (
- wellKnownLabelKey = "external-secrets.io/component"
- wellKnownLabelValue = "webhook"
-
ReasonUpdateFailed = "UpdateFailed"
errWebhookNotReady = "webhook not ready"
errSubsetsNotReady = "subsets not ready"
@@ -98,8 +103,8 @@ func (r *Reconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Resu
return ctrl.Result{}, err
}
- if cfg.Labels[wellKnownLabelKey] != wellKnownLabelValue {
- log.Info("ignoring webhook due to missing labels", wellKnownLabelKey, wellKnownLabelValue)
+ if cfg.Labels[constants.WellKnownLabelKey] != constants.WellKnownLabelValueWebhook {
+ log.Info("ignoring webhook due to missing labels", constants.WellKnownLabelKey, constants.WellKnownLabelValueWebhook)
return ctrl.Result{}, nil
}
@@ -146,7 +151,7 @@ func (r *Reconciler) ReadyCheck(_ *http.Request) error {
r.webhookReadyMu.Lock()
defer r.webhookReadyMu.Unlock()
if !r.webhookReady {
- return fmt.Errorf(errWebhookNotReady)
+ return errors.New(errWebhookNotReady)
}
var eps v1.Endpoints
err := r.Get(context.TODO(), types.NamespacedName{
@@ -157,10 +162,10 @@ func (r *Reconciler) ReadyCheck(_ *http.Request) error {
return err
}
if len(eps.Subsets) == 0 {
- return fmt.Errorf(errSubsetsNotReady)
+ return errors.New(errSubsetsNotReady)
}
if len(eps.Subsets[0].Addresses) == 0 {
- return fmt.Errorf(errAddressesNotReady)
+ return errors.New(errAddressesNotReady)
}
return nil
}
@@ -179,7 +184,7 @@ func (r *Reconciler) updateConfig(ctx context.Context, cfg *admissionregistratio
crt, ok := secret.Data[caCertName]
if !ok {
- return fmt.Errorf(errCACertNotReady)
+ return errors.New(errCACertNotReady)
}
if err := r.inject(cfg, r.SvcName, r.SvcNamespace, crt); err != nil {
return err
diff --git a/pkg/controllers/webhookconfig/webhookconfig_test.go b/pkg/controllers/webhookconfig/webhookconfig_test.go
index c5aaa90127d..888252a68d7 100644
--- a/pkg/controllers/webhookconfig/webhookconfig_test.go
+++ b/pkg/controllers/webhookconfig/webhookconfig_test.go
@@ -25,6 +25,8 @@ import (
"k8s.io/apimachinery/pkg/types"
pointer "k8s.io/utils/ptr"
+ "github.com/external-secrets/external-secrets/pkg/constants"
+
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
@@ -126,7 +128,7 @@ var _ = Describe("ValidatingWebhookConfig reconcile", Ordered, func() {
}
IgnoreNoMatch := func(tc *testCase) {
- delete(tc.vwc.ObjectMeta.Labels, wellKnownLabelKey)
+ delete(tc.vwc.ObjectMeta.Labels, constants.WellKnownLabelKey)
tc.assert = func() {
Consistently(func() bool {
var vwc admissionregistration.ValidatingWebhookConfiguration
@@ -232,7 +234,7 @@ func makeValidatingWebhookConfig() *admissionregistration.ValidatingWebhookConfi
ObjectMeta: metav1.ObjectMeta{
Name: "name-shouldnt-matter",
Labels: map[string]string{
- wellKnownLabelKey: wellKnownLabelValue,
+ constants.WellKnownLabelKey: constants.WellKnownLabelValueWebhook,
},
},
Webhooks: []admissionregistration.ValidatingWebhook{
diff --git a/pkg/generator/acr/acr.go b/pkg/generator/acr/acr.go
index 5aaff55806d..fa6b0daa8ee 100644
--- a/pkg/generator/acr/acr.go
+++ b/pkg/generator/acr/acr.go
@@ -102,7 +102,7 @@ func (g *Generator) generate(
fetchAccessToken accessTokenFetcher,
fetchRefreshToken refreshTokenFetcher) (map[string][]byte, error) {
if jsonSpec == nil {
- return nil, fmt.Errorf(errNoSpec)
+ return nil, errors.New(errNoSpec)
}
res, err := parseSpec(jsonSpec.Raw)
if err != nil {
@@ -136,7 +136,7 @@ func (g *Generator) generate(
namespace,
)
} else {
- return nil, fmt.Errorf("unexpeted configuration")
+ return nil, errors.New("unexpeted configuration")
}
if err != nil {
return nil, err
@@ -187,7 +187,7 @@ func fetchACRAccessToken(acrRefreshToken, _, registryURL, scope string) (string,
}
accessToken, ok := payload["access_token"]
if !ok {
- return "", fmt.Errorf("unable to get token")
+ return "", errors.New("unable to get token")
}
return accessToken, nil
}
@@ -222,7 +222,7 @@ func fetchACRRefreshToken(aadAccessToken, tenantID, registryURL string) (string,
}
refreshToken, ok := payload["refresh_token"]
if !ok {
- return "", fmt.Errorf("unable to get token")
+ return "", errors.New("unable to get token")
}
return refreshToken, nil
}
@@ -282,12 +282,18 @@ func accessTokenForWorkloadIdentity(ctx context.Context, crClient client.Client,
}
func accessTokenForManagedIdentity(ctx context.Context, envType v1beta1.AzureEnvironmentType, identityID string) (string, error) {
- // handle workload identity
- creds, err := azidentity.NewManagedIdentityCredential(
- &azidentity.ManagedIdentityCredentialOptions{
+ // handle managed identity
+ var opts *azidentity.ManagedIdentityCredentialOptions
+ if strings.Contains(identityID, "/") {
+ opts = &azidentity.ManagedIdentityCredentialOptions{
ID: azidentity.ResourceID(identityID),
- },
- )
+ }
+ } else {
+ opts = &azidentity.ManagedIdentityCredentialOptions{
+ ID: azidentity.ClientID(identityID),
+ }
+ }
+ creds, err := azidentity.NewManagedIdentityCredential(opts)
if err != nil {
return "", err
}
diff --git a/pkg/generator/ecr/ecr.go b/pkg/generator/ecr/ecr.go
index e896c03d14c..578b492d33d 100644
--- a/pkg/generator/ecr/ecr.go
+++ b/pkg/generator/ecr/ecr.go
@@ -17,6 +17,7 @@ package ecr
import (
"context"
"encoding/base64"
+ "errors"
"fmt"
"strconv"
"strings"
@@ -24,6 +25,8 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ecr"
"github.com/aws/aws-sdk-go/service/ecr/ecriface"
+ "github.com/aws/aws-sdk-go/service/ecrpublic"
+ "github.com/aws/aws-sdk-go/service/ecrpublic/ecrpubliciface"
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/yaml"
@@ -36,14 +39,15 @@ import (
type Generator struct{}
const (
- errNoSpec = "no config spec provided"
- errParseSpec = "unable to parse spec: %w"
- errCreateSess = "unable to create aws session: %w"
- errGetToken = "unable to get authorization token: %w"
+ errNoSpec = "no config spec provided"
+ errParseSpec = "unable to parse spec: %w"
+ errCreateSess = "unable to create aws session: %w"
+ errGetPrivateToken = "unable to get authorization token: %w"
+ errGetPublicToken = "unable to get public authorization token: %w"
)
func (g *Generator) Generate(ctx context.Context, jsonSpec *apiextensions.JSON, kube client.Client, namespace string) (map[string][]byte, error) {
- return g.generate(ctx, jsonSpec, kube, namespace, ecrFactory)
+ return g.generate(ctx, jsonSpec, kube, namespace, ecrPrivateFactory, ecrPublicFactory)
}
func (g *Generator) generate(
@@ -51,10 +55,11 @@ func (g *Generator) generate(
jsonSpec *apiextensions.JSON,
kube client.Client,
namespace string,
- ecrFunc ecrFactoryFunc,
+ ecrPrivateFunc ecrPrivateFactoryFunc,
+ ecrPublicFunc ecrPublicFactoryFunc,
) (map[string][]byte, error) {
if jsonSpec == nil {
- return nil, fmt.Errorf(errNoSpec)
+ return nil, errors.New(errNoSpec)
}
res, err := parseSpec(jsonSpec.Raw)
if err != nil {
@@ -75,10 +80,19 @@ func (g *Generator) generate(
if err != nil {
return nil, fmt.Errorf(errCreateSess, err)
}
- client := ecrFunc(sess)
+
+ if res.Spec.Scope == "public" {
+ return fetchECRPublicToken(sess, ecrPublicFunc)
+ }
+
+ return fetchECRPrivateToken(sess, ecrPrivateFunc)
+}
+
+func fetchECRPrivateToken(sess *session.Session, ecrPrivateFunc ecrPrivateFactoryFunc) (map[string][]byte, error) {
+ client := ecrPrivateFunc(sess)
out, err := client.GetAuthorizationToken(&ecr.GetAuthorizationTokenInput{})
if err != nil {
- return nil, fmt.Errorf(errGetToken, err)
+ return nil, fmt.Errorf(errGetPrivateToken, err)
}
if len(out.AuthorizationData) != 1 {
return nil, fmt.Errorf("unexpected number of authorization tokens. expected 1, found %d", len(out.AuthorizationData))
@@ -91,7 +105,7 @@ func (g *Generator) generate(
}
parts := strings.Split(string(decodedToken), ":")
if len(parts) != 2 {
- return nil, fmt.Errorf("unexpected token format")
+ return nil, errors.New("unexpected token format")
}
exp := out.AuthorizationData[0].ExpiresAt.UTC().Unix()
@@ -103,12 +117,41 @@ func (g *Generator) generate(
}, nil
}
-type ecrFactoryFunc func(aws *session.Session) ecriface.ECRAPI
+func fetchECRPublicToken(sess *session.Session, ecrPublicFunc ecrPublicFactoryFunc) (map[string][]byte, error) {
+ client := ecrPublicFunc(sess)
+ out, err := client.GetAuthorizationToken(&ecrpublic.GetAuthorizationTokenInput{})
+ if err != nil {
+ return nil, fmt.Errorf(errGetPublicToken, err)
+ }
-func ecrFactory(aws *session.Session) ecriface.ECRAPI {
+ decodedToken, err := base64.StdEncoding.DecodeString(*out.AuthorizationData.AuthorizationToken)
+ if err != nil {
+ return nil, err
+ }
+ parts := strings.Split(string(decodedToken), ":")
+ if len(parts) != 2 {
+ return nil, errors.New("unexpected token format")
+ }
+
+ exp := out.AuthorizationData.ExpiresAt.UTC().Unix()
+ return map[string][]byte{
+ "username": []byte(parts[0]),
+ "password": []byte(parts[1]),
+ "expires_at": []byte(strconv.FormatInt(exp, 10)),
+ }, nil
+}
+
+type ecrPrivateFactoryFunc func(aws *session.Session) ecriface.ECRAPI
+type ecrPublicFactoryFunc func(aws *session.Session) ecrpubliciface.ECRPublicAPI
+
+func ecrPrivateFactory(aws *session.Session) ecriface.ECRAPI {
return ecr.New(aws)
}
+func ecrPublicFactory(aws *session.Session) ecrpubliciface.ECRPublicAPI {
+ return ecrpublic.New(aws)
+}
+
func parseSpec(data []byte) (*genv1alpha1.ECRAuthorizationToken, error) {
var spec genv1alpha1.ECRAuthorizationToken
err := yaml.Unmarshal(data, &spec)
diff --git a/pkg/generator/ecr/ecr_test.go b/pkg/generator/ecr/ecr_test.go
index e16d7553bdd..1c9714db201 100644
--- a/pkg/generator/ecr/ecr_test.go
+++ b/pkg/generator/ecr/ecr_test.go
@@ -25,6 +25,8 @@ import (
"github.com/aws/aws-sdk-go/aws/session"
"github.com/aws/aws-sdk-go/service/ecr"
"github.com/aws/aws-sdk-go/service/ecr/ecriface"
+ "github.com/aws/aws-sdk-go/service/ecrpublic"
+ "github.com/aws/aws-sdk-go/service/ecrpublic/ecrpubliciface"
v1 "k8s.io/api/core/v1"
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -35,11 +37,12 @@ import (
func TestGenerate(t *testing.T) {
type args struct {
- ctx context.Context
- jsonSpec *apiextensions.JSON
- kube client.Client
- namespace string
- authTokenFunc func(*ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error)
+ ctx context.Context
+ jsonSpec *apiextensions.JSON
+ kube client.Client
+ namespace string
+ authTokenPrivateFunc func(*ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error)
+ authTokenPublicFunc func(*ecrpublic.GetAuthorizationTokenInput) (*ecrpublic.GetAuthorizationTokenOutput, error)
}
tests := []struct {
name string
@@ -58,7 +61,7 @@ func TestGenerate(t *testing.T) {
{
name: "invalid json",
args: args{
- authTokenFunc: func(gati *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) {
+ authTokenPrivateFunc: func(gati *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) {
return nil, errors.New("boom")
},
jsonSpec: &apiextensions.JSON{
@@ -68,7 +71,7 @@ func TestGenerate(t *testing.T) {
wantErr: true,
},
{
- name: "full spec",
+ name: "private ECR full spec",
args: args{
namespace: "foobar",
kube: clientfake.NewClientBuilder().WithObjects(&v1.Secret{
@@ -81,7 +84,7 @@ func TestGenerate(t *testing.T) {
"access-secret": []byte("bar"),
},
}).Build(),
- authTokenFunc: func(in *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) {
+ authTokenPrivateFunc: func(in *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) {
t := time.Unix(1234, 0)
return &ecr.GetAuthorizationTokenOutput{
AuthorizationData: []*ecr.AuthorizationData{
@@ -99,6 +102,7 @@ kind: ECRAuthorizationToken
spec:
region: eu-west-1
role: "my-role"
+ scope: private
auth:
secretRef:
accessKeyIDSecretRef:
@@ -116,6 +120,34 @@ spec:
"expires_at": []byte("1234"),
},
},
+ {
+ name: "public ECR full spec",
+ args: args{
+ namespace: "foobar",
+ authTokenPublicFunc: func(in *ecrpublic.GetAuthorizationTokenInput) (*ecrpublic.GetAuthorizationTokenOutput, error) {
+ t := time.Unix(5678, 0)
+ return &ecrpublic.GetAuthorizationTokenOutput{
+ AuthorizationData: &ecrpublic.AuthorizationData{
+ AuthorizationToken: utilpointer.To(base64.StdEncoding.EncodeToString([]byte("pubuser:pubpass"))),
+ ExpiresAt: &t,
+ },
+ }, nil
+ },
+ jsonSpec: &apiextensions.JSON{
+ Raw: []byte(`apiVersion: generators.external-secrets.io/v1alpha1
+kind: ECRAuthorizationToken
+spec:
+ region: us-east-1
+ role: "my-role"
+ scope: public`),
+ },
+ },
+ want: map[string][]byte{
+ "username": []byte("pubuser"),
+ "password": []byte("pubpass"),
+ "expires_at": []byte("5678"),
+ },
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -126,8 +158,13 @@ spec:
tt.args.kube,
tt.args.namespace,
func(aws *session.Session) ecriface.ECRAPI {
- return &FakeECR{
- authTokenFunc: tt.args.authTokenFunc,
+ return &FakeECRPrivate{
+ authTokenFunc: tt.args.authTokenPrivateFunc,
+ }
+ },
+ func(aws *session.Session) ecrpubliciface.ECRPublicAPI {
+ return &FakeECRPublic{
+ authTokenFunc: tt.args.authTokenPublicFunc,
}
},
)
@@ -142,11 +179,20 @@ spec:
}
}
-type FakeECR struct {
+type FakeECRPrivate struct {
ecriface.ECRAPI
authTokenFunc func(*ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error)
}
-func (e *FakeECR) GetAuthorizationToken(in *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) {
+func (e *FakeECRPrivate) GetAuthorizationToken(in *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) {
+ return e.authTokenFunc(in)
+}
+
+type FakeECRPublic struct {
+ ecrpubliciface.ECRPublicAPI
+ authTokenFunc func(*ecrpublic.GetAuthorizationTokenInput) (*ecrpublic.GetAuthorizationTokenOutput, error)
+}
+
+func (e *FakeECRPublic) GetAuthorizationToken(in *ecrpublic.GetAuthorizationTokenInput) (*ecrpublic.GetAuthorizationTokenOutput, error) {
return e.authTokenFunc(in)
}
diff --git a/pkg/generator/fake/fake.go b/pkg/generator/fake/fake.go
index 88ca9b9f5a1..4ddf54dcc72 100644
--- a/pkg/generator/fake/fake.go
+++ b/pkg/generator/fake/fake.go
@@ -16,6 +16,7 @@ package fake
import (
"context"
+ "errors"
"fmt"
apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
@@ -35,7 +36,7 @@ const (
func (g *Generator) Generate(_ context.Context, jsonSpec *apiextensions.JSON, _ client.Client, _ string) (map[string][]byte, error) {
if jsonSpec == nil {
- return nil, fmt.Errorf(errNoSpec)
+ return nil, errors.New(errNoSpec)
}
res, err := parseSpec(jsonSpec.Raw)
if err != nil {
diff --git a/pkg/generator/gcr/gcr.go b/pkg/generator/gcr/gcr.go
index 8264b84e10a..11e9b0cdddf 100644
--- a/pkg/generator/gcr/gcr.go
+++ b/pkg/generator/gcr/gcr.go
@@ -16,6 +16,7 @@ package gcr
import (
"context"
+ "errors"
"fmt"
"strconv"
@@ -57,7 +58,7 @@ func (g *Generator) generate(
namespace string,
tokenSource tokenSourceFunc) (map[string][]byte, error) {
if jsonSpec == nil {
- return nil, fmt.Errorf(errNoSpec)
+ return nil, errors.New(errNoSpec)
}
res, err := parseSpec(jsonSpec.Raw)
if err != nil {
diff --git a/pkg/generator/github/github.go b/pkg/generator/github/github.go
index 4859776fb4d..672aad3fed2 100644
--- a/pkg/generator/github/github.go
+++ b/pkg/generator/github/github.go
@@ -15,10 +15,13 @@ limitations under the License.
package github
import (
+ "bytes"
"context"
"crypto/rsa"
"encoding/json"
+ "errors"
"fmt"
+ "io"
"net/http"
"time"
@@ -36,11 +39,13 @@ type Generator struct {
}
type Github struct {
- HTTP *http.Client
- Kube client.Client
- Namespace string
- URL string
- InstallTkn string
+ HTTP *http.Client
+ Kube client.Client
+ Namespace string
+ URL string
+ InstallTkn string
+ Repositories []string
+ Permissions map[string]string
}
const (
@@ -70,7 +75,7 @@ func (g *Generator) generate(
kube client.Client,
namespace string) (map[string][]byte, error) {
if jsonSpec == nil {
- return nil, fmt.Errorf(errNoSpec)
+ return nil, errors.New(errNoSpec)
}
ctx, cancel := context.WithTimeout(ctx, contextTimeout)
defer cancel()
@@ -79,8 +84,27 @@ func (g *Generator) generate(
if err != nil {
return nil, fmt.Errorf("error creating request: %w", err)
}
+
+ payload := make(map[string]interface{})
+ if gh.Permissions != nil {
+ payload["permissions"] = gh.Permissions
+ }
+ if len(gh.Repositories) > 0 {
+ payload["repositories"] = gh.Repositories
+ }
+
+ var body io.Reader = http.NoBody
+ if len(payload) > 0 {
+ bodyBytes, err := json.Marshal(payload)
+ if err != nil {
+ return nil, fmt.Errorf("error marshaling payload: %w", err)
+ }
+
+ body = bytes.NewReader(bodyBytes)
+ }
+
// Github api expects POST request
- req, err := http.NewRequestWithContext(ctx, http.MethodPost, gh.URL, http.NoBody)
+ req, err := http.NewRequestWithContext(ctx, http.MethodPost, gh.URL, body)
if err != nil {
return nil, fmt.Errorf("error creating request: %w", err)
}
@@ -101,7 +125,7 @@ func (g *Generator) generate(
accessToken, ok := gat["token"].(string)
if !ok {
- return nil, fmt.Errorf("token isn't a string or token key doesn't exist")
+ return nil, errors.New("token isn't a string or token key doesn't exist")
}
return map[string][]byte{
defaultLoginUsername: []byte(accessToken),
@@ -119,7 +143,13 @@ func newGHClient(ctx context.Context, k client.Client, n string, hc *http.Client
if err != nil {
return nil, fmt.Errorf(errParseSpec, err)
}
- gh := &Github{Kube: k, Namespace: n, HTTP: hc}
+ gh := &Github{
+ Kube: k,
+ Namespace: n,
+ HTTP: hc,
+ Repositories: res.Spec.Repositories,
+ Permissions: res.Spec.Permissions,
+ }
ghPath := fmt.Sprintf("/app/installations/%s/access_tokens", res.Spec.InstallID)
gh.URL = defaultGithubAPI + ghPath
@@ -127,11 +157,11 @@ func newGHClient(ctx context.Context, k client.Client, n string, hc *http.Client
gh.URL = res.Spec.URL + ghPath
}
secret := &corev1.Secret{}
- if err := gh.Kube.Get(ctx, client.ObjectKey{Name: res.Spec.Auth.PrivatKey.SecretRef.Name, Namespace: n}, secret); err != nil {
+ if err := gh.Kube.Get(ctx, client.ObjectKey{Name: res.Spec.Auth.PrivateKey.SecretRef.Name, Namespace: n}, secret); err != nil {
return nil, fmt.Errorf("error getting GH pem from secret:%w", err)
}
- pk, err := jwt.ParseRSAPrivateKeyFromPEM(secret.Data[res.Spec.Auth.PrivatKey.SecretRef.Key])
+ pk, err := jwt.ParseRSAPrivateKeyFromPEM(secret.Data[res.Spec.Auth.PrivateKey.SecretRef.Key])
if err != nil {
return nil, fmt.Errorf("error parsing RSA private key: %w", err)
}
diff --git a/pkg/generator/github/github_test.go b/pkg/generator/github/github_test.go
index 665c2b8629d..5dc9f185f65 100644
--- a/pkg/generator/github/github_test.go
+++ b/pkg/generator/github/github_test.go
@@ -38,7 +38,7 @@ const (
func testHTTPSrv(t *testing.T, r []byte) *httptest.Server {
return httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
assert.Equal(t, "POST", req.Method, "Expected POST request")
- assert.Empty(t, req.Body)
+ assert.NotEmpty(t, req.Body)
assert.NotEmpty(t, req.Header.Get("Authorization"))
assert.Equal(t, "application/vnd.github.v3+json", req.Header.Get("Accept"))
@@ -54,15 +54,19 @@ func TestGenerate(t *testing.T) {
namespace string
}
pem, err := os.ReadFile(tstCrtName)
- assert.NoError(t, err, "Should not error when reading privatKey")
+ assert.NoError(t, err, "Should not error when reading privateKey")
validResponce := []byte(`{
"token": "ghs_16C7e42F292c6912E7710c838347Ae178B4a",
"expires_at": "2016-07-11T22:14:10Z",
"permissions": {
- "issues": "write",
"contents": "read"
},
+ "repositories": [
+ {
+ "id": 10000
+ }
+ ],
"repository_selection": "selected"
}`)
@@ -93,7 +97,7 @@ func TestGenerate(t *testing.T) {
Namespace: "foo",
},
Data: map[string][]byte{
- "privatKey": pem,
+ "privateKey": pem,
},
}).Build(),
jsonSpec: &apiextensions.JSON{
@@ -103,12 +107,16 @@ spec:
appID: "0000000"
installID: "00000000"
URL: %q
+ repositories:
+ - "Hello-World"
+ permissions:
+ contents: "read"
auth:
- privatKey:
+ privateKey:
secretRef:
name: "testName"
namespace: "foo"
- key: "privatKey"`, server.URL)),
+ key: "privateKey"`, server.URL)),
},
},
want: map[string][]byte{
diff --git a/pkg/generator/password/password.go b/pkg/generator/password/password.go
index 5b48e0102d0..312d7ef103a 100644
--- a/pkg/generator/password/password.go
+++ b/pkg/generator/password/password.go
@@ -16,6 +16,7 @@ package password
import (
"context"
+ "errors"
"fmt"
"github.com/sethvargo/go-password/password"
@@ -57,7 +58,7 @@ func (g *Generator) Generate(_ context.Context, jsonSpec *apiextensions.JSON, _
func (g *Generator) generate(jsonSpec *apiextensions.JSON, passGen generateFunc) (map[string][]byte, error) {
if jsonSpec == nil {
- return nil, fmt.Errorf(errNoSpec)
+ return nil, errors.New(errNoSpec)
}
res, err := parseSpec(jsonSpec.Raw)
if err != nil {
diff --git a/pkg/generator/password/password_test.go b/pkg/generator/password/password_test.go
index d334351a023..baf0c622798 100644
--- a/pkg/generator/password/password_test.go
+++ b/pkg/generator/password/password_test.go
@@ -15,7 +15,7 @@ limitations under the License.
package password
import (
- "fmt"
+ "errors"
"reflect"
"testing"
@@ -103,7 +103,7 @@ func TestGenerate(t *testing.T) {
},
passGen: func(len int, symbols int, symbolCharacters string, digits int, noUpper bool, allowRepeat bool,
) (string, error) {
- return "", fmt.Errorf("boom")
+ return "", errors.New("boom")
},
},
wantErr: true,
diff --git a/pkg/generator/quay/quay.go b/pkg/generator/quay/quay.go
new file mode 100644
index 00000000000..10abe352282
--- /dev/null
+++ b/pkg/generator/quay/quay.go
@@ -0,0 +1,211 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package quay
+
+import (
+ "context"
+ b64 "encoding/base64"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "strconv"
+ "strings"
+ "time"
+
+ authv1 "k8s.io/api/authentication/v1"
+ apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/client-go/kubernetes"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ ctrlcfg "sigs.k8s.io/controller-runtime/pkg/client/config"
+ "sigs.k8s.io/yaml"
+
+ genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
+ esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+)
+
+type Generator struct {
+ httpClient *http.Client
+}
+
+const (
+ defaultQuayURL = "quay.io"
+
+ errNoSpec = "no config spec provided"
+ errParseSpec = "unable to parse spec: %w"
+ errGetToken = "unable to get authorization token: %w"
+
+ httpClientTimeout = 5 * time.Second
+)
+
+func (g *Generator) Generate(ctx context.Context, jsonSpec *apiextensions.JSON, kube client.Client, namespace string) (map[string][]byte, error) {
+ return g.generate(
+ ctx,
+ jsonSpec,
+ kube,
+ namespace,
+ )
+}
+
+func (g *Generator) generate(
+ ctx context.Context,
+ jsonSpec *apiextensions.JSON,
+ _ client.Client,
+ namespace string) (map[string][]byte, error) {
+ if jsonSpec == nil {
+ return nil, errors.New(errNoSpec)
+ }
+ res, err := parseSpec(jsonSpec.Raw)
+ if err != nil {
+ return nil, fmt.Errorf(errParseSpec, err)
+ }
+
+ // Fetch the service account token
+ token, err := fetchServiceAccountToken(ctx, res.Spec.ServiceAccountRef, namespace)
+ if err != nil {
+ return nil, fmt.Errorf("failed to fetch service account token: %w", err)
+ }
+ url := res.Spec.URL
+ if url == "" {
+ url = defaultQuayURL
+ }
+ url = strings.TrimPrefix(url, "https://")
+
+ accessToken, err := getQuayRobotToken(ctx, token, res.Spec.RobotAccount, url, g.httpClient)
+ if err != nil {
+ return nil, err
+ }
+ exp, err := tokenExpiration(accessToken)
+ if err != nil {
+ return nil, err
+ }
+ return map[string][]byte{
+ "registry": []byte(url),
+ "auth": []byte(b64.StdEncoding.EncodeToString([]byte(res.Spec.RobotAccount + ":" + accessToken))),
+ "expiry": []byte(exp),
+ }, nil
+}
+
+func getClaims(tokenString string) (map[string]interface{}, error) {
+ // Split the token into its three parts
+ parts := strings.Split(tokenString, ".")
+ if len(parts) != 3 {
+ return nil, fmt.Errorf("invalid token format")
+ }
+
+ // Decode the payload (the second part of the token)
+ payload, err := b64.RawURLEncoding.DecodeString(parts[1])
+ if err != nil {
+ return nil, fmt.Errorf("error decoding payload: %w", err)
+ }
+
+ var claims map[string]interface{}
+ if err := json.Unmarshal(payload, &claims); err != nil {
+ return nil, fmt.Errorf("error un-marshaling claims: %w", err)
+ }
+ return claims, nil
+}
+
+func tokenExpiration(tokenString string) (string, error) {
+ claims, err := getClaims(tokenString)
+ if err != nil {
+ return "", fmt.Errorf("error getting claims: %w", err)
+ }
+ exp, ok := claims["exp"].(float64)
+ if ok {
+ return strconv.FormatFloat(exp, 'f', -1, 64), nil
+ }
+
+ return "", fmt.Errorf("exp claim not found or wrong type")
+}
+
+// https://docs.projectquay.io/manage_quay.html#exchanging-oauth2-robot-account-token
+func getQuayRobotToken(ctx context.Context, fedToken, robotAccount, url string, hc *http.Client) (string, error) {
+ if hc == nil {
+ hc = &http.Client{
+ Timeout: httpClientTimeout,
+ }
+ }
+
+ req, err := http.NewRequestWithContext(ctx, "GET", "https://"+url+"/oauth2/federation/robot/token", http.NoBody)
+ if err != nil {
+ return "", err
+ }
+ req.SetBasicAuth(robotAccount, fedToken)
+ resp, err := hc.Do(req)
+ if err != nil {
+ return "", err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != 200 {
+ return "", fmt.Errorf("request failed do to unexpected status: %s", resp.Status)
+ }
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return "", err
+ }
+
+ var result map[string]interface{}
+
+ err = json.Unmarshal(body, &result)
+ if err != nil {
+ return "", err
+ }
+ token, ok := result["token"]
+ if !ok {
+ return "", fmt.Errorf("token not found in response")
+ }
+ tokenString, ok := token.(string)
+ if !ok {
+ return "", fmt.Errorf("error when typecasting token to string")
+ }
+ return tokenString, nil
+}
+
+func fetchServiceAccountToken(ctx context.Context, saRef esmeta.ServiceAccountSelector, namespace string) (string, error) {
+ cfg, err := ctrlcfg.GetConfig()
+ if err != nil {
+ return "", err
+ }
+ kubeClient, err := kubernetes.NewForConfig(cfg)
+ if err != nil {
+ return "", fmt.Errorf("failed to create kubernetes client: %w", err)
+ }
+
+ tokenRequest := &authv1.TokenRequest{
+ Spec: authv1.TokenRequestSpec{
+ Audiences: saRef.Audiences,
+ },
+ }
+ tokenResponse, err := kubeClient.CoreV1().ServiceAccounts(namespace).CreateToken(ctx, saRef.Name, tokenRequest, metav1.CreateOptions{})
+ if err != nil {
+ return "", fmt.Errorf("failed to create token: %w", err)
+ }
+ return tokenResponse.Status.Token, nil
+}
+
+func parseSpec(data []byte) (*genv1alpha1.QuayAccessToken, error) {
+ var spec genv1alpha1.QuayAccessToken
+ err := yaml.Unmarshal(data, &spec)
+ return &spec, err
+}
+
+func init() {
+ genv1alpha1.Register(genv1alpha1.QuayAccessTokenKind, &Generator{})
+}
diff --git a/pkg/generator/register/register.go b/pkg/generator/register/register.go
index 876c4578b10..223ec3f519a 100644
--- a/pkg/generator/register/register.go
+++ b/pkg/generator/register/register.go
@@ -23,6 +23,9 @@ import (
_ "github.com/external-secrets/external-secrets/pkg/generator/gcr"
_ "github.com/external-secrets/external-secrets/pkg/generator/github"
_ "github.com/external-secrets/external-secrets/pkg/generator/password"
+ _ "github.com/external-secrets/external-secrets/pkg/generator/quay"
+ _ "github.com/external-secrets/external-secrets/pkg/generator/sts"
+ _ "github.com/external-secrets/external-secrets/pkg/generator/uuid"
_ "github.com/external-secrets/external-secrets/pkg/generator/vault"
_ "github.com/external-secrets/external-secrets/pkg/generator/webhook"
)
diff --git a/pkg/generator/sts/sts.go b/pkg/generator/sts/sts.go
new file mode 100644
index 00000000000..0645b023d1e
--- /dev/null
+++ b/pkg/generator/sts/sts.go
@@ -0,0 +1,114 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package sts
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "strconv"
+
+ "github.com/aws/aws-sdk-go/aws/session"
+ "github.com/aws/aws-sdk-go/service/sts"
+ "github.com/aws/aws-sdk-go/service/sts/stsiface"
+ apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/yaml"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
+ awsauth "github.com/external-secrets/external-secrets/pkg/provider/aws/auth"
+)
+
+type Generator struct{}
+
+const (
+ errNoSpec = "no config spec provided"
+ errParseSpec = "unable to parse spec: %w"
+ errCreateSess = "unable to create aws session: %w"
+ errGetToken = "unable to get authorization token: %w"
+)
+
+func (g *Generator) Generate(ctx context.Context, jsonSpec *apiextensions.JSON, kube client.Client, namespace string) (map[string][]byte, error) {
+ return g.generate(ctx, jsonSpec, kube, namespace, stsFactory)
+}
+
+func (g *Generator) generate(
+ ctx context.Context,
+ jsonSpec *apiextensions.JSON,
+ kube client.Client,
+ namespace string,
+ stsFunc stsFactoryFunc,
+) (map[string][]byte, error) {
+ if jsonSpec == nil {
+ return nil, errors.New(errNoSpec)
+ }
+ res, err := parseSpec(jsonSpec.Raw)
+ if err != nil {
+ return nil, fmt.Errorf(errParseSpec, err)
+ }
+ sess, err := awsauth.NewGeneratorSession(
+ ctx,
+ esv1beta1.AWSAuth{
+ SecretRef: (*esv1beta1.AWSAuthSecretRef)(res.Spec.Auth.SecretRef),
+ JWTAuth: (*esv1beta1.AWSJWTAuth)(res.Spec.Auth.JWTAuth),
+ },
+ res.Spec.Role,
+ res.Spec.Region,
+ kube,
+ namespace,
+ awsauth.DefaultSTSProvider,
+ awsauth.DefaultJWTProvider)
+ if err != nil {
+ return nil, fmt.Errorf(errCreateSess, err)
+ }
+ client := stsFunc(sess)
+ input := &sts.GetSessionTokenInput{}
+ if res.Spec.RequestParameters != nil {
+ input.DurationSeconds = res.Spec.RequestParameters.SessionDuration
+ input.TokenCode = res.Spec.RequestParameters.TokenCode
+ input.SerialNumber = res.Spec.RequestParameters.SerialNumber
+ }
+ out, err := client.GetSessionToken(input)
+ if err != nil {
+ return nil, fmt.Errorf(errGetToken, err)
+ }
+ if out.Credentials == nil {
+ return nil, errors.New("no credentials found")
+ }
+
+ return map[string][]byte{
+ "access_key_id": []byte(*out.Credentials.AccessKeyId),
+ "expiration": []byte(strconv.FormatInt(out.Credentials.Expiration.Unix(), 10)),
+ "secret_access_key": []byte(*out.Credentials.SecretAccessKey),
+ "session_token": []byte(*out.Credentials.SessionToken),
+ }, nil
+}
+
+type stsFactoryFunc func(aws *session.Session) stsiface.STSAPI
+
+func stsFactory(aws *session.Session) stsiface.STSAPI {
+ return sts.New(aws)
+}
+
+func parseSpec(data []byte) (*genv1alpha1.STSSessionToken, error) {
+ var spec genv1alpha1.STSSessionToken
+ err := yaml.Unmarshal(data, &spec)
+ return &spec, err
+}
+
+func init() {
+ genv1alpha1.Register(genv1alpha1.STSSessionTokenKind, &Generator{})
+}
diff --git a/pkg/generator/sts/sts_test.go b/pkg/generator/sts/sts_test.go
new file mode 100644
index 00000000000..f732fecb369
--- /dev/null
+++ b/pkg/generator/sts/sts_test.go
@@ -0,0 +1,151 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package sts
+
+import (
+ "context"
+ "errors"
+ "reflect"
+ "testing"
+ "time"
+
+ "github.com/aws/aws-sdk-go/aws/session"
+ "github.com/aws/aws-sdk-go/service/sts"
+ "github.com/aws/aws-sdk-go/service/sts/stsiface"
+ v1 "k8s.io/api/core/v1"
+ apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+ "github.com/external-secrets/external-secrets/pkg/utils"
+)
+
+func TestGenerate(t *testing.T) {
+ type args struct {
+ ctx context.Context
+ jsonSpec *apiextensions.JSON
+ kube client.Client
+ namespace string
+ tokenFunc func(*sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error)
+ }
+ tests := []struct {
+ name string
+ g *Generator
+ args args
+ want map[string][]byte
+ wantErr bool
+ }{
+ {
+ name: "nil spec",
+ args: args{
+ jsonSpec: nil,
+ },
+ wantErr: true,
+ },
+ {
+ name: "invalid json",
+ args: args{
+ tokenFunc: func(*sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error) {
+ return nil, errors.New("boom")
+ },
+ jsonSpec: &apiextensions.JSON{
+ Raw: []byte(``),
+ },
+ },
+ wantErr: true,
+ },
+ {
+ name: "full spec",
+ args: args{
+ namespace: "foobar",
+ kube: clientfake.NewClientBuilder().WithObjects(&v1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "my-aws-creds",
+ Namespace: "foobar",
+ },
+ Data: map[string][]byte{
+ "key-id": []byte("foo"),
+ "access-secret": []byte("bar"),
+ },
+ }).Build(),
+ tokenFunc: func(*sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error) {
+ t := time.Unix(1234, 0)
+ return &sts.GetSessionTokenOutput{
+ Credentials: &sts.Credentials{
+ AccessKeyId: utils.Ptr("access-key-id"),
+ Expiration: utils.Ptr(t),
+ SecretAccessKey: utils.Ptr("secret-access-key"),
+ SessionToken: utils.Ptr("session-token"),
+ },
+ }, nil
+ },
+ jsonSpec: &apiextensions.JSON{
+ Raw: []byte(`apiVersion: generators.external-secrets.io/v1alpha1
+kind: STSSessionToken
+spec:
+ region: eu-west-1
+ role: "my-role"
+ auth:
+ secretRef:
+ accessKeyIDSecretRef:
+ name: "my-aws-creds"
+ key: "key-id"
+ secretAccessKeySecretRef:
+ name: "my-aws-creds"
+ key: "access-secret"`),
+ },
+ },
+ want: map[string][]byte{
+ "access_key_id": []byte("access-key-id"),
+ "expiration": []byte("1234"),
+ "secret_access_key": []byte("secret-access-key"),
+ "session_token": []byte("session-token"),
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ g := &Generator{}
+ got, err := g.generate(
+ tt.args.ctx,
+ tt.args.jsonSpec,
+ tt.args.kube,
+ tt.args.namespace,
+ func(aws *session.Session) stsiface.STSAPI {
+ return &FakeSTS{
+ getSessionToken: tt.args.tokenFunc,
+ }
+ },
+ )
+ if (err != nil) != tt.wantErr {
+ t.Errorf("Generator.Generate() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("Generator.Generate() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+type FakeSTS struct {
+ stsiface.STSAPI
+ getSessionToken func(*sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error)
+}
+
+func (e *FakeSTS) GetSessionToken(in *sts.GetSessionTokenInput) (*sts.GetSessionTokenOutput, error) {
+ return e.getSessionToken(in)
+}
diff --git a/pkg/generator/uuid/uuid.go b/pkg/generator/uuid/uuid.go
new file mode 100644
index 00000000000..e8b455e28ec
--- /dev/null
+++ b/pkg/generator/uuid/uuid.go
@@ -0,0 +1,56 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package uuid
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/google/uuid"
+ apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+
+ genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
+)
+
+type Generator struct{}
+
+type generateFunc func() (string, error)
+
+func (g *Generator) Generate(_ context.Context, jsonSpec *apiextensions.JSON, _ client.Client, _ string) (map[string][]byte, error) {
+ return g.generate(
+ jsonSpec,
+ generateUUID,
+ )
+}
+
+func (g *Generator) generate(_ *apiextensions.JSON, uuidGen generateFunc) (map[string][]byte, error) {
+ uuid, err := uuidGen()
+ if err != nil {
+ return nil, fmt.Errorf("unable to generate UUID: %w", err)
+ }
+ return map[string][]byte{
+ "uuid": []byte(uuid),
+ }, nil
+}
+
+func generateUUID() (string, error) {
+ uuid := uuid.New()
+ return uuid.String(), nil
+}
+
+func init() {
+ genv1alpha1.Register(genv1alpha1.UUIDKind, &Generator{})
+}
diff --git a/pkg/generator/uuid/uuid_test.go b/pkg/generator/uuid/uuid_test.go
new file mode 100644
index 00000000000..5fe9db162f0
--- /dev/null
+++ b/pkg/generator/uuid/uuid_test.go
@@ -0,0 +1,63 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package uuid
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+)
+
+func TestGenerate(t *testing.T) {
+ type args struct {
+ jsonSpec *apiextensions.JSON
+ }
+ tests := []struct {
+ name string
+ g *Generator
+ args args
+ wantErr bool
+ }{
+ {
+ name: "generate UUID successfully",
+ args: args{
+ jsonSpec: &apiextensions.JSON{Raw: []byte(`{}`)},
+ },
+ wantErr: false,
+ },
+ {
+ name: "no json spec should not result in error",
+ args: args{
+ jsonSpec: nil,
+ },
+ wantErr: false,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ g := &Generator{}
+ got, err := g.generate(tt.args.jsonSpec, generateUUID)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("Generator.Generate() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if err == nil {
+ // Basic validation that the generated string looks like a UUID
+ assert.Regexp(t, `[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}`, string(got["uuid"]), "Generated string must be a valid UUID")
+ }
+ })
+ }
+}
diff --git a/pkg/generator/vault/vault.go b/pkg/generator/vault/vault.go
index c397f42b300..4ab236325bd 100644
--- a/pkg/generator/vault/vault.go
+++ b/pkg/generator/vault/vault.go
@@ -17,6 +17,7 @@ package vaultdynamic
import (
"context"
"encoding/json"
+ "errors"
"fmt"
vault "github.com/hashicorp/vault/api"
@@ -29,6 +30,7 @@ import (
genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
provider "github.com/external-secrets/external-secrets/pkg/provider/vault"
+ "github.com/external-secrets/external-secrets/pkg/provider/vault/util"
"github.com/external-secrets/external-secrets/pkg/utils"
)
@@ -61,42 +63,31 @@ func (g *Generator) Generate(ctx context.Context, jsonSpec *apiextensions.JSON,
func (g *Generator) generate(ctx context.Context, c *provider.Provider, jsonSpec *apiextensions.JSON, kube client.Client, corev1 typedcorev1.CoreV1Interface, namespace string) (map[string][]byte, error) {
if jsonSpec == nil {
- return nil, fmt.Errorf(errNoSpec)
+ return nil, errors.New(errNoSpec)
}
res, err := parseSpec(jsonSpec.Raw)
if err != nil {
return nil, fmt.Errorf(errParseSpec, err)
}
if res == nil || res.Spec.Provider == nil {
- return nil, fmt.Errorf("no Vault provider config in spec")
+ return nil, errors.New("no Vault provider config in spec")
}
- cl, err := c.NewGeneratorClient(ctx, kube, corev1, res.Spec.Provider, namespace)
+ cl, err := c.NewGeneratorClient(ctx, kube, corev1, res.Spec.Provider, namespace, res.Spec.RetrySettings)
if err != nil {
return nil, fmt.Errorf(errVaultClient, err)
}
- var result *vault.Secret
- if res.Spec.Method == "" || res.Spec.Method == "GET" {
- result, err = cl.Logical().ReadWithDataWithContext(ctx, res.Spec.Path, nil)
- } else if res.Spec.Method == "LIST" {
- result, err = cl.Logical().ListWithContext(ctx, res.Spec.Path)
- } else if res.Spec.Method == "DELETE" {
- result, err = cl.Logical().DeleteWithContext(ctx, res.Spec.Path)
- } else {
- params := make(map[string]any)
- if res.Spec.Parameters != nil {
- err = json.Unmarshal(res.Spec.Parameters.Raw, ¶ms)
- if err != nil {
- return nil, err
- }
- }
- result, err = cl.Logical().WriteWithContext(ctx, res.Spec.Path, params)
- }
+ result, err := g.fetchVaultSecret(ctx, res, cl)
if err != nil {
return nil, err
}
+
+ if result == nil && res.Spec.AllowEmptyResponse {
+ return nil, nil
+ }
+
if result == nil {
- return nil, fmt.Errorf(errGetSecret, fmt.Errorf("empty response from Vault"))
+ return nil, fmt.Errorf(errGetSecret, errors.New("empty response from Vault"))
}
data := make(map[string]any)
@@ -123,6 +114,32 @@ func (g *Generator) generate(ctx context.Context, c *provider.Provider, jsonSpec
return response, nil
}
+func (g *Generator) fetchVaultSecret(ctx context.Context, res *genv1alpha1.VaultDynamicSecret, cl util.Client) (*vault.Secret, error) {
+ var (
+ result *vault.Secret
+ err error
+ )
+
+ if res.Spec.Method == "" || res.Spec.Method == "GET" {
+ result, err = cl.Logical().ReadWithDataWithContext(ctx, res.Spec.Path, nil)
+ } else if res.Spec.Method == "LIST" {
+ result, err = cl.Logical().ListWithContext(ctx, res.Spec.Path)
+ } else if res.Spec.Method == "DELETE" {
+ result, err = cl.Logical().DeleteWithContext(ctx, res.Spec.Path)
+ } else {
+ params := make(map[string]any)
+ if res.Spec.Parameters != nil {
+ if err := json.Unmarshal(res.Spec.Parameters.Raw, ¶ms); err != nil {
+ return nil, err
+ }
+ }
+
+ result, err = cl.Logical().WriteWithContext(ctx, res.Spec.Path, params)
+ }
+
+ return result, err
+}
+
func parseSpec(data []byte) (*genv1alpha1.VaultDynamicSecret, error) {
var spec genv1alpha1.VaultDynamicSecret
err := yaml.Unmarshal(data, &spec)
diff --git a/pkg/generator/vault/vault_test.go b/pkg/generator/vault/vault_test.go
index a8693246f75..d033c3d2070 100644
--- a/pkg/generator/vault/vault_test.go
+++ b/pkg/generator/vault/vault_test.go
@@ -17,7 +17,6 @@ package vaultdynamic
import (
"context"
"errors"
- "fmt"
"testing"
"github.com/google/go-cmp/cmp"
@@ -91,7 +90,7 @@ spec:
kube: clientfake.NewClientBuilder().Build(),
},
want: want{
- err: fmt.Errorf("unable to setup Vault client: no role name was provided"),
+ err: errors.New("unable to setup Vault client: no role name was provided"),
},
},
"EmptyVaultResponse": {
@@ -124,7 +123,7 @@ spec:
}).Build(),
},
want: want{
- err: fmt.Errorf("unable to get dynamic secret: empty response from Vault"),
+ err: errors.New("unable to get dynamic secret: empty response from Vault"),
},
},
"EmptyVaultPOST": {
@@ -159,7 +158,81 @@ spec:
}).Build(),
},
want: want{
- err: fmt.Errorf("unable to get dynamic secret: empty response from Vault"),
+ err: errors.New("unable to get dynamic secret: empty response from Vault"),
+ },
+ },
+ "AllowEmptyVaultPOST": {
+ reason: "Allow empty response from Vault POST.",
+ args: args{
+ corev1: utilfake.NewCreateTokenMock().WithToken("ok"),
+ jsonSpec: &apiextensions.JSON{
+ Raw: []byte(`apiVersion: generators.external-secrets.io/v1alpha1
+kind: VaultDynamicSecret
+spec:
+ provider:
+ auth:
+ kubernetes:
+ role: test
+ serviceAccountRef:
+ name: "testing"
+ method: POST
+ parameters:
+ foo: "bar"
+ path: "github/token/example"
+ allowEmptyResponse: true`),
+ },
+ kube: clientfake.NewClientBuilder().WithObjects(&corev1.ServiceAccount{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "testing",
+ Namespace: "testing",
+ },
+ Secrets: []corev1.ObjectReference{
+ {
+ Name: "test",
+ },
+ },
+ }).Build(),
+ },
+ want: want{
+ err: nil,
+ val: nil,
+ },
+ },
+ "AllowEmptyVaultGET": {
+ reason: "Allow empty response from Vault GET.",
+ args: args{
+ corev1: utilfake.NewCreateTokenMock().WithToken("ok"),
+ jsonSpec: &apiextensions.JSON{
+ Raw: []byte(`apiVersion: generators.external-secrets.io/v1alpha1
+kind: VaultDynamicSecret
+spec:
+ provider:
+ auth:
+ kubernetes:
+ role: test
+ serviceAccountRef:
+ name: "testing"
+ method: GET
+ parameters:
+ foo: "bar"
+ path: "github/token/example"
+ allowEmptyResponse: true`),
+ },
+ kube: clientfake.NewClientBuilder().WithObjects(&corev1.ServiceAccount{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "testing",
+ Namespace: "testing",
+ },
+ Secrets: []corev1.ObjectReference{
+ {
+ Name: "test",
+ },
+ },
+ }).Build(),
+ },
+ want: want{
+ err: nil,
+ val: nil,
},
},
}
@@ -169,8 +242,10 @@ spec:
c := &provider.Provider{NewVaultClient: fake.ClientWithLoginMock}
gen := &Generator{}
val, err := gen.generate(context.Background(), c, tc.args.jsonSpec, tc.args.kube, tc.args.corev1, "testing")
- if diff := cmp.Diff(tc.want.err.Error(), err.Error()); diff != "" {
- t.Errorf("\n%s\nvault.GetSecret(...): -want error, +got error:\n%s", tc.reason, diff)
+ if err != nil || tc.want.err != nil {
+ if diff := cmp.Diff(tc.want.err.Error(), err.Error()); diff != "" {
+ t.Errorf("\n%s\nvault.GetSecret(...): -want error, +got error:\n%s", tc.reason, diff)
+ }
}
if diff := cmp.Diff(tc.want.val, val); diff != "" {
t.Errorf("\n%s\nvault.GetSecret(...): -want val, +got val:\n%s", tc.reason, diff)
diff --git a/pkg/generator/webhook/webhook.go b/pkg/generator/webhook/webhook.go
index 78fb1d7c360..7f2f5f62cbb 100644
--- a/pkg/generator/webhook/webhook.go
+++ b/pkg/generator/webhook/webhook.go
@@ -42,7 +42,7 @@ func (w *Webhook) Generate(ctx context.Context, jsonSpec *apiextensions.JSON, kc
w.wh.Namespace = ns
w.url = provider.URL
w.wh.Kube = kclient
- w.wh.HTTP, err = w.wh.GetHTTPClient(provider)
+ w.wh.HTTP, err = w.wh.GetHTTPClient(ctx, provider)
if err != nil {
return nil, fmt.Errorf("failed to prepare provider http client: %w", err)
}
diff --git a/pkg/provider/akeyless/akeyless.go b/pkg/provider/akeyless/akeyless.go
index f324b12ad8e..d230faaa011 100644
--- a/pkg/provider/akeyless/akeyless.go
+++ b/pkg/provider/akeyless/akeyless.go
@@ -15,6 +15,7 @@ limitations under the License.
package akeyless
import (
+ "bytes"
"context"
"crypto/tls"
"crypto/x509"
@@ -23,6 +24,7 @@ import (
"fmt"
"net/http"
"net/url"
+ "slices"
"strconv"
"strings"
"time"
@@ -37,15 +39,16 @@ import (
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
- esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
"github.com/external-secrets/external-secrets/pkg/find"
"github.com/external-secrets/external-secrets/pkg/utils"
- "github.com/external-secrets/external-secrets/pkg/utils/resolvers"
)
+type AkeylessCtx string
+
const (
- defaultAPIUrl = "https://api.akeyless.io"
- errNotImplemented = "not implemented"
+ defaultAPIUrl = "https://api.akeyless.io"
+ extSecretManagedTag = "k8s-external-secrets"
+ aKeylessToken AkeylessCtx = "AKEYLESS_TOKEN"
)
// https://github.com/external-secrets/external-secrets/issues/644
@@ -79,9 +82,13 @@ type Item struct {
}
type akeylessVaultInterface interface {
- GetSecretByType(ctx context.Context, secretName, token string, version int32) (string, error)
+ GetSecretByType(ctx context.Context, secretName string, version int32) (string, error)
TokenFromSecretRef(ctx context.Context) (string, error)
- ListSecrets(ctx context.Context, path, tag, token string) ([]string, error)
+ ListSecrets(ctx context.Context, path, tag string) ([]string, error)
+ DescribeItem(ctx context.Context, itemName string) (*akeyless.Item, error)
+ CreateSecret(ctx context.Context, remoteKey, data string) error
+ UpdateSecret(ctx context.Context, remoteKey, data string) error
+ DeleteSecret(ctx context.Context, remoteKey string) error
}
func init() {
@@ -121,11 +128,11 @@ func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnin
if akeylessGWApiURL != nil && *akeylessGWApiURL != "" {
url, err := url.Parse(*akeylessGWApiURL)
if err != nil {
- return nil, fmt.Errorf(errInvalidAkeylessURL)
+ return nil, errors.New(errInvalidAkeylessURL)
}
if url.Host == "" {
- return nil, fmt.Errorf(errInvalidAkeylessURL)
+ return nil, errors.New(errInvalidAkeylessURL)
}
}
if akeylessSpec.Auth.KubernetesAuth != nil {
@@ -142,11 +149,11 @@ func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnin
}
if akeylessSpec.Auth.KubernetesAuth.AccessID == "" {
- return nil, fmt.Errorf("missing kubernetes auth-method access-id")
+ return nil, errors.New("missing kubernetes auth-method access-id")
}
if akeylessSpec.Auth.KubernetesAuth.K8sConfName == "" {
- return nil, fmt.Errorf("missing kubernetes config name")
+ return nil, errors.New("missing kubernetes config name")
}
return nil, nil
}
@@ -158,11 +165,11 @@ func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnin
}
if accessID.Name == "" {
- return nil, fmt.Errorf(errInvalidAkeylessAccessIDName)
+ return nil, errors.New(errInvalidAkeylessAccessIDName)
}
if accessID.Key == "" {
- return nil, fmt.Errorf(errInvalidAkeylessAccessIDKey)
+ return nil, errors.New(errInvalidAkeylessAccessIDKey)
}
accessType := akeylessSpec.Auth.SecretRef.AccessType
@@ -180,7 +187,7 @@ func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnin
return nil, nil
}
-func newClient(_ context.Context, store esv1beta1.GenericStore, kube client.Client, corev1 typedcorev1.CoreV1Interface, namespace string) (esv1beta1.SecretsClient, error) {
+func newClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, corev1 typedcorev1.CoreV1Interface, namespace string) (esv1beta1.SecretsClient, error) {
akl := &akeylessBase{
kube: kube,
store: store,
@@ -199,10 +206,10 @@ func newClient(_ context.Context, store esv1beta1.GenericStore, kube client.Clie
}
if spec.Auth == nil {
- return nil, fmt.Errorf("missing Auth in store config")
+ return nil, errors.New("missing Auth in store config")
}
- client, err := akl.getAkeylessHTTPClient(spec)
+ client, err := akl.getAkeylessHTTPClient(ctx, spec)
if err != nil {
return nil, err
}
@@ -221,6 +228,17 @@ func newClient(_ context.Context, store esv1beta1.GenericStore, kube client.Clie
return &Akeyless{Client: akl, url: akeylessGwAPIURL}, nil
}
+func (a *Akeyless) contextWithToken(ctx context.Context) (context.Context, error) {
+ if v := ctx.Value(aKeylessToken); v != nil {
+ return ctx, nil
+ }
+ token, err := a.Client.TokenFromSecretRef(ctx)
+ if err != nil {
+ return nil, err
+ }
+ return context.WithValue(ctx, aKeylessToken, token), nil
+}
+
func (a *Akeyless) Close(_ context.Context) error {
return nil
}
@@ -236,26 +254,13 @@ func (a *Akeyless) Validate() (esv1beta1.ValidationResult, error) {
return esv1beta1.ValidationResultReady, nil
}
-func (a *Akeyless) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
- return fmt.Errorf(errNotImplemented)
-}
-
-func (a *Akeyless) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
- return fmt.Errorf(errNotImplemented)
-}
-
-func (a *Akeyless) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf(errNotImplemented)
-}
-
// Implements store.Client.GetSecret Interface.
// Retrieves a secret with the secret name defined in ref.Name.
func (a *Akeyless) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
if utils.IsNil(a.Client) {
- return nil, fmt.Errorf(errUninitalizedAkeylessProvider)
+ return nil, errors.New(errUninitalizedAkeylessProvider)
}
-
- token, err := a.Client.TokenFromSecretRef(ctx)
+ ctx, err := a.contextWithToken(ctx)
if err != nil {
return nil, err
}
@@ -266,7 +271,7 @@ func (a *Akeyless) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDa
version = int32(i)
}
}
- value, err := a.Client.GetSecretByType(ctx, ref.Key, token, version)
+ value, err := a.Client.GetSecretByType(ctx, ref.Key, version)
if err != nil {
return nil, err
}
@@ -293,11 +298,15 @@ func (a *Akeyless) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDa
return []byte(val.String()), nil
}
-// Implements store.Client.GetAllSecrets Interface.
-// Retrieves a all secrets with defined in ref.Name or tags.
+// GetAllSecrets Implements store.Client.GetAllSecrets Interface.
+// Retrieves all secrets with defined in ref.Name or tags.
func (a *Akeyless) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
if utils.IsNil(a.Client) {
- return nil, fmt.Errorf(errUninitalizedAkeylessProvider)
+ return nil, errors.New(errUninitalizedAkeylessProvider)
+ }
+ ctx, err := a.contextWithToken(ctx)
+ if err != nil {
+ return nil, err
}
searchPath := ""
@@ -310,45 +319,34 @@ func (a *Akeyless) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecr
searchPath += "/"
}
}
- token, err := a.Client.TokenFromSecretRef(ctx)
- if err != nil {
- return nil, err
+ if ref.Name != nil {
+ return a.findSecretsFromName(ctx, searchPath, *ref.Name)
+ }
+ if len(ref.Tags) > 0 {
+ return a.getSecrets(ctx, searchPath, ref.Tags)
}
- if ref.Name != nil {
- potentialSecrets, err := a.Client.ListSecrets(ctx, searchPath, "", token)
+ return nil, errors.New("unexpected find operator")
+}
+
+func (a *Akeyless) getSecrets(ctx context.Context, searchPath string, tags map[string]string) (map[string][]byte, error) {
+ var potentialSecretsName []string
+ for _, v := range tags {
+ potentialSecrets, err := a.Client.ListSecrets(ctx, searchPath, v)
if err != nil {
return nil, err
}
- if len(potentialSecrets) == 0 {
- return nil, nil
+ if len(potentialSecrets) > 0 {
+ potentialSecretsName = append(potentialSecretsName, potentialSecrets...)
}
- return a.findSecretsFromName(ctx, potentialSecrets, *ref.Name, token)
}
- if len(ref.Tags) > 0 {
- var potentialSecretsName []string
- for _, v := range ref.Tags {
- potentialSecrets, err := a.Client.ListSecrets(ctx, searchPath, v, token)
- if err != nil {
- return nil, err
- }
- if len(potentialSecrets) > 0 {
- potentialSecretsName = append(potentialSecretsName, potentialSecrets...)
- }
- }
- if len(potentialSecretsName) == 0 {
- return nil, nil
- }
- return a.getSecrets(ctx, potentialSecretsName, token)
+ if len(potentialSecretsName) == 0 {
+ return nil, nil
}
- return nil, errors.New("unexpected find operator")
-}
-
-func (a *Akeyless) getSecrets(ctx context.Context, candidates []string, token string) (map[string][]byte, error) {
secrets := make(map[string][]byte)
- for _, name := range candidates {
- secretValue, err := a.Client.GetSecretByType(ctx, name, token, 0)
+ for _, name := range potentialSecretsName {
+ secretValue, err := a.Client.GetSecretByType(ctx, name, 0)
if err != nil {
return nil, err
}
@@ -359,16 +357,24 @@ func (a *Akeyless) getSecrets(ctx context.Context, candidates []string, token st
return secrets, nil
}
-func (a *Akeyless) findSecretsFromName(ctx context.Context, candidates []string, ref esv1beta1.FindName, token string) (map[string][]byte, error) {
+func (a *Akeyless) findSecretsFromName(ctx context.Context, searchPath string, ref esv1beta1.FindName) (map[string][]byte, error) {
+ potentialSecrets, err := a.Client.ListSecrets(ctx, searchPath, "")
+ if err != nil {
+ return nil, err
+ }
+ if len(potentialSecrets) == 0 {
+ return nil, nil
+ }
+
secrets := make(map[string][]byte)
matcher, err := find.New(ref)
if err != nil {
return nil, err
}
- for _, name := range candidates {
+ for _, name := range potentialSecrets {
ok := matcher.MatchName(name)
if ok {
- secretValue, err := a.Client.GetSecretByType(ctx, name, token, 0)
+ secretValue, err := a.Client.GetSecretByType(ctx, name, 0)
if err != nil {
return nil, err
}
@@ -380,13 +386,12 @@ func (a *Akeyless) findSecretsFromName(ctx context.Context, candidates []string,
return secrets, nil
}
-// Implements store.Client.GetSecretMap Interface.
+// GetSecretMap implements store.Client.GetSecretMap Interface.
// New version of GetSecretMap.
func (a *Akeyless) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
if utils.IsNil(a.Client) {
- return nil, fmt.Errorf(errUninitalizedAkeylessProvider)
+ return nil, errors.New(errUninitalizedAkeylessProvider)
}
-
val, err := a.GetSecret(ctx, ref)
if err != nil {
return nil, err
@@ -406,110 +411,148 @@ func (a *Akeyless) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecre
return secretData, nil
}
-func (a *akeylessBase) getAkeylessHTTPClient(provider *esv1beta1.AkeylessProvider) (*http.Client, error) {
- client := &http.Client{Timeout: 30 * time.Second}
- if len(provider.CABundle) == 0 && provider.CAProvider == nil {
- return client, nil
+func (a *Akeyless) SecretExists(ctx context.Context, ref esv1beta1.PushSecretRemoteRef) (bool, error) {
+ if utils.IsNil(a.Client) {
+ return false, errors.New(errUninitalizedAkeylessProvider)
+ }
+ secret, err := a.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: ref.GetRemoteKey()})
+ if errors.Is(err, ErrItemNotExists) {
+ return false, nil
}
- caCertPool, err := a.getCACertPool(provider)
if err != nil {
- return nil, err
+ return false, err
}
-
- tlsConf := &tls.Config{
- RootCAs: caCertPool,
- MinVersion: tls.VersionTLS12,
+ if ref.GetProperty() == "" {
+ return true, nil
}
- client.Transport = &http.Transport{TLSClientConfig: tlsConf}
- return client, nil
+ var secretMap map[string]any
+ err = json.Unmarshal(secret, &secretMap)
+ if err != nil {
+ return false, err
+ }
+ _, ok := secretMap[ref.GetProperty()]
+ return ok, nil
}
-func (a *akeylessBase) getCACertPool(provider *esv1beta1.AkeylessProvider) (*x509.CertPool, error) {
- caCertPool := x509.NewCertPool()
- if len(provider.CABundle) > 0 {
- pem, err := base64decode(provider.CABundle)
- if err != nil {
- pem = provider.CABundle
- }
- ok := caCertPool.AppendCertsFromPEM(pem)
- if !ok {
- return nil, fmt.Errorf("failed to append caBundle")
- }
+func initMapIfNotExist(psd esv1beta1.PushSecretData, secretMapSize int) map[string]any {
+ mapSize := 1
+ if psd.GetProperty() == "" {
+ mapSize = secretMapSize
}
+ return make(map[string]any, mapSize)
+}
- if provider.CAProvider != nil &&
- a.storeKind == esv1beta1.ClusterSecretStoreKind &&
- provider.CAProvider.Namespace == nil {
- return nil, fmt.Errorf("missing namespace on caProvider secret")
+func (a *Akeyless) PushSecret(ctx context.Context, secret *corev1.Secret, psd esv1beta1.PushSecretData) error {
+ if utils.IsNil(a.Client) {
+ return errors.New(errUninitalizedAkeylessProvider)
}
-
- if provider.CAProvider != nil {
- var cert []byte
- var err error
-
- switch provider.CAProvider.Type {
- case esv1beta1.CAProviderTypeSecret:
- cert, err = a.getCertFromSecret(provider)
- case esv1beta1.CAProviderTypeConfigMap:
- cert, err = a.getCertFromConfigMap(provider)
- default:
- err = fmt.Errorf("unknown CAProvider type: %s", provider.CAProvider.Type)
- }
-
- if err != nil {
- return nil, err
- }
- pem, err := base64decode(cert)
- if err != nil {
- pem = cert
- }
- ok := caCertPool.AppendCertsFromPEM(pem)
- if !ok {
- return nil, fmt.Errorf("failed to append caBundle")
+ ctx, err := a.contextWithToken(ctx)
+ if err != nil {
+ return err
+ }
+ secretRemote, err := a.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: psd.GetRemoteKey()})
+ isNotExists := errors.Is(err, ErrItemNotExists)
+ if err != nil && !isNotExists {
+ return err
+ }
+ var data map[string]any
+ if isNotExists {
+ data = initMapIfNotExist(psd, len(secret.Data))
+ err = nil
+ } else {
+ err = json.Unmarshal(secretRemote, &data)
+ }
+ if err != nil {
+ return err
+ }
+ if psd.GetProperty() == "" {
+ for k, v := range secret.Data {
+ data[k] = string(v)
}
+ } else if v, ok := secret.Data[psd.GetSecretKey()]; ok {
+ data[psd.GetProperty()] = string(v)
+ }
+ dataByte, err := json.Marshal(data)
+ if err != nil {
+ return err
}
- return caCertPool, nil
+ if bytes.Equal(dataByte, secretRemote) {
+ return nil
+ }
+ if isNotExists {
+ return a.Client.CreateSecret(ctx, psd.GetRemoteKey(), string(dataByte))
+ }
+ return a.Client.UpdateSecret(ctx, psd.GetRemoteKey(), string(dataByte))
}
-func (a *akeylessBase) getCertFromSecret(provider *esv1beta1.AkeylessProvider) ([]byte, error) {
- secretRef := esmeta.SecretKeySelector{
- Name: provider.CAProvider.Name,
- Key: provider.CAProvider.Key,
+func (a *Akeyless) DeleteSecret(ctx context.Context, psr esv1beta1.PushSecretRemoteRef) error {
+ if utils.IsNil(a.Client) {
+ return errors.New(errUninitalizedAkeylessProvider)
}
-
- if provider.CAProvider.Namespace != nil {
- secretRef.Namespace = provider.CAProvider.Namespace
+ ctx, err := a.contextWithToken(ctx)
+ if err != nil {
+ return err
}
-
- ctx := context.Background()
- cert, err := resolvers.SecretKeyRef(ctx, a.kube, a.storeKind, a.namespace, &secretRef)
+ item, err := a.Client.DescribeItem(ctx, psr.GetRemoteKey())
if err != nil {
- return nil, err
+ return err
}
-
- return []byte(cert), nil
-}
-
-func (a *akeylessBase) getCertFromConfigMap(provider *esv1beta1.AkeylessProvider) ([]byte, error) {
- objKey := client.ObjectKey{
- Name: provider.CAProvider.Name,
+ if item == nil || item.ItemTags == nil || !slices.Contains(*item.ItemTags, extSecretManagedTag) {
+ return nil
+ }
+ if psr.GetProperty() == "" {
+ err = a.Client.DeleteSecret(ctx, psr.GetRemoteKey())
+ return err
+ }
+ secret, err := a.GetSecret(ctx, esv1beta1.ExternalSecretDataRemoteRef{Key: psr.GetRemoteKey()})
+ if err != nil {
+ return err
+ }
+ var secretMap map[string]any
+ err = json.Unmarshal(secret, &secretMap)
+ if err != nil {
+ return err
+ }
+ delete(secretMap, psr.GetProperty())
+ if len(secretMap) == 0 {
+ err = a.Client.DeleteSecret(ctx, psr.GetRemoteKey())
+ return err
+ }
+ byteSecretMap, err := json.Marshal(secretMap)
+ if err != nil {
+ return err
}
+ err = a.Client.UpdateSecret(ctx, psr.GetRemoteKey(), string(byteSecretMap))
+ return err
+}
- if provider.CAProvider.Namespace != nil {
- objKey.Namespace = *provider.CAProvider.Namespace
+func (a *akeylessBase) getAkeylessHTTPClient(ctx context.Context, provider *esv1beta1.AkeylessProvider) (*http.Client, error) {
+ client := &http.Client{Timeout: 30 * time.Second}
+ if len(provider.CABundle) == 0 && provider.CAProvider == nil {
+ return client, nil
}
- configMapRef := &corev1.ConfigMap{}
- ctx := context.Background()
- err := a.kube.Get(ctx, objKey, configMapRef)
+ cert, err := utils.FetchCACertFromSource(ctx, utils.CreateCertOpts{
+ StoreKind: a.storeKind,
+ Client: a.kube,
+ Namespace: a.namespace,
+ CABundle: provider.CABundle,
+ CAProvider: provider.CAProvider,
+ })
if err != nil {
- return nil, fmt.Errorf("failed to get caProvider secret %s: %w", objKey.Name, err)
+ return nil, err
}
- val, ok := configMapRef.Data[provider.CAProvider.Key]
+ caCertPool := x509.NewCertPool()
+ ok := caCertPool.AppendCertsFromPEM(cert)
if !ok {
- return nil, fmt.Errorf("failed to get caProvider configMap %s -> %s", objKey.Name, provider.CAProvider.Key)
+ return nil, errors.New("failed to append caBundle")
}
- return []byte(val), nil
+ tlsConf := &tls.Config{
+ RootCAs: caCertPool,
+ MinVersion: tls.VersionTLS12,
+ }
+ client.Transport = &http.Transport{TLSClientConfig: tlsConf}
+ return client, nil
}
diff --git a/pkg/provider/akeyless/akeyless_api.go b/pkg/provider/akeyless/akeyless_api.go
index 7cb1445662a..9af7c2a4d59 100644
--- a/pkg/provider/akeyless/akeyless_api.go
+++ b/pkg/provider/akeyless/akeyless_api.go
@@ -40,12 +40,20 @@ import (
"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
)
-var apiErr akeyless.GenericOpenAPIError
+var (
+ apiErr akeyless.GenericOpenAPIError
+ ErrItemNotExists = errors.New("item does not exist")
+ ErrTokenNotExists = errors.New("token does not exist")
+)
const DefServiceAccountFile = "/var/run/secrets/kubernetes.io/serviceaccount/token"
-func (a *akeylessBase) GetToken(accessID, accType, accTypeParam string, k8sAuth *esv1beta1.AkeylessKubernetesAuth) (string, error) {
- ctx := context.Background()
+type Tokener interface {
+ SetToken(v string)
+ SetUidToken(v string)
+}
+
+func (a *akeylessBase) GetToken(ctx context.Context, accessID, accType, accTypeParam string, k8sAuth *esv1beta1.AkeylessKubernetesAuth) (string, error) {
authBody := akeyless.NewAuthWithDefaults()
authBody.AccessId = akeyless.PtrString(accessID)
if accType == "api_key" || accType == "access_key" {
@@ -71,83 +79,91 @@ func (a *akeylessBase) GetToken(accessID, accType, accTypeParam string, k8sAuth
authOut, res, err := a.RestAPI.Auth(ctx).Body(*authBody).Execute()
metrics.ObserveAPICall(constants.ProviderAKEYLESSSM, constants.CallAKEYLESSSMAuth, err)
+ if errors.As(err, &apiErr) {
+ return "", fmt.Errorf("authentication failed: %v", string(apiErr.Body()))
+ }
if err != nil {
- if errors.As(err, &apiErr) {
- return "", fmt.Errorf("authentication failed: %v", string(apiErr.Body()))
- }
return "", fmt.Errorf("authentication failed: %w", err)
}
defer res.Body.Close()
-
token := authOut.GetToken()
return token, nil
}
-func (a *akeylessBase) GetSecretByType(ctx context.Context, secretName, token string, version int32) (string, error) {
- item, err := a.DescribeItem(ctx, secretName, token)
+func (a *akeylessBase) GetSecretByType(ctx context.Context, secretName string, version int32) (string, error) {
+ item, err := a.DescribeItem(ctx, secretName)
if err != nil {
return "", err
}
+ if _, ok := item.GetItemNameOk(); !ok {
+ return "", ErrItemNotExists
+ }
secretType := item.GetItemType()
-
switch secretType {
case "STATIC_SECRET":
- return a.GetStaticSecret(ctx, secretName, token, version)
+ return a.GetStaticSecret(ctx, secretName, version)
case "DYNAMIC_SECRET":
- return a.GetDynamicSecrets(ctx, secretName, token)
+ return a.GetDynamicSecrets(ctx, secretName)
case "ROTATED_SECRET":
- return a.GetRotatedSecrets(ctx, secretName, token, version)
+ return a.GetRotatedSecrets(ctx, secretName, version)
case "CERTIFICATE":
- return a.GetCertificate(ctx, secretName, token, version)
+ return a.GetCertificate(ctx, secretName, version)
default:
return "", fmt.Errorf("invalid item type: %v", secretType)
}
}
-func (a *akeylessBase) DescribeItem(ctx context.Context, itemName, token string) (*akeyless.Item, error) {
- body := akeyless.DescribeItem{
- Name: itemName,
+func SetBodyToken(t Tokener, ctx context.Context) error {
+ token, ok := ctx.Value(aKeylessToken).(string)
+ if !ok {
+ return ErrTokenNotExists
}
if strings.HasPrefix(token, "u-") {
- body.UidToken = &token
+ t.SetUidToken(token)
} else {
- body.Token = &token
+ t.SetToken(token)
+ }
+ return nil
+}
+
+func (a *akeylessBase) DescribeItem(ctx context.Context, itemName string) (*akeyless.Item, error) {
+ body := akeyless.DescribeItem{
+ Name: itemName,
+ }
+ if err := SetBodyToken(&body, ctx); err != nil {
+ return nil, err
}
gsvOut, res, err := a.RestAPI.DescribeItem(ctx).Body(body).Execute()
metrics.ObserveAPICall(constants.ProviderAKEYLESSSM, constants.CallAKEYLESSSMDescribeItem, err)
- if err != nil {
- if errors.As(err, &apiErr) {
- var item *Item
- err = json.Unmarshal(apiErr.Body(), &item)
- if err != nil {
- return nil, fmt.Errorf("can't describe item: %v, error: %v", itemName, string(apiErr.Body()))
- }
- } else {
- return nil, fmt.Errorf("can't describe item: %w", err)
+ if errors.As(err, &apiErr) {
+ var item *Item
+ err = json.Unmarshal(apiErr.Body(), &item)
+ if err != nil {
+ return nil, fmt.Errorf("can't describe item: %v, error: %v", itemName, string(apiErr.Body()))
}
}
+ if err != nil {
+ return nil, fmt.Errorf("can't describe item: %w", err)
+ }
defer res.Body.Close()
return &gsvOut, nil
}
-func (a *akeylessBase) GetCertificate(ctx context.Context, certificateName, token string, version int32) (string, error) {
+func (a *akeylessBase) GetCertificate(ctx context.Context, certificateName string, version int32) (string, error) {
body := akeyless.GetCertificateValue{
Name: certificateName,
Version: &version,
}
- if strings.HasPrefix(token, "u-") {
- body.UidToken = &token
- } else {
- body.Token = &token
+ if err := SetBodyToken(&body, ctx); err != nil {
+ return "", err
}
-
gcvOut, res, err := a.RestAPI.GetCertificateValue(ctx).Body(body).Execute()
metrics.ObserveAPICall(constants.ProviderAKEYLESSSM, constants.CallAKEYLESSSMGetCertificateValue, err)
+ if errors.As(err, &apiErr) {
+ return "", fmt.Errorf("can't get certificate value: %v", string(apiErr.Body()))
+ }
if err != nil {
- if errors.As(err, &apiErr) {
- return "", fmt.Errorf("can't get certificate value: %v", string(apiErr.Body()))
- }
return "", fmt.Errorf("can't get certificate value: %w", err)
}
defer res.Body.Close()
@@ -160,102 +176,83 @@ func (a *akeylessBase) GetCertificate(ctx context.Context, certificateName, toke
return string(out), nil
}
-func (a *akeylessBase) GetRotatedSecrets(ctx context.Context, secretName, token string, version int32) (string, error) {
+func (a *akeylessBase) GetRotatedSecrets(ctx context.Context, secretName string, version int32) (string, error) {
body := akeyless.GetRotatedSecretValue{
Names: secretName,
Version: &version,
}
- if strings.HasPrefix(token, "u-") {
- body.UidToken = &token
- } else {
- body.Token = &token
+ if err := SetBodyToken(&body, ctx); err != nil {
+ return "", err
}
-
gsvOut, res, err := a.RestAPI.GetRotatedSecretValue(ctx).Body(body).Execute()
metrics.ObserveAPICall(constants.ProviderAKEYLESSSM, constants.CallAKEYLESSSMGetRotatedSecretValue, err)
+ if errors.As(err, &apiErr) {
+ return "", fmt.Errorf("can't get rotated secret value: %v", string(apiErr.Body()))
+ }
if err != nil {
- if errors.As(err, &apiErr) {
- return "", fmt.Errorf("can't get rotated secret value: %v", string(apiErr.Body()))
- }
return "", fmt.Errorf("can't get rotated secret value: %w", err)
}
defer res.Body.Close()
-
valI, ok := gsvOut["value"]
+ var out []byte
if ok {
val, convert := valI.(map[string]any)
if !convert {
- return "", fmt.Errorf("failure converting key from gsvOut")
+ return "", errors.New("failure converting key from gsvOut")
}
if _, ok := val["payload"]; ok {
return fmt.Sprintf("%v", val["payload"]), nil
} else if _, ok := val["target_value"]; ok {
- out, err := json.Marshal(val["target_value"])
- if err != nil {
- return "", fmt.Errorf("can't marshal rotated secret value: %w", err)
- }
- return string(out), nil
+ out, err = json.Marshal(val["target_value"])
} else {
- out, err := json.Marshal(val)
- if err != nil {
- return "", fmt.Errorf("can't marshal rotated secret value: %w", err)
- }
- return string(out), nil
+ out, err = json.Marshal(val)
}
+ } else {
+ out, err = json.Marshal(gsvOut)
}
- out, err := json.Marshal(gsvOut)
if err != nil {
return "", fmt.Errorf("can't marshal rotated secret value: %w", err)
}
return string(out), nil
}
-func (a *akeylessBase) GetDynamicSecrets(ctx context.Context, secretName, token string) (string, error) {
+func (a *akeylessBase) GetDynamicSecrets(ctx context.Context, secretName string) (string, error) {
body := akeyless.GetDynamicSecretValue{
Name: secretName,
}
- if strings.HasPrefix(token, "u-") {
- body.UidToken = &token
- } else {
- body.Token = &token
+ if err := SetBodyToken(&body, ctx); err != nil {
+ return "", err
}
-
gsvOut, res, err := a.RestAPI.GetDynamicSecretValue(ctx).Body(body).Execute()
metrics.ObserveAPICall(constants.ProviderAKEYLESSSM, constants.CallAKEYLESSSMGetDynamicSecretValue, err)
+ if errors.As(err, &apiErr) {
+ return "", fmt.Errorf("can't get dynamic secret value: %v", string(apiErr.Body()))
+ }
if err != nil {
- if errors.As(err, &apiErr) {
- return "", fmt.Errorf("can't get dynamic secret value: %v", string(apiErr.Body()))
- }
return "", fmt.Errorf("can't get dynamic secret value: %w", err)
}
defer res.Body.Close()
-
out, err := json.Marshal(gsvOut)
if err != nil {
return "", fmt.Errorf("can't marshal dynamic secret value: %w", err)
}
-
return string(out), nil
}
-func (a *akeylessBase) GetStaticSecret(ctx context.Context, secretName, token string, version int32) (string, error) {
- gsvBody := akeyless.GetSecretValue{
+func (a *akeylessBase) GetStaticSecret(ctx context.Context, secretName string, version int32) (string, error) {
+ body := akeyless.GetSecretValue{
Names: []string{secretName},
Version: &version,
}
-
- if strings.HasPrefix(token, "u-") {
- gsvBody.UidToken = &token
- } else {
- gsvBody.Token = &token
+ if err := SetBodyToken(&body, ctx); err != nil {
+ return "", err
}
-
- gsvOut, res, err := a.RestAPI.GetSecretValue(ctx).Body(gsvBody).Execute()
+ gsvOut, res, err := a.RestAPI.GetSecretValue(ctx).Body(body).Execute()
metrics.ObserveAPICall(constants.ProviderAKEYLESSSM, constants.CallAKEYLESSSMGetSecretValue, err)
+ if errors.As(err, &apiErr) {
+ return "", fmt.Errorf("can't get secret value: %v", string(apiErr.Body()))
+ }
if err != nil {
- if errors.As(err, &apiErr) {
- return "", fmt.Errorf("can't get secret value: %v", string(apiErr.Body()))
- }
return "", fmt.Errorf("can't get secret value: %w", err)
}
defer res.Body.Close()
@@ -263,7 +260,6 @@ func (a *akeylessBase) GetStaticSecret(ctx context.Context, secretName, token st
if !ok {
return "", fmt.Errorf("can't get secret: %v", secretName)
}
-
return val, nil
}
@@ -284,31 +280,27 @@ func (a *akeylessBase) getCloudID(provider, accTypeParam string) (string, error)
return cloudID, err
}
-func (a *akeylessBase) ListSecrets(ctx context.Context, path, tag, token string) ([]string, error) {
+func (a *akeylessBase) ListSecrets(ctx context.Context, path, tag string) ([]string, error) {
secretTypes := &[]string{"static-secret", "dynamic-secret", "rotated-secret"}
MinimalView := true
if tag != "" {
MinimalView = false
}
- gsvBody := akeyless.ListItems{
+ body := akeyless.ListItems{
Filter: &path,
Type: secretTypes,
MinimalView: &MinimalView,
Tag: &tag,
}
-
- if strings.HasPrefix(token, "u-") {
- gsvBody.UidToken = &token
- } else {
- gsvBody.Token = &token
+ if err := SetBodyToken(&body, ctx); err != nil {
+ return nil, err
}
-
- lipOut, res, err := a.RestAPI.ListItems(ctx).Body(gsvBody).Execute()
+ lipOut, res, err := a.RestAPI.ListItems(ctx).Body(body).Execute()
metrics.ObserveAPICall(constants.ProviderAKEYLESSSM, constants.CallAKEYLESSSMListItems, err)
+ if errors.As(err, &apiErr) {
+ return nil, fmt.Errorf("can't get secrets list: %v", string(apiErr.Body()))
+ }
if err != nil {
- if errors.As(err, &apiErr) {
- return nil, fmt.Errorf("can't get secrets list: %v", string(apiErr.Body()))
- }
return nil, fmt.Errorf("error on get secrets list: %w", err)
}
defer res.Body.Close()
@@ -325,34 +317,79 @@ func (a *akeylessBase) ListSecrets(ctx context.Context, path, tag, token string)
return listNames, nil
}
+func (a *akeylessBase) CreateSecret(ctx context.Context, remoteKey, data string) error {
+ body := akeyless.CreateSecret{
+ Name: remoteKey,
+ Value: data,
+ Tags: &[]string{extSecretManagedTag},
+ }
+ if err := SetBodyToken(&body, ctx); err != nil {
+ return err
+ }
+ _, res, err := a.RestAPI.CreateSecret(ctx).Body(body).Execute()
+ defer res.Body.Close()
+ metrics.ObserveAPICall(constants.ProviderAKEYLESSSM, constants.CallAKEYLESSSMCreateSecret, err)
+ return err
+}
+
+func (a *akeylessBase) UpdateSecret(ctx context.Context, remoteKey, data string) error {
+ body := akeyless.UpdateSecretVal{
+ Name: remoteKey,
+ Value: data,
+ }
+ if err := SetBodyToken(&body, ctx); err != nil {
+ return err
+ }
+ _, res, err := a.RestAPI.UpdateSecretVal(ctx).Body(body).Execute()
+ defer res.Body.Close()
+ metrics.ObserveAPICall(constants.ProviderAKEYLESSSM, constants.CallAKEYLESSSMUpdateSecretVal, err)
+ return err
+}
+
+func (a *akeylessBase) DeleteSecret(ctx context.Context, remoteKey string) error {
+ body := akeyless.DeleteItem{
+ Name: remoteKey,
+ }
+ if err := SetBodyToken(&body, ctx); err != nil {
+ return err
+ }
+ _, res, err := a.RestAPI.DeleteItem(ctx).Body(body).Execute()
+ defer res.Body.Close()
+ metrics.ObserveAPICall(constants.ProviderAKEYLESSSM, constants.CallAKEYLESSSMDeleteItem, err)
+ return err
+}
+
func (a *akeylessBase) getK8SServiceAccountJWT(ctx context.Context, kubernetesAuth *esv1beta1.AkeylessKubernetesAuth) (string, error) {
- if kubernetesAuth != nil {
- if kubernetesAuth.ServiceAccountRef != nil {
- // Kubernetes =v1.24: fetch token via TokenRequest API
- jwt, err = a.getJWTfromServiceAccountToken(ctx, *kubernetesAuth.ServiceAccountRef, nil, 600)
- if err != nil {
- return "", err
- }
- return jwt, nil
- } else if kubernetesAuth.SecretRef != nil {
- tokenRef := kubernetesAuth.SecretRef
- if tokenRef.Key == "" {
- tokenRef = kubernetesAuth.SecretRef.DeepCopy()
- tokenRef.Key = "token"
- }
- jwt, err := resolvers.SecretKeyRef(ctx, a.kube, a.storeKind, a.namespace, tokenRef)
- if err != nil {
- return "", err
- }
- return jwt, nil
+ if kubernetesAuth == nil {
+ return readK8SServiceAccountJWT()
+ }
+
+ switch {
+ case kubernetesAuth.ServiceAccountRef != nil:
+ jwt, err := a.getJWTFromServiceAccount(ctx, kubernetesAuth.ServiceAccountRef)
+ if jwt != "" {
+ return jwt, err
+ }
+ // Kubernetes >=v1.24: fetch token via TokenRequest API
+ jwt, err = a.getJWTfromServiceAccountToken(ctx, *kubernetesAuth.ServiceAccountRef, nil, 600)
+ if err != nil {
+ return "", err
+ }
+ return jwt, nil
+ case kubernetesAuth.SecretRef != nil:
+ tokenRef := kubernetesAuth.SecretRef
+ if tokenRef.Key == "" {
+ tokenRef = kubernetesAuth.SecretRef.DeepCopy()
+ tokenRef.Key = "token"
}
+ jwt, err := resolvers.SecretKeyRef(ctx, a.kube, a.storeKind, a.namespace, tokenRef)
+ if err != nil {
+ return "", err
+ }
+ return jwt, nil
}
- return readK8SServiceAccountJWT()
+
+ return "", fmt.Errorf("can't determine k8s service account jwt")
}
func (a *akeylessBase) getJWTFromServiceAccount(ctx context.Context, serviceAccountRef *esmeta.ServiceAccountSelector) (string, error) {
diff --git a/pkg/provider/akeyless/akeyless_test.go b/pkg/provider/akeyless/akeyless_test.go
index 8ad4c3f0c86..295471a9c9e 100644
--- a/pkg/provider/akeyless/akeyless_test.go
+++ b/pkg/provider/akeyless/akeyless_test.go
@@ -16,41 +16,83 @@ package akeyless
import (
"context"
+ "errors"
"fmt"
- "reflect"
"strings"
"testing"
+ "github.com/akeylesslabs/akeyless-go/v3"
+ "github.com/stretchr/testify/require"
+ corev1 "k8s.io/api/core/v1"
+
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
fakeakeyless "github.com/external-secrets/external-secrets/pkg/provider/akeyless/fake"
+ testingfake "github.com/external-secrets/external-secrets/pkg/provider/testing/fake"
)
type akeylessTestCase struct {
+ testName string
mockClient *fakeakeyless.AkeylessMockClient
apiInput *fakeakeyless.Input
apiOutput *fakeakeyless.Output
ref *esv1beta1.ExternalSecretDataRemoteRef
+ input any
+ input2 any
expectError string
+ expectedVal any
expectedSecret string
- // for testing secretmap
- expectedData map[string][]byte
}
-func makeValidAkeylessTestCase() *akeylessTestCase {
+const fmtExpectedError = "unexpected error: %s, expected: '%s'"
+
+func (a *akeylessTestCase) SetMockClient(c *fakeakeyless.AkeylessMockClient) *akeylessTestCase {
+ a.mockClient = c
+ return a
+}
+
+func (a *akeylessTestCase) SetExpectErr(err string) *akeylessTestCase {
+ a.expectError = err
+ return a
+}
+
+func (a *akeylessTestCase) SetExpectVal(val any) *akeylessTestCase {
+ a.expectedVal = val
+ return a
+}
+
+func (a *akeylessTestCase) SetExpectInput(input any) *akeylessTestCase {
+ a.input = input
+ return a
+}
+
+func (a *akeylessTestCase) SetExpectInput2(input any) *akeylessTestCase {
+ a.input2 = input
+ return a
+}
+
+func makeValidAkeylessTestCase(testName string) *akeylessTestCase {
smtc := akeylessTestCase{
+ testName: testName,
mockClient: &fakeakeyless.AkeylessMockClient{},
apiInput: makeValidInput(),
ref: makeValidRef(),
apiOutput: makeValidOutput(),
expectError: "",
expectedSecret: "",
- expectedData: map[string][]byte{},
}
smtc.mockClient.WithValue(smtc.apiInput, smtc.apiOutput)
return &smtc
}
+func nilProviderTestCase() *akeylessTestCase {
+ return makeValidAkeylessTestCase("nil provider").SetMockClient(nil).SetExpectErr(errUninitalizedAkeylessProvider)
+}
+func failGetTestCase() *akeylessTestCase {
+ return makeValidAkeylessTestCase("fail GetSecret").SetExpectVal(false).SetExpectErr("fail get").
+ SetMockClient(fakeakeyless.New().SetGetSecretFn(func(secretName string, version int32) (string, error) { return "", errors.New("fail get") }))
+}
+
func makeValidRef() *esv1beta1.ExternalSecretDataRemoteRef {
return &esv1beta1.ExternalSecretDataRemoteRef{
Key: "test-secret",
@@ -74,7 +116,7 @@ func makeValidOutput() *fakeakeyless.Output {
}
func makeValidAkeylessTestCaseCustom(tweaks ...func(smtc *akeylessTestCase)) *akeylessTestCase {
- smtc := makeValidAkeylessTestCase()
+ smtc := makeValidAkeylessTestCase("")
for _, fn := range tweaks {
fn(smtc)
}
@@ -85,7 +127,7 @@ func makeValidAkeylessTestCaseCustom(tweaks ...func(smtc *akeylessTestCase)) *ak
// This case can be shared by both GetSecret and GetSecretMap tests.
// bad case: set apiErr.
var setAPIErr = func(smtc *akeylessTestCase) {
- smtc.apiOutput.Err = fmt.Errorf("oh no")
+ smtc.apiOutput.Err = errors.New("oh no")
smtc.expectError = "oh no"
}
@@ -113,16 +155,12 @@ func TestAkeylessGetSecret(t *testing.T) {
}
sm := Akeyless{}
- for k, v := range successCases {
+ for _, v := range successCases {
sm.Client = v.mockClient
fmt.Println(*v.ref)
out, err := sm.GetSecret(context.Background(), *v.ref)
- if !ErrorContains(err, v.expectError) {
- t.Errorf("[%d] unexpected error: %s, expected: '%s'", k, err.Error(), v.expectError)
- }
- if string(out) != v.expectedSecret {
- t.Errorf("[%d] unexpected secret: expected %s, got %s", k, v.expectedSecret, string(out))
- }
+ require.Truef(t, ErrorContains(err, v.expectError), fmtExpectedError, err, v.expectError)
+ require.Equal(t, string(out), v.expectedSecret)
}
}
@@ -159,9 +197,7 @@ func TestValidateStore(t *testing.T) {
}
_, err := provider.ValidateStore(store)
- if err != nil {
- t.Errorf(err.Error())
- }
+ require.NoError(t, err)
})
t.Run("k8s auth", func(t *testing.T) {
@@ -185,9 +221,7 @@ func TestValidateStore(t *testing.T) {
}
_, err := provider.ValidateStore(store)
- if err != nil {
- t.Errorf(err.Error())
- }
+ require.NoError(t, err)
})
t.Run("bad conf auth", func(t *testing.T) {
@@ -203,9 +237,7 @@ func TestValidateStore(t *testing.T) {
}
_, err := provider.ValidateStore(store)
- if err == nil {
- t.Errorf("expected an error")
- }
+ require.Error(t, err)
})
t.Run("bad k8s conf auth", func(t *testing.T) {
@@ -228,9 +260,7 @@ func TestValidateStore(t *testing.T) {
}
_, err := provider.ValidateStore(store)
- if err == nil {
- t.Errorf("expected an error")
- }
+ require.Error(t, err)
})
}
@@ -238,7 +268,7 @@ func TestGetSecretMap(t *testing.T) {
// good case: default version & deserialization
setDeserialization := func(smtc *akeylessTestCase) {
smtc.apiOutput.Value = `{"foo":"bar"}`
- smtc.expectedData["foo"] = []byte("bar")
+ smtc.expectedVal = map[string][]byte{"foo": []byte("bar")}
}
// bad case: invalid json
@@ -249,21 +279,17 @@ func TestGetSecretMap(t *testing.T) {
successCases := []*akeylessTestCase{
makeValidAkeylessTestCaseCustom(setDeserialization),
- makeValidAkeylessTestCaseCustom(setInvalidJSON),
- makeValidAkeylessTestCaseCustom(setAPIErr),
- makeValidAkeylessTestCaseCustom(setNilMockClient),
+ makeValidAkeylessTestCaseCustom(setInvalidJSON).SetExpectVal(map[string][]byte(nil)),
+ makeValidAkeylessTestCaseCustom(setAPIErr).SetExpectVal(map[string][]byte(nil)),
+ makeValidAkeylessTestCaseCustom(setNilMockClient).SetExpectVal(map[string][]byte(nil)),
}
sm := Akeyless{}
- for k, v := range successCases {
+ for _, v := range successCases {
sm.Client = v.mockClient
out, err := sm.GetSecretMap(context.Background(), *v.ref)
- if !ErrorContains(err, v.expectError) {
- t.Errorf("[%d] unexpected error: %s, expected: '%s'", k, err.Error(), v.expectError)
- }
- if err == nil && !reflect.DeepEqual(out, v.expectedData) {
- t.Errorf("[%d] unexpected secret data: expected %#v, got %#v", k, v.expectedData, out)
- }
+ require.Truef(t, ErrorContains(err, v.expectError), fmtExpectedError, err, v.expectError)
+ require.Equal(t, v.expectedVal.(map[string][]byte), out)
}
}
@@ -276,3 +302,150 @@ func ErrorContains(out error, want string) bool {
}
return strings.Contains(out.Error(), want)
}
+
+func TestSecretExists(t *testing.T) {
+ testCases := []*akeylessTestCase{
+ nilProviderTestCase().SetExpectVal(false),
+ makeValidAkeylessTestCase("no secret").SetExpectVal(false).
+ SetMockClient(fakeakeyless.New().SetGetSecretFn(func(secretName string, version int32) (string, error) { return "", ErrItemNotExists })),
+ failGetTestCase(),
+ makeValidAkeylessTestCase("success without property").SetExpectVal(true).SetExpectInput(&testingfake.PushSecretData{Property: ""}).
+ SetMockClient(fakeakeyless.New().SetGetSecretFn(func(secretName string, version int32) (string, error) { return "my secret", nil })),
+ makeValidAkeylessTestCase("fail unmarshal").SetExpectVal(false).SetExpectErr("invalid character 'd' looking for beginning of value").SetExpectInput(&testingfake.PushSecretData{Property: "prop"}).
+ SetMockClient(fakeakeyless.New().SetGetSecretFn(func(secretName string, version int32) (string, error) { return "daenerys", nil })),
+ makeValidAkeylessTestCase("no property").SetExpectVal(false).SetExpectInput(&testingfake.PushSecretData{Property: "prop"}).
+ SetMockClient(fakeakeyless.New().SetGetSecretFn(func(secretName string, version int32) (string, error) { return `{"propa": "a"}`, nil })),
+ makeValidAkeylessTestCase("success with property").SetExpectVal(true).SetExpectInput(&testingfake.PushSecretData{Property: "prop"}).
+ SetMockClient(fakeakeyless.New().SetGetSecretFn(func(secretName string, version int32) (string, error) { return `{"prop": "a"}`, nil })),
+ }
+
+ sm := Akeyless{}
+ t.Parallel()
+ for _, v := range testCases {
+ t.Run(v.testName, func(t *testing.T) {
+ sm.Client = v.mockClient
+ if v.input == nil {
+ v.input = &testingfake.PushSecretData{}
+ }
+ out, err := sm.SecretExists(context.Background(), v.input.(esv1beta1.PushSecretRemoteRef))
+ require.Truef(t, ErrorContains(err, v.expectError), fmtExpectedError, err, v.expectError)
+ require.Equal(t, out, v.expectedVal.(bool))
+ })
+ }
+}
+
+func TestPushSecret(t *testing.T) {
+ testCases := []*akeylessTestCase{
+ nilProviderTestCase(),
+ failGetTestCase(),
+ makeValidAkeylessTestCase("fail unmarshal").SetExpectErr("invalid character 'm' looking for beginning of value").
+ SetMockClient(fakeakeyless.New().SetGetSecretFn(func(secretName string, version int32) (string, error) { return "morgoth", nil })),
+ makeValidAkeylessTestCase("create new secret").SetExpectInput(&corev1.Secret{Data: map[string][]byte{"test": []byte("test")}}).
+ SetMockClient(fakeakeyless.New().SetGetSecretFn(func(secretName string, version int32) (string, error) { return "", ErrItemNotExists }).
+ SetCreateSecretFn(func(ctx context.Context, remoteKey string, data string) error {
+ if data != `{"test":"test"}` {
+ return errors.New("secret is not good")
+ }
+ return nil
+ })),
+ makeValidAkeylessTestCase("update secret").SetExpectInput(&corev1.Secret{Data: map[string][]byte{"test2": []byte("test2")}}).
+ SetMockClient(fakeakeyless.New().SetGetSecretFn(func(secretName string, version int32) (string, error) { return `{"test2":"untest"}`, nil }).
+ SetUpdateSecretFn(func(ctx context.Context, remoteKey string, data string) error {
+ if data != `{"test2":"test2"}` {
+ return errors.New("secret is not good")
+ }
+ return nil
+ })),
+ makeValidAkeylessTestCase("shouldnt update").SetExpectInput(&corev1.Secret{Data: map[string][]byte{"test": []byte("test")}}).
+ SetMockClient(fakeakeyless.New().SetGetSecretFn(func(secretName string, version int32) (string, error) { return `{"test":"test"}`, nil })),
+ makeValidAkeylessTestCase("merge secret maps").SetExpectInput(&corev1.Secret{Data: map[string][]byte{"test": []byte("test")}}).
+ SetExpectInput2(&testingfake.PushSecretData{Property: "test", SecretKey: "test"}).
+ SetMockClient(fakeakeyless.New().SetGetSecretFn(func(secretName string, version int32) (string, error) { return `{"test2":"test2"}`, nil }).
+ SetUpdateSecretFn(func(ctx context.Context, remoteKey string, data string) error {
+ expected := `{"test":"test","test2":"test2"}`
+ if data != expected {
+ return fmt.Errorf("secret %s expected %s", data, expected)
+ }
+ return nil
+ })),
+ }
+
+ sm := Akeyless{}
+ t.Parallel()
+ for _, v := range testCases {
+ t.Run(v.testName, func(t *testing.T) {
+ sm.Client = v.mockClient
+ if v.input == nil {
+ v.input = &corev1.Secret{}
+ }
+ if v.input2 == nil {
+ v.input2 = &testingfake.PushSecretData{}
+ }
+ err := sm.PushSecret(context.Background(), v.input.(*corev1.Secret), v.input2.(esv1beta1.PushSecretData))
+ require.Truef(t, ErrorContains(err, v.expectError), fmtExpectedError, err, v.expectError)
+ })
+ }
+}
+
+func TestDeleteSecret(t *testing.T) {
+ testCases := []*akeylessTestCase{
+ nilProviderTestCase(),
+ makeValidAkeylessTestCase("fail describe").SetExpectErr("err desc").
+ SetMockClient(fakeakeyless.New().SetDescribeItemFn(func(ctx context.Context, itemName string) (*akeyless.Item, error) { return nil, errors.New("err desc") })),
+ makeValidAkeylessTestCase("no such item").
+ SetMockClient(fakeakeyless.New().SetDescribeItemFn(func(ctx context.Context, itemName string) (*akeyless.Item, error) { return nil, nil })),
+ makeValidAkeylessTestCase("tags nil").
+ SetMockClient(fakeakeyless.New().SetDescribeItemFn(func(ctx context.Context, itemName string) (*akeyless.Item, error) { return &akeyless.Item{}, nil })),
+ makeValidAkeylessTestCase("no external secret managed tags").
+ SetMockClient(fakeakeyless.New().SetDescribeItemFn(func(ctx context.Context, itemName string) (*akeyless.Item, error) {
+ return &akeyless.Item{ItemTags: &[]string{"some-random-tag"}}, nil
+ })),
+ makeValidAkeylessTestCase("delete whole secret").SetExpectInput(&testingfake.PushSecretData{RemoteKey: "42"}).
+ SetMockClient(fakeakeyless.New().SetDescribeItemFn(func(ctx context.Context, itemName string) (*akeyless.Item, error) {
+ return &akeyless.Item{ItemTags: &[]string{extSecretManagedTag}}, nil
+ }).SetDeleteSecretFn(func(ctx context.Context, remoteKey string) error {
+ if remoteKey != "42" {
+ return fmt.Errorf("remote key %s expected %s", remoteKey, "42")
+ }
+ return nil
+ })),
+ makeValidAkeylessTestCase("delete property of secret").SetExpectInput(&testingfake.PushSecretData{Property: "Foo"}).
+ SetMockClient(fakeakeyless.New().SetDescribeItemFn(func(ctx context.Context, itemName string) (*akeyless.Item, error) {
+ return &akeyless.Item{ItemTags: &[]string{extSecretManagedTag}}, nil
+ }).SetGetSecretFn(func(secretName string, version int32) (string, error) {
+ return `{"Dio": "Brando", "Foo": "Fighters"}`, nil
+ }).
+ SetUpdateSecretFn(func(ctx context.Context, remoteKey string, data string) error {
+ expected := `{"Dio":"Brando"}`
+ if data != expected {
+ return fmt.Errorf("secret %s expected %s", data, expected)
+ }
+ return nil
+ })),
+ makeValidAkeylessTestCase("delete secret if one property left").SetExpectInput(&testingfake.PushSecretData{RemoteKey: "Rings", Property: "Annatar"}).
+ SetMockClient(fakeakeyless.New().SetDescribeItemFn(func(ctx context.Context, itemName string) (*akeyless.Item, error) {
+ return &akeyless.Item{ItemTags: &[]string{extSecretManagedTag}}, nil
+ }).SetGetSecretFn(func(secretName string, version int32) (string, error) {
+ return `{"Annatar": "The Lord of Gifts"}`, nil
+ }).
+ SetDeleteSecretFn(func(ctx context.Context, remoteKey string) error {
+ if remoteKey != "Rings" {
+ return fmt.Errorf("remote key %s expected %s", remoteKey, "Annatar")
+ }
+ return nil
+ })),
+ }
+
+ sm := Akeyless{}
+ t.Parallel()
+ for _, v := range testCases {
+ t.Run(v.testName, func(t *testing.T) {
+ sm.Client = v.mockClient
+ if v.input == nil {
+ v.input = &testingfake.PushSecretData{}
+ }
+ err := sm.DeleteSecret(context.Background(), v.input.(esv1beta1.PushSecretData))
+ require.Truef(t, ErrorContains(err, v.expectError), fmtExpectedError, err, v.expectError)
+ })
+ }
+}
diff --git a/pkg/provider/akeyless/auth.go b/pkg/provider/akeyless/auth.go
index fac21bc9e06..f23b20e0946 100644
--- a/pkg/provider/akeyless/auth.go
+++ b/pkg/provider/akeyless/auth.go
@@ -16,6 +16,7 @@ package akeyless
import (
"context"
+ "errors"
"fmt"
"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
@@ -37,7 +38,7 @@ func (a *akeylessBase) TokenFromSecretRef(ctx context.Context) (string, error) {
if prov.Auth.KubernetesAuth != nil {
auth := prov.Auth.KubernetesAuth
- return a.GetToken(auth.AccessID, "k8s", auth.K8sConfName, auth)
+ return a.GetToken(ctx, auth.AccessID, "k8s", auth.K8sConfName, auth)
}
accessID, err := resolvers.SecretKeyRef(
@@ -72,11 +73,11 @@ func (a *akeylessBase) TokenFromSecretRef(ctx context.Context) (string, error) {
}
if accessID == "" {
- return "", fmt.Errorf(errMissingSAK)
+ return "", errors.New(errMissingSAK)
}
if accessType == "" {
- return "", fmt.Errorf(errMissingAKID)
+ return "", errors.New(errMissingAKID)
}
- return a.GetToken(accessID, accessType, accessTypeParam, prov.Auth.KubernetesAuth)
+ return a.GetToken(ctx, accessID, accessType, accessTypeParam, prov.Auth.KubernetesAuth)
}
diff --git a/pkg/provider/akeyless/fake/fake.go b/pkg/provider/akeyless/fake/fake.go
index ba491a3ec2d..b3c2e212728 100644
--- a/pkg/provider/akeyless/fake/fake.go
+++ b/pkg/provider/akeyless/fake/fake.go
@@ -16,27 +16,78 @@ package fake
import (
"context"
+
+ akeyless "github.com/akeylesslabs/akeyless-go/v3"
)
type AkeylessMockClient struct {
- getSecret func(secretName, token string, version int32) (string, error)
+ getSecret func(secretName string, version int32) (string, error)
+ createSecret func(ctx context.Context, remoteKey, data string) error
+ updateSecret func(ctx context.Context, remoteKey, data string) error
+ deleteSecret func(ctx context.Context, remoteKey string) error
+ describeItem func(ctx context.Context, itemName string) (*akeyless.Item, error)
+}
+
+func New() *AkeylessMockClient {
+ return &AkeylessMockClient{}
+}
+
+func (mc *AkeylessMockClient) SetGetSecretFn(f func(secretName string, version int32) (string, error)) *AkeylessMockClient {
+ mc.getSecret = f
+ return mc
+}
+
+func (mc *AkeylessMockClient) SetCreateSecretFn(f func(ctx context.Context, remoteKey, data string) error) *AkeylessMockClient {
+ mc.createSecret = f
+ return mc
+}
+
+func (mc *AkeylessMockClient) SetUpdateSecretFn(f func(ctx context.Context, remoteKey, data string) error) *AkeylessMockClient {
+ mc.updateSecret = f
+ return mc
+}
+
+func (mc *AkeylessMockClient) SetDeleteSecretFn(f func(ctx context.Context, remoteKey string) error) *AkeylessMockClient {
+ mc.deleteSecret = f
+ return mc
+}
+
+func (mc *AkeylessMockClient) SetDescribeItemFn(f func(ctx context.Context, itemName string) (*akeyless.Item, error)) *AkeylessMockClient {
+ mc.describeItem = f
+ return mc
+}
+
+func (mc *AkeylessMockClient) CreateSecret(ctx context.Context, remoteKey, data string) error {
+ return mc.createSecret(ctx, remoteKey, data)
+}
+
+func (mc *AkeylessMockClient) DeleteSecret(ctx context.Context, remoteKey string) error {
+ return mc.deleteSecret(ctx, remoteKey)
+}
+
+func (mc *AkeylessMockClient) DescribeItem(ctx context.Context, itemName string) (*akeyless.Item, error) {
+ return mc.describeItem(ctx, itemName)
+}
+
+func (mc *AkeylessMockClient) UpdateSecret(ctx context.Context, remoteKey, data string) error {
+ return mc.updateSecret(ctx, remoteKey, data)
}
func (mc *AkeylessMockClient) TokenFromSecretRef(_ context.Context) (string, error) {
return "newToken", nil
}
-func (mc *AkeylessMockClient) GetSecretByType(_ context.Context, secretName, token string, version int32) (string, error) {
- return mc.getSecret(secretName, token, version)
+func (mc *AkeylessMockClient) GetSecretByType(_ context.Context, secretName string, version int32) (string, error) {
+ return mc.getSecret(secretName, version)
}
-func (mc *AkeylessMockClient) ListSecrets(_ context.Context, _, _, _ string) ([]string, error) {
+func (mc *AkeylessMockClient) ListSecrets(_ context.Context, _, _ string) ([]string, error) {
return nil, nil
}
func (mc *AkeylessMockClient) WithValue(_ *Input, out *Output) {
if mc != nil {
- mc.getSecret = func(secretName, token string, version int32) (string, error) {
+ mc.getSecret = func(secretName string, version int32) (string, error) {
return out.Value, out.Err
}
}
diff --git a/pkg/provider/akeyless/utils.go b/pkg/provider/akeyless/utils.go
index 26f82ae0d25..7bc406cc860 100644
--- a/pkg/provider/akeyless/utils.go
+++ b/pkg/provider/akeyless/utils.go
@@ -15,7 +15,7 @@ limitations under the License.
package akeyless
import (
- "encoding/base64"
+ "errors"
"fmt"
"io"
"net/http"
@@ -48,14 +48,14 @@ const (
// GetAKeylessProvider does the necessary nil checks and returns the akeyless provider or an error.
func GetAKeylessProvider(store esv1beta1.GenericStore) (*esv1beta1.AkeylessProvider, error) {
if store == nil {
- return nil, fmt.Errorf(errNilStore)
+ return nil, errors.New(errNilStore)
}
spc := store.GetSpec()
if spc == nil {
- return nil, fmt.Errorf(errMissingStoreSpec)
+ return nil, errors.New(errMissingStoreSpec)
}
if spc.Provider == nil {
- return nil, fmt.Errorf(errMissingProvider)
+ return nil, errors.New(errMissingStoreSpec)
}
prov := spc.Provider.Akeyless
if prov == nil {
@@ -109,12 +109,3 @@ func sendReq(url string) string {
body, _ := io.ReadAll(resp.Body)
return string(body)
}
-
-func base64decode(in []byte) ([]byte, error) {
- out := make([]byte, len(in))
- l, err := base64.StdEncoding.Decode(out, in)
- if err != nil {
- return nil, err
- }
- return out[:l], nil
-}
diff --git a/pkg/provider/alibaba/client.go b/pkg/provider/alibaba/client.go
index ab77a58886a..0de0b1f25f1 100644
--- a/pkg/provider/alibaba/client.go
+++ b/pkg/provider/alibaba/client.go
@@ -16,6 +16,7 @@ package alibaba
import (
"context"
+ "errors"
"fmt"
"net/http"
"net/url"
@@ -66,7 +67,7 @@ func newClient(config *openapi.Config, options *util.RuntimeOptions) (*secretsMa
}
if utils.Deref(endpoint) == "" {
- return nil, fmt.Errorf("error KMS endpoint is missing")
+ return nil, errors.New("error KMS endpoint is missing")
}
const (
@@ -123,29 +124,19 @@ func (s *secretsManagerClient) GetSecretValue(
func (s *secretsManagerClient) doAPICall(ctx context.Context,
action string,
request any) (any, error) {
- accessKeyID, err := s.config.Credential.GetAccessKeyId()
+ creds, err := s.config.Credential.GetCredential()
if err != nil {
- return nil, fmt.Errorf("error getting AccessKeyId: %w", err)
- }
-
- accessKeySecret, err := s.config.Credential.GetAccessKeySecret()
- if err != nil {
- return nil, fmt.Errorf("error getting AccessKeySecret: %w", err)
- }
-
- securityToken, err := s.config.Credential.GetSecurityToken()
- if err != nil {
- return nil, fmt.Errorf("error getting SecurityToken: %w", err)
+ return nil, fmt.Errorf("could not get credentials: %w", err)
}
apiRequest := newOpenAPIRequest(s.endpoint, action, methodTypeGET, request)
- apiRequest.query["AccessKeyId"] = accessKeyID
+ apiRequest.query["AccessKeyId"] = creds.AccessKeyId
- if utils.Deref(securityToken) != "" {
- apiRequest.query["SecurityToken"] = securityToken
+ if utils.Deref(creds.SecurityToken) != "" {
+ apiRequest.query["SecurityToken"] = creds.SecurityToken
}
- apiRequest.query["Signature"] = openapiutil.GetRPCSignature(apiRequest.query, utils.Ptr(apiRequest.method.String()), accessKeySecret)
+ apiRequest.query["Signature"] = openapiutil.GetRPCSignature(apiRequest.query, utils.Ptr(apiRequest.method.String()), creds.AccessKeySecret)
httpReq, err := newHTTPRequestWithContext(ctx, apiRequest)
if err != nil {
diff --git a/pkg/provider/alibaba/kms.go b/pkg/provider/alibaba/kms.go
index 5df02356d7e..f03b83817c4 100644
--- a/pkg/provider/alibaba/kms.go
+++ b/pkg/provider/alibaba/kms.go
@@ -17,6 +17,7 @@ package alibaba
import (
"context"
"encoding/json"
+ "errors"
"fmt"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
@@ -57,27 +58,27 @@ type SMInterface interface {
}
func (kms *KeyManagementService) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
- return fmt.Errorf(errNotImplemented)
+ return errors.New(errNotImplemented)
}
func (kms *KeyManagementService) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
- return fmt.Errorf(errNotImplemented)
+ return errors.New(errNotImplemented)
}
func (kms *KeyManagementService) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf(errNotImplemented)
+ return false, errors.New(errNotImplemented)
}
// Empty GetAllSecrets.
func (kms *KeyManagementService) GetAllSecrets(_ context.Context, _ esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
// TO be implemented
- return nil, fmt.Errorf(errNotImplemented)
+ return nil, errors.New(errNotImplemented)
}
// GetSecret returns a single secret from the provider.
func (kms *KeyManagementService) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
if utils.IsNil(kms.Client) {
- return nil, fmt.Errorf(errUninitalizedAlibabaProvider)
+ return nil, errors.New(errUninitalizedAlibabaProvider)
}
request := &kmssdk.GetSecretValueRequest{
@@ -199,7 +200,7 @@ func newAuth(ctx context.Context, kube kclient.Client, store esv1beta1.GenericSt
return credentials, nil
default:
- return nil, fmt.Errorf("alibaba authentication methods wasn't provided")
+ return nil, errors.New("alibaba authentication methods wasn't provided")
}
}
@@ -250,7 +251,7 @@ func (kms *KeyManagementService) Close(_ context.Context) error {
func (kms *KeyManagementService) Validate() (esv1beta1.ValidationResult, error) {
err := retry.Do(
func() error {
- _, err := kms.Config.Credential.GetSecurityToken()
+ _, err := kms.Config.Credential.GetCredential()
if err != nil {
return err
}
@@ -273,7 +274,7 @@ func (kms *KeyManagementService) ValidateStore(store esv1beta1.GenericStore) (ad
regionID := alibabaSpec.RegionID
if regionID == "" {
- return nil, fmt.Errorf("missing alibaba region")
+ return nil, errors.New("missing alibaba region")
}
return nil, kms.validateStoreAuth(store)
@@ -289,7 +290,7 @@ func (kms *KeyManagementService) validateStoreAuth(store esv1beta1.GenericStore)
case alibabaSpec.Auth.SecretRef != nil:
return kms.validateStoreAccessKeyAuth(store)
default:
- return fmt.Errorf("missing alibaba auth provider")
+ return errors.New("missing alibaba auth provider")
}
}
@@ -298,19 +299,19 @@ func (kms *KeyManagementService) validateStoreRRSAAuth(store esv1beta1.GenericSt
alibabaSpec := storeSpec.Provider.Alibaba
if alibabaSpec.Auth.RRSAAuth.OIDCProviderARN == "" {
- return fmt.Errorf("missing alibaba OIDC proivder ARN")
+ return errors.New("missing alibaba OIDC proivder ARN")
}
if alibabaSpec.Auth.RRSAAuth.OIDCTokenFilePath == "" {
- return fmt.Errorf("missing alibaba OIDC token file path")
+ return errors.New("missing alibaba OIDC token file path")
}
if alibabaSpec.Auth.RRSAAuth.RoleARN == "" {
- return fmt.Errorf("missing alibaba Assume Role ARN")
+ return errors.New("missing alibaba Assume Role ARN")
}
if alibabaSpec.Auth.RRSAAuth.SessionName == "" {
- return fmt.Errorf("missing alibaba session name")
+ return errors.New("missing alibaba session name")
}
return nil
@@ -327,11 +328,11 @@ func (kms *KeyManagementService) validateStoreAccessKeyAuth(store esv1beta1.Gene
}
if accessKeyID.Name == "" {
- return fmt.Errorf("missing alibaba access ID name")
+ return errors.New("missing alibaba access ID name")
}
if accessKeyID.Key == "" {
- return fmt.Errorf("missing alibaba access ID key")
+ return errors.New("missing alibaba access ID key")
}
accessKeySecret := alibabaSpec.Auth.SecretRef.AccessKeySecret
@@ -341,11 +342,11 @@ func (kms *KeyManagementService) validateStoreAccessKeyAuth(store esv1beta1.Gene
}
if accessKeySecret.Name == "" {
- return fmt.Errorf("missing alibaba access key secret name")
+ return errors.New("missing alibaba access key secret name")
}
if accessKeySecret.Key == "" {
- return fmt.Errorf("missing alibaba access key secret key")
+ return errors.New("missing alibaba access key secret key")
}
return nil
diff --git a/pkg/provider/alibaba/kms_test.go b/pkg/provider/alibaba/kms_test.go
index 01fca59a923..ea988b72636 100644
--- a/pkg/provider/alibaba/kms_test.go
+++ b/pkg/provider/alibaba/kms_test.go
@@ -16,7 +16,7 @@ package alibaba
import (
"context"
- "fmt"
+ "errors"
"reflect"
"strings"
"testing"
@@ -92,7 +92,7 @@ func makeValidKMSTestCaseCustom(tweaks ...func(kmstc *keyManagementServiceTestCa
}
var setAPIErr = func(kmstc *keyManagementServiceTestCase) {
- kmstc.apiErr = fmt.Errorf("oh no")
+ kmstc.apiErr = errors.New("oh no")
kmstc.expectError = "oh no"
}
@@ -203,7 +203,7 @@ func TestValidateAccessKeyStore(t *testing.T) {
_, err := kms.ValidateStore(store)
if err != nil {
- t.Errorf(err.Error())
+ t.Error(err.Error())
}
}
@@ -230,7 +230,7 @@ func TestValidateRRSAStore(t *testing.T) {
_, err := kms.ValidateStore(store)
if err != nil {
- t.Errorf(err.Error())
+ t.Error(err.Error())
}
}
diff --git a/pkg/provider/aws/auth/auth.go b/pkg/provider/aws/auth/auth.go
index 83e66770d0d..b20551ed4d2 100644
--- a/pkg/provider/aws/auth/auth.go
+++ b/pkg/provider/aws/auth/auth.go
@@ -155,7 +155,7 @@ func New(ctx context.Context, store esv1beta1.GenericStore, kube client.Client,
return sess, nil
}
-// NewSession creates a new aws session based on the provided store
+// NewGeneratorSession creates a new aws session based on the provided store
// it uses the following authentication mechanisms in order:
// * service-account token authentication via AssumeRoleWithWebIdentity
// * static credentials from a Kind=Secret, optionally with doing a AssumeRole.
diff --git a/pkg/provider/aws/parameterstore/fake/fake.go b/pkg/provider/aws/parameterstore/fake/fake.go
index cea9cafb820..1787e02a5d6 100644
--- a/pkg/provider/aws/parameterstore/fake/fake.go
+++ b/pkg/provider/aws/parameterstore/fake/fake.go
@@ -16,7 +16,7 @@ package fake
import (
"context"
- "fmt"
+ "errors"
"github.com/aws/aws-sdk-go/aws"
"github.com/aws/aws-sdk-go/aws/request"
@@ -26,12 +26,14 @@ import (
// Client implements the aws parameterstore interface.
type Client struct {
- GetParameterWithContextFn GetParameterWithContextFn
- GetParametersByPathWithContextFn GetParametersByPathWithContextFn
- PutParameterWithContextFn PutParameterWithContextFn
- DeleteParameterWithContextFn DeleteParameterWithContextFn
- DescribeParametersWithContextFn DescribeParametersWithContextFn
- ListTagsForResourceWithContextFn ListTagsForResourceWithContextFn
+ GetParameterWithContextFn GetParameterWithContextFn
+ GetParametersByPathWithContextFn GetParametersByPathWithContextFn
+ PutParameterWithContextFn PutParameterWithContextFn
+ PutParameterWithContextCalledN int
+ PutParameterWithContextFnCalledWith [][]*ssm.PutParameterInput
+ DeleteParameterWithContextFn DeleteParameterWithContextFn
+ DescribeParametersWithContextFn DescribeParametersWithContextFn
+ ListTagsForResourceWithContextFn ListTagsForResourceWithContextFn
}
type GetParameterWithContextFn func(aws.Context, *ssm.GetParameterInput, ...request.Option) (*ssm.GetParameterOutput, error)
@@ -86,6 +88,8 @@ func NewDescribeParametersWithContextFn(output *ssm.DescribeParametersOutput, er
}
func (sm *Client) PutParameterWithContext(ctx aws.Context, input *ssm.PutParameterInput, options ...request.Option) (*ssm.PutParameterOutput, error) {
+ sm.PutParameterWithContextCalledN++
+ sm.PutParameterWithContextFnCalledWith = append(sm.PutParameterWithContextFnCalledWith, []*ssm.PutParameterInput{input})
return sm.PutParameterWithContextFn(ctx, input, options...)
}
@@ -98,7 +102,7 @@ func NewPutParameterWithContextFn(output *ssm.PutParameterOutput, err error) Put
func (sm *Client) WithValue(in *ssm.GetParameterInput, val *ssm.GetParameterOutput, err error) {
sm.GetParameterWithContextFn = func(ctx aws.Context, paramIn *ssm.GetParameterInput, options ...request.Option) (*ssm.GetParameterOutput, error) {
if !cmp.Equal(paramIn, in) {
- return nil, fmt.Errorf("unexpected test argument")
+ return nil, errors.New("unexpected test argument")
}
return val, err
}
diff --git a/pkg/provider/aws/parameterstore/parameterstore.go b/pkg/provider/aws/parameterstore/parameterstore.go
index d6b6f0be9e4..b36f8693159 100644
--- a/pkg/provider/aws/parameterstore/parameterstore.go
+++ b/pkg/provider/aws/parameterstore/parameterstore.go
@@ -19,6 +19,7 @@ import (
"encoding/json"
"errors"
"fmt"
+ "slices"
"strings"
"github.com/aws/aws-sdk-go/aws"
@@ -28,7 +29,8 @@ import (
"github.com/aws/aws-sdk-go/service/ssm"
"github.com/tidwall/gjson"
corev1 "k8s.io/api/core/v1"
- utilpointer "k8s.io/utils/ptr"
+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ "k8s.io/utils/ptr"
ctrl "sigs.k8s.io/controller-runtime"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
@@ -37,8 +39,22 @@ import (
"github.com/external-secrets/external-secrets/pkg/metrics"
"github.com/external-secrets/external-secrets/pkg/provider/aws/util"
"github.com/external-secrets/external-secrets/pkg/utils"
+ "github.com/external-secrets/external-secrets/pkg/utils/metadata"
)
+// Tier defines policy details for PushSecret.
+type Tier struct {
+ Type string `json:"type"`
+ Policies *apiextensionsv1.JSON `json:"policies"`
+}
+
+// PushSecretMetadataSpec defines the spec for the metadata for PushSecret.
+type PushSecretMetadataSpec struct {
+ SecretType string `json:"secretType,omitempty"`
+ KMSKeyID string `json:"kmsKeyID,omitempty"`
+ Tier Tier `json:"tier,omitempty"`
+}
+
// https://github.com/external-secrets/external-secrets/issues/644
var (
_ esv1beta1.SecretsClient = &ParameterStore{}
@@ -52,6 +68,7 @@ type ParameterStore struct {
sess *session.Session
client PMInterface
referentAuth bool
+ prefix string
}
// PMInterface is a subset of the parameterstore api.
@@ -71,11 +88,12 @@ const (
)
// New constructs a ParameterStore Provider that is specific to a store.
-func New(sess *session.Session, cfg *aws.Config, referentAuth bool) (*ParameterStore, error) {
+func New(sess *session.Session, cfg *aws.Config, prefix string, referentAuth bool) (*ParameterStore, error) {
return &ParameterStore{
sess: sess,
referentAuth: referentAuth,
client: ssm.New(sess, cfg),
+ prefix: prefix,
}, nil
}
@@ -97,7 +115,7 @@ func (pm *ParameterStore) getTagsByName(ctx aws.Context, ref *ssm.GetParameterOu
}
func (pm *ParameterStore) DeleteSecret(ctx context.Context, remoteRef esv1beta1.PushSecretRemoteRef) error {
- secretName := remoteRef.GetRemoteKey()
+ secretName := pm.prefix + remoteRef.GetRemoteKey()
secretValue := ssm.GetParameterInput{
Name: &secretName,
}
@@ -134,17 +152,20 @@ func (pm *ParameterStore) DeleteSecret(ctx context.Context, remoteRef esv1beta1.
}
func (pm *ParameterStore) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf("not implemented")
+ return false, errors.New("not implemented")
}
func (pm *ParameterStore) PushSecret(ctx context.Context, secret *corev1.Secret, data esv1beta1.PushSecretData) error {
- parameterType := "String"
- overwrite := true
-
var (
value []byte
err error
)
+
+ meta, err := pm.constructMetadataWithDefaults(data.GetMetadata())
+ if err != nil {
+ return err
+ }
+
key := data.GetSecretKey()
if key == "" {
@@ -156,18 +177,26 @@ func (pm *ParameterStore) PushSecret(ctx context.Context, secret *corev1.Secret,
value = secret.Data[key]
}
- stringValue := string(value)
- secretName := data.GetRemoteKey()
-
+ secretName := pm.prefix + data.GetRemoteKey()
secretRequest := ssm.PutParameterInput{
- Name: &secretName,
- Value: &stringValue,
- Type: ¶meterType,
- Overwrite: &overwrite,
+ Name: ptr.To(pm.prefix + data.GetRemoteKey()),
+ Value: ptr.To(string(value)),
+ Type: ptr.To(meta.Spec.SecretType),
+ Overwrite: ptr.To(true),
+ }
+
+ if meta.Spec.SecretType == "SecureString" {
+ secretRequest.KeyId = &meta.Spec.KMSKeyID
+ }
+
+ if meta.Spec.Tier.Type == "Advanced" {
+ secretRequest.Tier = ptr.To(meta.Spec.Tier.Type)
+ secretRequest.Policies = ptr.To(string(meta.Spec.Tier.Policies.Raw))
}
secretValue := ssm.GetParameterInput{
- Name: &secretName,
+ Name: &secretName,
+ WithDecryption: aws.Bool(true),
}
existing, err := pm.client.GetParameterWithContext(ctx, &secretValue)
@@ -180,37 +209,43 @@ func (pm *ParameterStore) PushSecret(ctx context.Context, secret *corev1.Secret,
// If we have a valid parameter returned to us, check its tags
if existing != nil && existing.Parameter != nil {
- fmt.Println("The existing value contains data:", existing.String())
- tags, err := pm.getTagsByName(ctx, existing)
- if err != nil {
- return fmt.Errorf("error getting the existing tags for the parameter %v: %w", secretName, err)
- }
+ return pm.setExisting(ctx, existing, secretName, value, secretRequest)
+ }
- isManaged := isManagedByESO(tags)
+ // let's set the secret
+ // Do we need to delete the existing parameter on the remote?
+ return pm.setManagedRemoteParameter(ctx, secretRequest, true)
+}
- if !isManaged {
- return fmt.Errorf("secret not managed by external-secrets")
- }
+func (pm *ParameterStore) setExisting(ctx context.Context, existing *ssm.GetParameterOutput, secretName string, value []byte, secretRequest ssm.PutParameterInput) error {
+ tags, err := pm.getTagsByName(ctx, existing)
+ if err != nil {
+ return fmt.Errorf("error getting the existing tags for the parameter %v: %w", secretName, err)
+ }
- if existing.Parameter.Value != nil && *existing.Parameter.Value == string(value) {
- return nil
- }
+ isManaged := isManagedByESO(tags)
- return pm.setManagedRemoteParameter(ctx, secretRequest, false)
+ if !isManaged {
+ return errors.New("secret not managed by external-secrets")
}
- // let's set the secret
- // Do we need to delete the existing parameter on the remote?
- return pm.setManagedRemoteParameter(ctx, secretRequest, true)
+ // When fetching a remote SecureString parameter without decrypting, the default value will always be 'sensitive'
+ // in this case, no updates will be pushed remotely
+ if existing.Parameter.Value != nil && *existing.Parameter.Value == "sensitive" {
+ return errors.New("unable to compare 'sensitive' result, ensure to request a decrypted value")
+ }
+
+ if existing.Parameter.Value != nil && *existing.Parameter.Value == string(value) {
+ return nil
+ }
+
+ return pm.setManagedRemoteParameter(ctx, secretRequest, false)
}
func isManagedByESO(tags []*ssm.Tag) bool {
- for _, tag := range tags {
- if *tag.Key == managedBy && *tag.Value == externalSecrets {
- return true
- }
- }
- return false
+ return slices.ContainsFunc(tags, func(tag *ssm.Tag) bool {
+ return *tag.Key == managedBy && *tag.Value == externalSecrets
+ })
}
func (pm *ParameterStore) setManagedRemoteParameter(ctx context.Context, secretRequest ssm.PutParameterInput, createManagedByTags bool) error {
@@ -277,17 +312,17 @@ func (pm *ParameterStore) findByName(ctx context.Context, ref esv1beta1.External
logger.Info("GetParametersByPath: access denied. using fallback to describe parameters. It is recommended to add ssm:GetParametersByPath permissions", "path", ref.Path)
return pm.fallbackFindByName(ctx, ref)
}
+
return nil, err
}
+
for _, param := range it.Parameters {
if !matcher.MatchName(*param.Name) {
continue
}
- err = pm.fetchAndSet(ctx, data, *param.Name)
- if err != nil {
- return nil, err
- }
+ data[*param.Name] = []byte(*param.Value)
}
+
nextToken = it.NextToken
if nextToken == nil {
break
@@ -346,9 +381,9 @@ func (pm *ParameterStore) findByTags(ctx context.Context, ref esv1beta1.External
filters := make([]*ssm.ParameterStringFilter, 0)
for k, v := range ref.Tags {
filters = append(filters, &ssm.ParameterStringFilter{
- Key: utilpointer.To(fmt.Sprintf("tag:%s", k)),
- Values: []*string{utilpointer.To(v)},
- Option: utilpointer.To("Equals"),
+ Key: ptr.To(fmt.Sprintf("tag:%s", k)),
+ Values: []*string{ptr.To(v)},
+ Option: ptr.To("Equals"),
})
}
@@ -390,7 +425,7 @@ func (pm *ParameterStore) findByTags(ctx context.Context, ref esv1beta1.External
func (pm *ParameterStore) fetchAndSet(ctx context.Context, data map[string][]byte, name string) error {
out, err := pm.client.GetParameterWithContext(ctx, &ssm.GetParameterInput{
- Name: utilpointer.To(name),
+ Name: ptr.To(name),
WithDecryption: aws.Bool(true),
})
metrics.ObserveAPICall(constants.ProviderAWSPS, constants.CallAWSPSGetParameter, err)
@@ -444,7 +479,7 @@ func (pm *ParameterStore) GetSecret(ctx context.Context, ref esv1beta1.ExternalS
func (pm *ParameterStore) getParameterTags(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (*ssm.GetParameterOutput, error) {
param := ssm.GetParameterOutput{
Parameter: &ssm.Parameter{
- Name: parameterNameWithVersion(ref),
+ Name: pm.parameterNameWithVersion(ref),
},
}
tags, err := pm.getTagsByName(ctx, ¶m)
@@ -465,7 +500,7 @@ func (pm *ParameterStore) getParameterTags(ctx context.Context, ref esv1beta1.Ex
func (pm *ParameterStore) getParameterValue(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (*ssm.GetParameterOutput, error) {
out, err := pm.client.GetParameterWithContext(ctx, &ssm.GetParameterInput{
- Name: parameterNameWithVersion(ref),
+ Name: pm.parameterNameWithVersion(ref),
WithDecryption: aws.Bool(true),
})
@@ -496,8 +531,8 @@ func (pm *ParameterStore) GetSecretMap(ctx context.Context, ref esv1beta1.Extern
return secretData, nil
}
-func parameterNameWithVersion(ref esv1beta1.ExternalSecretDataRemoteRef) *string {
- name := ref.Key
+func (pm *ParameterStore) parameterNameWithVersion(ref esv1beta1.ExternalSecretDataRemoteRef) *string {
+ name := pm.prefix + ref.Key
if ref.Version != "" {
// see docs: https://docs.aws.amazon.com/systems-manager/latest/userguide/sysman-paramstore-versions.html#reference-parameter-version
name += ":" + ref.Version
@@ -521,3 +556,33 @@ func (pm *ParameterStore) Validate() (esv1beta1.ValidationResult, error) {
}
return esv1beta1.ValidationResultReady, nil
}
+
+func (pm *ParameterStore) constructMetadataWithDefaults(data *apiextensionsv1.JSON) (*metadata.PushSecretMetadata[PushSecretMetadataSpec], error) {
+ var (
+ meta *metadata.PushSecretMetadata[PushSecretMetadataSpec]
+ err error
+ )
+
+ meta, err = metadata.ParseMetadataParameters[PushSecretMetadataSpec](data)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse metadata: %w", err)
+ }
+
+ if meta == nil {
+ meta = &metadata.PushSecretMetadata[PushSecretMetadataSpec]{}
+ }
+
+ if meta.Spec.Tier.Type == "" {
+ meta.Spec.Tier.Type = "Standard"
+ }
+
+ if meta.Spec.SecretType == "" {
+ meta.Spec.SecretType = "String"
+ }
+
+ if meta.Spec.KMSKeyID == "" {
+ meta.Spec.KMSKeyID = "alias/aws/ssm"
+ }
+
+ return meta, nil
+}
diff --git a/pkg/provider/aws/parameterstore/parameterstore_test.go b/pkg/provider/aws/parameterstore/parameterstore_test.go
index 2dc81bcaa0e..2fd5f9e0a9c 100644
--- a/pkg/provider/aws/parameterstore/parameterstore_test.go
+++ b/pkg/provider/aws/parameterstore/parameterstore_test.go
@@ -17,7 +17,6 @@ package parameterstore
import (
"context"
"errors"
- "fmt"
"strings"
"testing"
@@ -25,7 +24,10 @@ import (
"github.com/aws/aws-sdk-go/aws/awserr"
"github.com/aws/aws-sdk-go/service/ssm"
"github.com/google/go-cmp/cmp"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
@@ -39,6 +41,11 @@ const (
invalidProp = "INVALPROP"
)
+var (
+ fakeSecretKey = "fakeSecretKey"
+ fakeValue = "fakeValue"
+)
+
type parameterstoreTestCase struct {
fakeClient *fakeps.Client
apiInput *ssm.GetParameterInput
@@ -48,6 +55,7 @@ type parameterstoreTestCase struct {
expectError string
expectedSecret string
expectedData map[string][]byte
+ prefix string
}
func makeValidParameterStoreTestCase() *parameterstoreTestCase {
@@ -57,6 +65,7 @@ func makeValidParameterStoreTestCase() *parameterstoreTestCase {
apiOutput: makeValidAPIOutput(),
remoteRef: makeValidRemoteRef(),
apiErr: nil,
+ prefix: "",
expectError: "",
expectedSecret: "",
expectedData: make(map[string][]byte),
@@ -238,7 +247,7 @@ func TestDeleteSecret(t *testing.T) {
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
- ref := fake.PushSecretData{RemoteKey: "fake-key"}
+ ref := fake.PushSecretData{RemoteKey: remoteKey}
ps := ParameterStore{
client: &tc.args.client,
}
@@ -261,11 +270,12 @@ func TestDeleteSecret(t *testing.T) {
})
}
}
+
+const remoteKey = "fake-key"
+
func TestPushSecret(t *testing.T) {
invalidParameters := errors.New(ssm.ErrCodeInvalidParameters)
alreadyExistsError := errors.New(ssm.ErrCodeAlreadyExistsException)
- fakeSecretKey := "fakeSecretKey"
- fakeValue := "fakeValue"
fakeSecret := &corev1.Secret{
Data: map[string][]byte{
fakeSecretKey: []byte(fakeValue),
@@ -306,8 +316,9 @@ func TestPushSecret(t *testing.T) {
}
type args struct {
- store *esv1beta1.AWSProvider
- client fakeps.Client
+ store *esv1beta1.AWSProvider
+ metadata *apiextensionsv1.JSON
+ client fakeps.Client
}
type want struct {
@@ -391,7 +402,7 @@ func TestPushSecret(t *testing.T) {
},
},
want: want{
- err: fmt.Errorf("secret not managed by external-secrets"),
+ err: errors.New("secret not managed by external-secrets"),
},
},
"SetSecretGetTagsError": {
@@ -402,11 +413,11 @@ func TestPushSecret(t *testing.T) {
PutParameterWithContextFn: fakeps.NewPutParameterWithContextFn(putParameterOutput, nil),
GetParameterWithContextFn: fakeps.NewGetParameterWithContextFn(validGetParameterOutput, nil),
DescribeParametersWithContextFn: fakeps.NewDescribeParametersWithContextFn(describeParameterOutput, nil),
- ListTagsForResourceWithContextFn: fakeps.NewListTagsForResourceWithContextFn(nil, fmt.Errorf("you shall not tag")),
+ ListTagsForResourceWithContextFn: fakeps.NewListTagsForResourceWithContextFn(nil, errors.New("you shall not tag")),
},
},
want: want{
- err: fmt.Errorf("you shall not tag"),
+ err: errors.New("you shall not tag"),
},
},
"SetSecretContentMatches": {
@@ -424,11 +435,161 @@ func TestPushSecret(t *testing.T) {
err: nil,
},
},
+ "SetSecretWithValidMetadata": {
+ reason: "test push secret with valid parameterStoreType metadata",
+ args: args{
+ store: makeValidParameterStore().Spec.Provider.AWS,
+ metadata: &apiextensionsv1.JSON{
+ Raw: []byte(`{
+ "apiVersion": "kubernetes.external-secrets.io/v1alpha1",
+ "kind": "PushSecretMetadata",
+ "spec": {
+ "secretType": "SecureString",
+ "kmsKeyID": "arn:aws:kms:sa-east-1:00000000000:key/bb123123-b2b0-4f60-ac3a-44a13f0e6b6c"
+ }
+ }`),
+ },
+ client: fakeps.Client{
+ PutParameterWithContextFn: fakeps.NewPutParameterWithContextFn(putParameterOutput, nil),
+ GetParameterWithContextFn: fakeps.NewGetParameterWithContextFn(sameGetParameterOutput, nil),
+ DescribeParametersWithContextFn: fakeps.NewDescribeParametersWithContextFn(describeParameterOutput, nil),
+ ListTagsForResourceWithContextFn: fakeps.NewListTagsForResourceWithContextFn(validListTagsForResourceOutput, nil),
+ },
+ },
+ want: want{
+ err: nil,
+ },
+ },
+ "SetSecretWithValidMetadataListString": {
+ reason: "test push secret with valid parameterStoreType metadata and unused parameterStoreKeyID",
+ args: args{
+ store: makeValidParameterStore().Spec.Provider.AWS,
+ metadata: &apiextensionsv1.JSON{
+ Raw: []byte(`{
+ "apiVersion": "kubernetes.external-secrets.io/v1alpha1",
+ "kind": "PushSecretMetadata",
+ "spec": {
+ "secretType": "StringList"
+ }
+ }`),
+ },
+ client: fakeps.Client{
+ PutParameterWithContextFn: fakeps.NewPutParameterWithContextFn(putParameterOutput, nil),
+ GetParameterWithContextFn: fakeps.NewGetParameterWithContextFn(sameGetParameterOutput, nil),
+ DescribeParametersWithContextFn: fakeps.NewDescribeParametersWithContextFn(describeParameterOutput, nil),
+ ListTagsForResourceWithContextFn: fakeps.NewListTagsForResourceWithContextFn(validListTagsForResourceOutput, nil),
+ },
+ },
+ want: want{
+ err: nil,
+ },
+ },
+ "SetSecretWithInvalidMetadata": {
+ reason: "test push secret with invalid metadata structure",
+ args: args{
+ store: makeValidParameterStore().Spec.Provider.AWS,
+ metadata: &apiextensionsv1.JSON{
+ Raw: []byte(`{ fakeMetadataKey: "" }`),
+ },
+ client: fakeps.Client{
+ PutParameterWithContextFn: fakeps.NewPutParameterWithContextFn(putParameterOutput, nil),
+ GetParameterWithContextFn: fakeps.NewGetParameterWithContextFn(sameGetParameterOutput, nil),
+ DescribeParametersWithContextFn: fakeps.NewDescribeParametersWithContextFn(describeParameterOutput, nil),
+ ListTagsForResourceWithContextFn: fakeps.NewListTagsForResourceWithContextFn(validListTagsForResourceOutput, nil),
+ },
+ },
+ want: want{
+ err: errors.New(`failed to parse metadata: failed to parse kubernetes.external-secrets.io/v1alpha1 PushSecretMetadata: error unmarshaling JSON: while decoding JSON: json: unknown field "fakeMetadataKey"`),
+ },
+ },
+ "GetRemoteSecretWithoutDecryption": {
+ reason: "test if push secret's get remote source is encrypted for valid comparison",
+ args: args{
+ store: makeValidParameterStore().Spec.Provider.AWS,
+ metadata: &apiextensionsv1.JSON{
+ Raw: []byte(`{
+ "apiVersion": "kubernetes.external-secrets.io/v1alpha1",
+ "kind": "PushSecretMetadata",
+ "spec": {
+ "secretType": "SecureString",
+ "kmsKeyID": "arn:aws:kms:sa-east-1:00000000000:key/bb123123-b2b0-4f60-ac3a-44a13f0e6b6c"
+ }
+ }`),
+ },
+ client: fakeps.Client{
+ PutParameterWithContextFn: fakeps.NewPutParameterWithContextFn(putParameterOutput, nil),
+ GetParameterWithContextFn: fakeps.NewGetParameterWithContextFn(&ssm.GetParameterOutput{
+ Parameter: &ssm.Parameter{
+ Type: aws.String("SecureString"),
+ Value: aws.String("sensitive"),
+ },
+ }, nil),
+ DescribeParametersWithContextFn: fakeps.NewDescribeParametersWithContextFn(describeParameterOutput, nil),
+ ListTagsForResourceWithContextFn: fakeps.NewListTagsForResourceWithContextFn(validListTagsForResourceOutput, nil),
+ },
+ },
+ want: want{
+ err: errors.New("unable to compare 'sensitive' result, ensure to request a decrypted value"),
+ },
+ },
+ "SecretWithAdvancedTier": {
+ reason: "test if we can provide advanced tier policies",
+ args: args{
+ store: makeValidParameterStore().Spec.Provider.AWS,
+ metadata: &apiextensionsv1.JSON{
+ Raw: []byte(`{
+ "apiVersion": "kubernetes.external-secrets.io/v1alpha1",
+ "kind": "PushSecretMetadata",
+ "spec": {
+ "secretType": "SecureString",
+ "kmsKeyID": "arn:aws:kms:sa-east-1:00000000000:key/bb123123-b2b0-4f60-ac3a-44a13f0e6b6c",
+ "tier": {
+ "type": "Advanced",
+ "policies": [
+ {
+ "type": "Expiration",
+ "version": "1.0",
+ "attributes": {
+ "timestamp": "2024-12-02T21:34:33.000Z"
+ }
+ },
+ {
+ "type": "ExpirationNotification",
+ "version": "1.0",
+ "attributes": {
+ "before": "2",
+ "unit": "Days"
+ }
+ }
+ ]
+ }
+ }
+ }`),
+ },
+ client: fakeps.Client{
+ PutParameterWithContextFn: fakeps.NewPutParameterWithContextFn(putParameterOutput, nil),
+ GetParameterWithContextFn: fakeps.NewGetParameterWithContextFn(&ssm.GetParameterOutput{
+ Parameter: &ssm.Parameter{
+ Type: aws.String("SecureString"),
+ Value: aws.String("sensitive"),
+ },
+ }, nil),
+ DescribeParametersWithContextFn: fakeps.NewDescribeParametersWithContextFn(describeParameterOutput, nil),
+ ListTagsForResourceWithContextFn: fakeps.NewListTagsForResourceWithContextFn(validListTagsForResourceOutput, nil),
+ },
+ },
+ want: want{
+ err: errors.New("unable to compare 'sensitive' result, ensure to request a decrypted value"),
+ },
+ },
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
- psd := fake.PushSecretData{SecretKey: fakeSecretKey, RemoteKey: "fake-key"}
+ psd := fake.PushSecretData{SecretKey: fakeSecretKey, RemoteKey: remoteKey}
+ if tc.args.metadata != nil {
+ psd.Metadata = tc.args.metadata
+ }
ps := ParameterStore{
client: &tc.args.client,
}
@@ -442,13 +603,89 @@ func TestPushSecret(t *testing.T) {
// if errors are the same type but their contents do not match
if err != nil && tc.want.err != nil {
if !strings.Contains(err.Error(), tc.want.err.Error()) {
- t.Errorf("\nTesting SetSecret:\nName: %v\nReason: %v\nWant error: %v\nGot error got nil", name, tc.reason, tc.want.err)
+ t.Errorf("\nTesting SetSecret:\nName: %v\nReason: %v\nWant error: %v\nGot error: %s", name, tc.reason, tc.want.err, err)
}
}
})
}
}
+func TestPushSecretWithPrefix(t *testing.T) {
+ fakeSecret := &corev1.Secret{
+ Data: map[string][]byte{
+ fakeSecretKey: []byte(fakeValue),
+ },
+ }
+ managedByESO := ssm.Tag{
+ Key: &managedBy,
+ Value: &externalSecrets,
+ }
+ putParameterOutput := &ssm.PutParameterOutput{}
+ getParameterOutput := &ssm.GetParameterOutput{}
+ describeParameterOutput := &ssm.DescribeParametersOutput{}
+ validListTagsForResourceOutput := &ssm.ListTagsForResourceOutput{
+ TagList: []*ssm.Tag{&managedByESO},
+ }
+
+ client := fakeps.Client{
+ PutParameterWithContextFn: fakeps.NewPutParameterWithContextFn(putParameterOutput, nil),
+ GetParameterWithContextFn: fakeps.NewGetParameterWithContextFn(getParameterOutput, nil),
+ DescribeParametersWithContextFn: fakeps.NewDescribeParametersWithContextFn(describeParameterOutput, nil),
+ ListTagsForResourceWithContextFn: fakeps.NewListTagsForResourceWithContextFn(validListTagsForResourceOutput, nil),
+ }
+
+ psd := fake.PushSecretData{SecretKey: fakeSecretKey, RemoteKey: remoteKey}
+ ps := ParameterStore{
+ client: &client,
+ prefix: "/test/this/thing/",
+ }
+ err := ps.PushSecret(context.TODO(), fakeSecret, psd)
+ require.NoError(t, err)
+
+ input := client.PutParameterWithContextFnCalledWith[0][0]
+ assert.Equal(t, "/test/this/thing/fake-key", *input.Name)
+}
+
+func TestPushSecretCalledOnlyOnce(t *testing.T) {
+ fakeSecret := &corev1.Secret{
+ Data: map[string][]byte{
+ fakeSecretKey: []byte(fakeValue),
+ },
+ }
+
+ managedByESO := ssm.Tag{
+ Key: &managedBy,
+ Value: &externalSecrets,
+ }
+
+ putParameterOutput := &ssm.PutParameterOutput{}
+ validGetParameterOutput := &ssm.GetParameterOutput{
+ Parameter: &ssm.Parameter{
+ Value: &fakeValue,
+ },
+ }
+ describeParameterOutput := &ssm.DescribeParametersOutput{}
+ validListTagsForResourceOutput := &ssm.ListTagsForResourceOutput{
+ TagList: []*ssm.Tag{&managedByESO},
+ }
+
+ client := fakeps.Client{
+ PutParameterWithContextFn: fakeps.NewPutParameterWithContextFn(putParameterOutput, nil),
+ GetParameterWithContextFn: fakeps.NewGetParameterWithContextFn(validGetParameterOutput, nil),
+ DescribeParametersWithContextFn: fakeps.NewDescribeParametersWithContextFn(describeParameterOutput, nil),
+ ListTagsForResourceWithContextFn: fakeps.NewListTagsForResourceWithContextFn(validListTagsForResourceOutput, nil),
+ }
+
+ psd := fake.PushSecretData{SecretKey: fakeSecretKey, RemoteKey: remoteKey}
+ ps := ParameterStore{
+ client: &client,
+ }
+
+ require.NoError(t, ps.PushSecret(context.TODO(), fakeSecret, psd))
+
+ assert.Equal(t, 0, client.PutParameterWithContextCalledN)
+}
+
// test the ssm<->aws interface
// make sure correct values are passed and errors are handled accordingly.
func TestGetSecret(t *testing.T) {
@@ -458,12 +695,24 @@ func TestGetSecret(t *testing.T) {
pstc.expectedSecret = "RRRRR"
}
+ // good case: key is passed in and prefix is set, output is sent back
+ setSecretStringWithPrefix := func(pstc *parameterstoreTestCase) {
+ pstc.apiInput = &ssm.GetParameterInput{
+ Name: aws.String("/test/this/baz"),
+ WithDecryption: aws.Bool(true),
+ }
+ pstc.prefix = "/test/this"
+ pstc.apiOutput.Parameter.Value = aws.String("RRRRR")
+ pstc.expectedSecret = "RRRRR"
+ }
+
// good case: extract property
setExtractProperty := func(pstc *parameterstoreTestCase) {
pstc.apiOutput.Parameter.Value = aws.String(`{"/shmoo": "bang"}`)
pstc.expectedSecret = "bang"
pstc.remoteRef.Property = "/shmoo"
}
+
// good case: extract property with `.`
setExtractPropertyWithDot := func(pstc *parameterstoreTestCase) {
pstc.apiOutput.Parameter.Value = aws.String(`{"/shmoo.boom": "bang"}`)
@@ -501,7 +750,7 @@ func TestGetSecret(t *testing.T) {
// base case: api output return error
setAPIError := func(pstc *parameterstoreTestCase) {
pstc.apiOutput = &ssm.GetParameterOutput{}
- pstc.apiErr = fmt.Errorf("oh no")
+ pstc.apiErr = errors.New("oh no")
pstc.expectError = "oh no"
}
@@ -538,6 +787,7 @@ func TestGetSecret(t *testing.T) {
}
successCases := []*parameterstoreTestCase{
+ makeValidParameterStoreTestCaseCustom(setSecretStringWithPrefix),
makeValidParameterStoreTestCaseCustom(setSecretString),
makeValidParameterStoreTestCaseCustom(setExtractProperty),
makeValidParameterStoreTestCaseCustom(setMissingProperty),
@@ -554,6 +804,7 @@ func TestGetSecret(t *testing.T) {
ps := ParameterStore{}
for k, v := range successCases {
ps.client = v.fakeClient
+ ps.prefix = v.prefix
out, err := ps.GetSecret(context.Background(), *v.remoteRef)
if !ErrorContains(err, v.expectError) {
t.Errorf("[%d] unexpected error: %s, expected: '%s'", k, err.Error(), v.expectError)
@@ -583,7 +834,7 @@ func TestGetSecretMap(t *testing.T) {
setAPIError := func(pstc *parameterstoreTestCase) {
pstc.apiOutput.Parameter = &ssm.Parameter{}
pstc.expectError = "some api err"
- pstc.apiErr = fmt.Errorf("some api err")
+ pstc.apiErr = errors.New("some api err")
}
// bad case: invalid json
setInvalidJSON := func(pstc *parameterstoreTestCase) {
diff --git a/pkg/provider/aws/provider.go b/pkg/provider/aws/provider.go
index 77e1cc13655..0089f5fb720 100644
--- a/pkg/provider/aws/provider.go
+++ b/pkg/provider/aws/provider.go
@@ -156,7 +156,7 @@ func newClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Cl
case esv1beta1.AWSServiceSecretsManager:
return secretsmanager.New(sess, cfg, prov.SecretsManager, true)
case esv1beta1.AWSServiceParameterStore:
- return parameterstore.New(sess, cfg, true)
+ return parameterstore.New(sess, cfg, storeSpec.Provider.AWS.Prefix, true)
}
return nil, fmt.Errorf(errUnknownProviderService, prov.Service)
}
@@ -195,7 +195,7 @@ func newClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Cl
case esv1beta1.AWSServiceSecretsManager:
return secretsmanager.New(sess, cfg, prov.SecretsManager, false)
case esv1beta1.AWSServiceParameterStore:
- return parameterstore.New(sess, cfg, false)
+ return parameterstore.New(sess, cfg, storeSpec.Provider.AWS.Prefix, false)
}
return nil, fmt.Errorf(errUnknownProviderService, prov.Service)
}
diff --git a/pkg/provider/aws/secretsmanager/fake/fake.go b/pkg/provider/aws/secretsmanager/fake/fake.go
index 21ca2135e23..4794478635f 100644
--- a/pkg/provider/aws/secretsmanager/fake/fake.go
+++ b/pkg/provider/aws/secretsmanager/fake/fake.go
@@ -16,6 +16,7 @@ package fake
import (
"bytes"
+ "errors"
"fmt"
"time"
@@ -27,14 +28,15 @@ import (
// Client implements the aws secretsmanager interface.
type Client struct {
- ExecutionCounter int
- valFn map[string]func(*awssm.GetSecretValueInput) (*awssm.GetSecretValueOutput, error)
- CreateSecretWithContextFn CreateSecretWithContextFn
- GetSecretValueWithContextFn GetSecretValueWithContextFn
- PutSecretValueWithContextFn PutSecretValueWithContextFn
- DescribeSecretWithContextFn DescribeSecretWithContextFn
- DeleteSecretWithContextFn DeleteSecretWithContextFn
- ListSecretsFn ListSecretsFn
+ ExecutionCounter int
+ valFn map[string]func(*awssm.GetSecretValueInput) (*awssm.GetSecretValueOutput, error)
+ CreateSecretWithContextFn CreateSecretWithContextFn
+ GetSecretValueWithContextFn GetSecretValueWithContextFn
+ PutSecretValueWithContextFn PutSecretValueWithContextFn
+ DescribeSecretWithContextFn DescribeSecretWithContextFn
+ DeleteSecretWithContextFn DeleteSecretWithContextFn
+ ListSecretsFn ListSecretsFn
+ BatchGetSecretValueWithContextFn BatchGetSecretValueWithContextFn
}
type CreateSecretWithContextFn func(aws.Context, *awssm.CreateSecretInput, ...request.Option) (*awssm.CreateSecretOutput, error)
@@ -43,6 +45,7 @@ type PutSecretValueWithContextFn func(aws.Context, *awssm.PutSecretValueInput, .
type DescribeSecretWithContextFn func(aws.Context, *awssm.DescribeSecretInput, ...request.Option) (*awssm.DescribeSecretOutput, error)
type DeleteSecretWithContextFn func(ctx aws.Context, input *awssm.DeleteSecretInput, opts ...request.Option) (*awssm.DeleteSecretOutput, error)
type ListSecretsFn func(ctx aws.Context, input *awssm.ListSecretsInput, opts ...request.Option) (*awssm.ListSecretsOutput, error)
+type BatchGetSecretValueWithContextFn func(aws.Context, *awssm.BatchGetSecretValueInput, ...request.Option) (*awssm.BatchGetSecretValueOutput, error)
func (sm Client) CreateSecretWithContext(ctx aws.Context, input *awssm.CreateSecretInput, options ...request.Option) (*awssm.CreateSecretOutput, error) {
return sm.CreateSecretWithContextFn(ctx, input, options...)
@@ -51,7 +54,7 @@ func (sm Client) CreateSecretWithContext(ctx aws.Context, input *awssm.CreateSec
func NewCreateSecretWithContextFn(output *awssm.CreateSecretOutput, err error, expectedSecretBinary ...[]byte) CreateSecretWithContextFn {
return func(ctx aws.Context, actualInput *awssm.CreateSecretInput, options ...request.Option) (*awssm.CreateSecretOutput, error) {
if *actualInput.ClientRequestToken != "00000000-0000-0000-0000-000000000001" {
- return nil, fmt.Errorf("expected the version to be 1 at creation")
+ return nil, errors.New("expected the version to be 1 at creation")
}
if len(expectedSecretBinary) == 1 {
if bytes.Equal(actualInput.SecretBinary, expectedSecretBinary[0]) {
@@ -156,13 +159,17 @@ func (sm *Client) GetSecretValue(in *awssm.GetSecretValueInput) (*awssm.GetSecre
if entry, found := sm.valFn[sm.cacheKeyForInput(in)]; found {
return entry(in)
}
- return nil, fmt.Errorf("test case not found")
+ return nil, errors.New("test case not found")
}
func (sm *Client) ListSecrets(input *awssm.ListSecretsInput) (*awssm.ListSecretsOutput, error) {
return sm.ListSecretsFn(nil, input)
}
+func (sm *Client) BatchGetSecretValueWithContext(_ aws.Context, in *awssm.BatchGetSecretValueInput, _ ...request.Option) (*awssm.BatchGetSecretValueOutput, error) {
+ return sm.BatchGetSecretValueWithContextFn(nil, in)
+}
+
func (sm *Client) cacheKeyForInput(in *awssm.GetSecretValueInput) string {
var secretID, versionID string
if in.SecretId != nil {
@@ -177,7 +184,7 @@ func (sm *Client) cacheKeyForInput(in *awssm.GetSecretValueInput) string {
func (sm *Client) WithValue(in *awssm.GetSecretValueInput, val *awssm.GetSecretValueOutput, err error) {
sm.valFn[sm.cacheKeyForInput(in)] = func(paramIn *awssm.GetSecretValueInput) (*awssm.GetSecretValueOutput, error) {
if !cmp.Equal(paramIn, in) {
- return nil, fmt.Errorf("unexpected test argument")
+ return nil, errors.New("unexpected test argument")
}
return val, err
}
diff --git a/pkg/provider/aws/secretsmanager/secretsmanager.go b/pkg/provider/aws/secretsmanager/secretsmanager.go
index 1a3bf9a665d..4113e42e45e 100644
--- a/pkg/provider/aws/secretsmanager/secretsmanager.go
+++ b/pkg/provider/aws/secretsmanager/secretsmanager.go
@@ -65,6 +65,7 @@ type SecretsManager struct {
// SMInterface is a subset of the smiface api.
// see: https://docs.aws.amazon.com/sdk-for-go/api/service/secretsmanager/secretsmanageriface/
type SMInterface interface {
+ BatchGetSecretValueWithContext(aws.Context, *awssm.BatchGetSecretValueInput, ...request.Option) (*awssm.BatchGetSecretValueOutput, error)
ListSecrets(*awssm.ListSecretsInput) (*awssm.ListSecretsOutput, error)
GetSecretValue(*awssm.GetSecretValueInput) (*awssm.GetSecretValueOutput, error)
CreateSecretWithContext(aws.Context, *awssm.CreateSecretInput, ...request.Option) (*awssm.CreateSecretOutput, error)
@@ -112,55 +113,11 @@ func (sm *SecretsManager) fetch(ctx context.Context, ref esv1beta1.ExternalSecre
return secretOut, nil
}
- var secretOut *awssm.GetSecretValueOutput
- var err error
-
- if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
- describeSecretInput := &awssm.DescribeSecretInput{
- SecretId: &ref.Key,
- }
-
- descOutput, err := sm.client.DescribeSecretWithContext(ctx, describeSecretInput)
- if err != nil {
- return nil, err
- }
- log.Info("found metadata secret", "key", ref.Key, "output", descOutput)
-
- jsonTags, err := util.SecretTagsToJSONString(descOutput.Tags)
- if err != nil {
- return nil, err
- }
- secretOut = &awssm.GetSecretValueOutput{
- ARN: descOutput.ARN,
- CreatedDate: descOutput.CreatedDate,
- Name: descOutput.Name,
- SecretString: &jsonTags,
- VersionId: &ver,
- }
- } else {
- var getSecretValueInput *awssm.GetSecretValueInput
- if strings.HasPrefix(ver, "uuid/") {
- versionID := strings.TrimPrefix(ver, "uuid/")
- getSecretValueInput = &awssm.GetSecretValueInput{
- SecretId: &ref.Key,
- VersionId: &versionID,
- }
- } else {
- getSecretValueInput = &awssm.GetSecretValueInput{
- SecretId: &ref.Key,
- VersionStage: &ver,
- }
- }
- secretOut, err = sm.client.GetSecretValue(getSecretValueInput)
- metrics.ObserveAPICall(constants.ProviderAWSSM, constants.CallAWSSMGetSecretValue, err)
- var nf *awssm.ResourceNotFoundException
- if errors.As(err, &nf) {
- return nil, esv1beta1.NoSecretErr
- }
- if err != nil {
- return nil, err
- }
+ secretOut, err := sm.constructSecretValue(ctx, ref, ver)
+ if err != nil {
+ return nil, err
}
+
sm.cache[cacheKey] = secretOut
return secretOut, nil
@@ -212,13 +169,32 @@ func (sm *SecretsManager) DeleteSecret(ctx context.Context, remoteRef esv1beta1.
return err
}
-func (sm *SecretsManager) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf("not implemented")
+func (sm *SecretsManager) SecretExists(ctx context.Context, pushSecretRef esv1beta1.PushSecretRemoteRef) (bool, error) {
+ secretName := pushSecretRef.GetRemoteKey()
+ secretValue := awssm.GetSecretValueInput{
+ SecretId: &secretName,
+ }
+ _, err := sm.client.GetSecretValueWithContext(ctx, &secretValue)
+ if err != nil {
+ return sm.handleSecretError(err)
+ }
+ return true, nil
+}
+
+func (sm *SecretsManager) handleSecretError(err error) (bool, error) {
+ var aerr awserr.Error
+ if ok := errors.As(err, &aerr); !ok {
+ return false, err
+ }
+ if aerr.Code() == awssm.ErrCodeResourceNotFoundException {
+ return false, nil
+ }
+ return false, err
}
func (sm *SecretsManager) PushSecret(ctx context.Context, secret *corev1.Secret, psd esv1beta1.PushSecretData) error {
if psd.GetSecretKey() == "" {
- return fmt.Errorf("pushing the whole secret is not yet implemented")
+ return errors.New("pushing the whole secret is not yet implemented")
}
secretName := psd.GetRemoteKey()
@@ -329,12 +305,16 @@ func (sm *SecretsManager) findByName(ctx context.Context, ref esv1beta1.External
ref.Path,
},
})
+
+ return sm.fetchWithBatch(ctx, filters, matcher)
}
data := make(map[string][]byte)
var nextToken *string
for {
+ // I put this into the for loop on purpose.
+ log.V(0).Info("using ListSecret to fetch all secrets; this is a costly operations, please use batching by defining a _path_")
it, err := sm.client.ListSecrets(&awssm.ListSecretsInput{
Filters: filters,
NextToken: nextToken,
@@ -349,8 +329,7 @@ func (sm *SecretsManager) findByName(ctx context.Context, ref esv1beta1.External
continue
}
log.V(1).Info("aws sm findByName matches", "name", *secret.Name)
- err = sm.fetchAndSet(ctx, data, *secret.Name)
- if err != nil {
+ if err := sm.fetchAndSet(ctx, data, *secret.Name); err != nil {
return nil, err
}
}
@@ -387,31 +366,7 @@ func (sm *SecretsManager) findByTags(ctx context.Context, ref esv1beta1.External
})
}
- data := make(map[string][]byte)
- var nextToken *string
- for {
- log.V(1).Info("aws sm findByTag", "nextToken", nextToken)
- it, err := sm.client.ListSecrets(&awssm.ListSecretsInput{
- Filters: filters,
- NextToken: nextToken,
- })
- metrics.ObserveAPICall(constants.ProviderAWSSM, constants.CallAWSSMListSecrets, err)
- if err != nil {
- return nil, err
- }
- log.V(1).Info("aws sm findByTag found", "secrets", len(it.SecretList))
- for _, secret := range it.SecretList {
- err = sm.fetchAndSet(ctx, data, *secret.Name)
- if err != nil {
- return nil, err
- }
- }
- nextToken = it.NextToken
- if nextToken == nil {
- break
- }
- }
- return data, nil
+ return sm.fetchWithBatch(ctx, filters, nil)
}
func (sm *SecretsManager) fetchAndSet(ctx context.Context, data map[string][]byte, name string) error {
@@ -567,9 +522,9 @@ func (sm *SecretsManager) putSecretValueWithContext(ctx context.Context, secretI
return err
}
if !isManagedByESO(data) {
- return fmt.Errorf("secret not managed by external-secrets")
+ return errors.New("secret not managed by external-secrets")
}
- if awsSecret != nil && bytes.Equal(awsSecret.SecretBinary, value) {
+ if awsSecret != nil && bytes.Equal(awsSecret.SecretBinary, value) || utils.CompareStringAndByteSlices(awsSecret.SecretString, value) {
return nil
}
@@ -595,3 +550,91 @@ func (sm *SecretsManager) putSecretValueWithContext(ctx context.Context, secretI
return err
}
+
+func (sm *SecretsManager) fetchWithBatch(ctx context.Context, filters []*awssm.Filter, matcher *find.Matcher) (map[string][]byte, error) {
+ data := make(map[string][]byte)
+ var nextToken *string
+
+ for {
+ it, err := sm.client.BatchGetSecretValueWithContext(ctx, &awssm.BatchGetSecretValueInput{
+ Filters: filters,
+ NextToken: nextToken,
+ })
+ metrics.ObserveAPICall(constants.ProviderAWSSM, constants.CallAWSSMBatchGetSecretValue, err)
+ if err != nil {
+ return nil, err
+ }
+ log.V(1).Info("aws sm findByName found", "secrets", len(it.SecretValues))
+ for _, secret := range it.SecretValues {
+ if matcher != nil && !matcher.MatchName(*secret.Name) {
+ continue
+ }
+ log.V(1).Info("aws sm findByName matches", "name", *secret.Name)
+
+ sm.setSecretValues(secret, data)
+ }
+ nextToken = it.NextToken
+ if nextToken == nil {
+ break
+ }
+ }
+
+ return data, nil
+}
+
+func (sm *SecretsManager) setSecretValues(secret *awssm.SecretValueEntry, data map[string][]byte) {
+ if secret.SecretString != nil {
+ data[*secret.Name] = []byte(*secret.SecretString)
+ }
+ if secret.SecretBinary != nil {
+ data[*secret.Name] = secret.SecretBinary
+ }
+}
+
+func (sm *SecretsManager) constructSecretValue(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef, ver string) (*awssm.GetSecretValueOutput, error) {
+ if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
+ describeSecretInput := &awssm.DescribeSecretInput{
+ SecretId: &ref.Key,
+ }
+
+ descOutput, err := sm.client.DescribeSecretWithContext(ctx, describeSecretInput)
+ if err != nil {
+ return nil, err
+ }
+ log.Info("found metadata secret", "key", ref.Key, "output", descOutput)
+
+ jsonTags, err := util.SecretTagsToJSONString(descOutput.Tags)
+ if err != nil {
+ return nil, err
+ }
+ return &awssm.GetSecretValueOutput{
+ ARN: descOutput.ARN,
+ CreatedDate: descOutput.CreatedDate,
+ Name: descOutput.Name,
+ SecretString: &jsonTags,
+ VersionId: &ver,
+ }, nil
+ }
+
+ var getSecretValueInput *awssm.GetSecretValueInput
+ if strings.HasPrefix(ver, "uuid/") {
+ versionID := strings.TrimPrefix(ver, "uuid/")
+ getSecretValueInput = &awssm.GetSecretValueInput{
+ SecretId: &ref.Key,
+ VersionId: &versionID,
+ }
+ } else {
+ getSecretValueInput = &awssm.GetSecretValueInput{
+ SecretId: &ref.Key,
+ VersionStage: &ver,
+ }
+ }
+ secretOut, err := sm.client.GetSecretValue(getSecretValueInput)
+ metrics.ObserveAPICall(constants.ProviderAWSSM, constants.CallAWSSMGetSecretValue, err)
+ var nf *awssm.ResourceNotFoundException
+ if errors.As(err, &nf) {
+ return nil, esv1beta1.NoSecretErr
+ }
+
+ return secretOut, err
+}
diff --git a/pkg/provider/aws/secretsmanager/secretsmanager_test.go b/pkg/provider/aws/secretsmanager/secretsmanager_test.go
index 51c2880b471..115a7dfd318 100644
--- a/pkg/provider/aws/secretsmanager/secretsmanager_test.go
+++ b/pkg/provider/aws/secretsmanager/secretsmanager_test.go
@@ -62,6 +62,7 @@ const (
tagvalue1 = "tagvalue1"
tagname2 = "tagname2"
tagvalue2 = "tagvalue2"
+ fakeKey = "fake-key"
)
func makeValidSecretsManagerTestCase() *secretsManagerTestCase {
@@ -111,7 +112,7 @@ func makeValidSecretsManagerTestCaseCustom(tweaks ...func(smtc *secretsManagerTe
// This case can be shared by both GetSecret and GetSecretMap tests.
// bad case: set apiErr.
var setAPIErr = func(smtc *secretsManagerTestCase) {
- smtc.apiErr = fmt.Errorf("oh no")
+ smtc.apiErr = errors.New("oh no")
smtc.expectError = "oh no"
}
@@ -464,11 +465,11 @@ func TestSetSecret(t *testing.T) {
ARN: &arn,
}
- pushSecretDataWithoutProperty := fake.PushSecretData{SecretKey: secretKey, RemoteKey: "fake-key", Property: ""}
- pushSecretDataWithMetadata := fake.PushSecretData{SecretKey: secretKey, RemoteKey: "fake-key", Property: "", Metadata: &apiextensionsv1.JSON{
+ pushSecretDataWithoutProperty := fake.PushSecretData{SecretKey: secretKey, RemoteKey: fakeKey, Property: ""}
+ pushSecretDataWithMetadata := fake.PushSecretData{SecretKey: secretKey, RemoteKey: fakeKey, Property: "", Metadata: &apiextensionsv1.JSON{
Raw: []byte(`{"secretPushFormat": "string"}`),
}}
- pushSecretDataWithProperty := fake.PushSecretData{SecretKey: secretKey, RemoteKey: "fake-key", Property: "other-fake-property"}
+ pushSecretDataWithProperty := fake.PushSecretData{SecretKey: secretKey, RemoteKey: fakeKey, Property: "other-fake-property"}
type args struct {
store *esv1beta1.AWSProvider
@@ -655,7 +656,7 @@ func TestSetSecret(t *testing.T) {
Version: &defaultUpdatedVersion,
}),
},
- pushSecretData: fake.PushSecretData{SecretKey: secretKey, RemoteKey: "fake-key", Property: "fake-property.other-fake-property"},
+ pushSecretData: fake.PushSecretData{SecretKey: secretKey, RemoteKey: fakeKey, Property: "fake-property.other-fake-property"},
},
want: want{
err: nil,
@@ -769,7 +770,7 @@ func TestSetSecret(t *testing.T) {
pushSecretData: pushSecretDataWithoutProperty,
},
want: want{
- err: fmt.Errorf("secret not managed by external-secrets"),
+ err: errors.New("secret not managed by external-secrets"),
},
},
}
@@ -992,7 +993,7 @@ func TestDeleteSecret(t *testing.T) {
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
- ref := fake.PushSecretData{RemoteKey: "fake-key"}
+ ref := fake.PushSecretData{RemoteKey: fakeKey}
sm := SecretsManager{
client: &tc.args.client,
config: &tc.args.config,
@@ -1064,17 +1065,15 @@ func TestSecretsManagerGetAllSecrets(t *testing.T) {
}
// Test cases
testCases := []struct {
- name string
- ref esv1beta1.ExternalSecretFind
-
- secretName string
- secretVersion string
- secretValue string
- fetchError error
- listSecretsFn func(ctx context.Context, input *awssm.ListSecretsInput, opts ...request.Option) (*awssm.ListSecretsOutput, error)
-
- expectedData map[string][]byte
- expectedError string
+ name string
+ ref esv1beta1.ExternalSecretFind
+ secretName string
+ secretVersion string
+ secretValue string
+ batchGetSecretValueWithContextFn func(aws.Context, *awssm.BatchGetSecretValueInput, ...request.Option) (*awssm.BatchGetSecretValueOutput, error)
+ listSecretsFn func(ctx context.Context, input *awssm.ListSecretsInput, opts ...request.Option) (*awssm.ListSecretsOutput, error)
+ expectedData map[string][]byte
+ expectedError string
}{
{
name: "Matching secrets found",
@@ -1087,14 +1086,16 @@ func TestSecretsManagerGetAllSecrets(t *testing.T) {
secretName: secretName,
secretVersion: secretVersion,
secretValue: secretValue,
- listSecretsFn: func(ctx context.Context, input *awssm.ListSecretsInput, opts ...request.Option) (*awssm.ListSecretsOutput, error) {
+ batchGetSecretValueWithContextFn: func(_ aws.Context, input *awssm.BatchGetSecretValueInput, _ ...request.Option) (*awssm.BatchGetSecretValueOutput, error) {
assert.Len(t, input.Filters, 1)
assert.Equal(t, "name", *input.Filters[0].Key)
assert.Equal(t, secretPath, *input.Filters[0].Values[0])
- return &awssm.ListSecretsOutput{
- SecretList: []*awssm.SecretListEntry{
+ return &awssm.BatchGetSecretValueOutput{
+ SecretValues: []*awssm.SecretValueEntry{
{
- Name: ptr.To(secretName),
+ Name: ptr.To(secretName),
+ VersionStages: []*string{ptr.To(secretVersion)},
+ SecretBinary: []byte(secretValue),
},
},
}, nil
@@ -1115,15 +1116,14 @@ func TestSecretsManagerGetAllSecrets(t *testing.T) {
secretName: secretName,
secretVersion: secretVersion,
secretValue: secretValue,
- fetchError: errBoom,
- listSecretsFn: func(ctx context.Context, input *awssm.ListSecretsInput, opts ...request.Option) (*awssm.ListSecretsOutput, error) {
- return &awssm.ListSecretsOutput{
- SecretList: []*awssm.SecretListEntry{
+ batchGetSecretValueWithContextFn: func(aws.Context, *awssm.BatchGetSecretValueInput, ...request.Option) (*awssm.BatchGetSecretValueOutput, error) {
+ return &awssm.BatchGetSecretValueOutput{
+ SecretValues: []*awssm.SecretValueEntry{
{
Name: ptr.To(secretName),
},
},
- }, nil
+ }, errBoom
},
expectedData: nil,
expectedError: errBoom.Error(),
@@ -1157,6 +1157,15 @@ func TestSecretsManagerGetAllSecrets(t *testing.T) {
},
}, nil
},
+ batchGetSecretValueWithContextFn: func(aws.Context, *awssm.BatchGetSecretValueInput, ...request.Option) (*awssm.BatchGetSecretValueOutput, error) {
+ return &awssm.BatchGetSecretValueOutput{
+ SecretValues: []*awssm.SecretValueEntry{
+ {
+ Name: ptr.To("other-secret"),
+ },
+ },
+ }, nil
+ },
expectedData: make(map[string][]byte),
expectedError: "",
},
@@ -1179,16 +1188,18 @@ func TestSecretsManagerGetAllSecrets(t *testing.T) {
secretName: secretName,
secretVersion: secretVersion,
secretValue: secretValue,
- listSecretsFn: func(ctx context.Context, input *awssm.ListSecretsInput, opts ...request.Option) (*awssm.ListSecretsOutput, error) {
+ batchGetSecretValueWithContextFn: func(_ aws.Context, input *awssm.BatchGetSecretValueInput, _ ...request.Option) (*awssm.BatchGetSecretValueOutput, error) {
assert.Len(t, input.Filters, 2)
assert.Equal(t, "tag-key", *input.Filters[0].Key)
assert.Equal(t, "foo", *input.Filters[0].Values[0])
assert.Equal(t, "tag-value", *input.Filters[1].Key)
assert.Equal(t, "bar", *input.Filters[1].Values[0])
- return &awssm.ListSecretsOutput{
- SecretList: []*awssm.SecretListEntry{
+ return &awssm.BatchGetSecretValueOutput{
+ SecretValues: []*awssm.SecretValueEntry{
{
- Name: ptr.To(secretName),
+ Name: ptr.To(secretName),
+ VersionStages: []*string{ptr.To(secretVersion)},
+ SecretBinary: []byte(secretValue),
},
},
}, nil
@@ -1206,15 +1217,16 @@ func TestSecretsManagerGetAllSecrets(t *testing.T) {
secretName: secretName,
secretVersion: secretVersion,
secretValue: secretValue,
- fetchError: errBoom,
- listSecretsFn: func(ctx context.Context, input *awssm.ListSecretsInput, opts ...request.Option) (*awssm.ListSecretsOutput, error) {
- return &awssm.ListSecretsOutput{
- SecretList: []*awssm.SecretListEntry{
+ batchGetSecretValueWithContextFn: func(aws.Context, *awssm.BatchGetSecretValueInput, ...request.Option) (*awssm.BatchGetSecretValueOutput, error) {
+ return &awssm.BatchGetSecretValueOutput{
+ SecretValues: []*awssm.SecretValueEntry{
{
- Name: ptr.To(secretName),
+ Name: ptr.To(secretName),
+ VersionStages: []*string{ptr.To(secretVersion)},
+ SecretBinary: []byte(secretValue),
},
},
- }, nil
+ }, errBoom
},
expectedData: nil,
expectedError: errBoom.Error(),
@@ -1224,7 +1236,7 @@ func TestSecretsManagerGetAllSecrets(t *testing.T) {
ref: esv1beta1.ExternalSecretFind{
Tags: secretTags,
},
- listSecretsFn: func(ctx context.Context, input *awssm.ListSecretsInput, opts ...request.Option) (*awssm.ListSecretsOutput, error) {
+ batchGetSecretValueWithContextFn: func(aws.Context, *awssm.BatchGetSecretValueInput, ...request.Option) (*awssm.BatchGetSecretValueOutput, error) {
return nil, errBoom
},
expectedData: nil,
@@ -1235,15 +1247,8 @@ func TestSecretsManagerGetAllSecrets(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
fc := fakesm.NewClient()
+ fc.BatchGetSecretValueWithContextFn = tc.batchGetSecretValueWithContextFn
fc.ListSecretsFn = tc.listSecretsFn
- fc.WithValue(&awssm.GetSecretValueInput{
- SecretId: ptr.To(tc.secretName),
- VersionStage: ptr.To(tc.secretVersion),
- }, &awssm.GetSecretValueOutput{
- Name: ptr.To(tc.secretName),
- VersionStages: []*string{ptr.To(tc.secretVersion)},
- SecretBinary: []byte(tc.secretValue),
- }, tc.fetchError)
sm := SecretsManager{
client: fc,
cache: make(map[string]*awssm.GetSecretValueOutput),
@@ -1316,6 +1321,94 @@ func TestSecretsManagerValidate(t *testing.T) {
})
}
}
+func TestSecretExists(t *testing.T) {
+ arn := "arn:aws:secretsmanager:us-east-1:702902267788:secret:foo-bar5-Robbgh"
+ defaultVersion := "00000000-0000-0000-0000-000000000002"
+ secretValueOutput := &awssm.GetSecretValueOutput{
+ ARN: &arn,
+ VersionId: &defaultVersion,
+ }
+
+ blankSecretValueOutput := &awssm.GetSecretValueOutput{}
+
+ getSecretCorrectErr := awssm.ResourceNotFoundException{}
+ getSecretWrongErr := awssm.InvalidRequestException{}
+
+ pushSecretDataWithoutProperty := fake.PushSecretData{SecretKey: "fake-secret-key", RemoteKey: fakeKey, Property: ""}
+
+ type args struct {
+ store *esv1beta1.AWSProvider
+ client fakesm.Client
+ pushSecretData fake.PushSecretData
+ }
+
+ type want struct {
+ err error
+ wantError bool
+ }
+
+ tests := map[string]struct {
+ args args
+ want want
+ }{
+ "SecretExistsReturnsTrueForExistingSecret": {
+ args: args{
+ store: makeValidSecretStore().Spec.Provider.AWS,
+ client: fakesm.Client{
+ GetSecretValueWithContextFn: fakesm.NewGetSecretValueWithContextFn(secretValueOutput, nil),
+ },
+ pushSecretData: pushSecretDataWithoutProperty,
+ },
+ want: want{
+ err: nil,
+ wantError: true,
+ },
+ },
+ "SecretExistsReturnsFalseForNonExistingSecret": {
+ args: args{
+ store: makeValidSecretStore().Spec.Provider.AWS,
+ client: fakesm.Client{
+ GetSecretValueWithContextFn: fakesm.NewGetSecretValueWithContextFn(blankSecretValueOutput, &getSecretCorrectErr),
+ },
+ pushSecretData: pushSecretDataWithoutProperty,
+ },
+ want: want{
+ err: nil,
+ wantError: false,
+ },
+ },
+ "SecretExistsReturnsFalseForErroredSecret": {
+ args: args{
+ store: makeValidSecretStore().Spec.Provider.AWS,
+ client: fakesm.Client{
+ GetSecretValueWithContextFn: fakesm.NewGetSecretValueWithContextFn(blankSecretValueOutput, &getSecretWrongErr),
+ },
+ pushSecretData: pushSecretDataWithoutProperty,
+ },
+ want: want{
+ err: &getSecretWrongErr,
+ wantError: false,
+ },
+ },
+ }
+
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ sm := &SecretsManager{
+ client: &tc.args.client,
+ }
+ got, err := sm.SecretExists(context.Background(), tc.args.pushSecretData)
+
+ assert.Equal(
+ t,
+ tc.want,
+ want{
+ err: err,
+ wantError: got,
+ })
+ })
+ }
+}
// FakeCredProvider implements the AWS credentials.Provider interface
// It is used to inject an error into the AWS session to cause a
diff --git a/pkg/provider/aws/util/provider.go b/pkg/provider/aws/util/provider.go
index 8dcba3ef54e..1ddc1d82756 100644
--- a/pkg/provider/aws/util/provider.go
+++ b/pkg/provider/aws/util/provider.go
@@ -16,6 +16,7 @@ package util
import (
"encoding/json"
+ "errors"
"fmt"
awssm "github.com/aws/aws-sdk-go/service/secretsmanager"
@@ -35,14 +36,14 @@ const (
// it returns the aws provider or an error.
func GetAWSProvider(store esv1beta1.GenericStore) (*esv1beta1.AWSProvider, error) {
if store == nil {
- return nil, fmt.Errorf(errNilStore)
+ return nil, errors.New(errNilStore)
}
spc := store.GetSpec()
if spc == nil {
- return nil, fmt.Errorf(errMissingStoreSpec)
+ return nil, errors.New(errMissingStoreSpec)
}
if spc.Provider == nil {
- return nil, fmt.Errorf(errMissingProvider)
+ return nil, errors.New(errMissingProvider)
}
prov := spc.Provider.AWS
if prov == nil {
diff --git a/pkg/provider/azure/keyvault/fake/fake.go b/pkg/provider/azure/keyvault/fake/fake.go
index c88bb323f6e..f588f449ae3 100644
--- a/pkg/provider/azure/keyvault/fake/fake.go
+++ b/pkg/provider/azure/keyvault/fake/fake.go
@@ -17,7 +17,7 @@ package fake
import (
"context"
- "github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault"
+ "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.0/keyvault"
)
type AzureMockClient struct {
diff --git a/pkg/provider/azure/keyvault/keyvault.go b/pkg/provider/azure/keyvault/keyvault.go
index 1f71f058ca6..cd2ccc32f2a 100644
--- a/pkg/provider/azure/keyvault/keyvault.go
+++ b/pkg/provider/azure/keyvault/keyvault.go
@@ -26,16 +26,17 @@ import (
"path"
"regexp"
"strings"
+ "time"
- "github.com/Azure/azure-sdk-for-go/profiles/latest/keyvault/keyvault"
+ "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.0/keyvault"
"github.com/Azure/go-autorest/autorest"
"github.com/Azure/go-autorest/autorest/adal"
"github.com/Azure/go-autorest/autorest/azure"
kvauth "github.com/Azure/go-autorest/autorest/azure/auth"
+ "github.com/Azure/go-autorest/autorest/date"
"github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/tidwall/gjson"
- "golang.org/x/crypto/pkcs12"
"golang.org/x/crypto/sha3"
authv1 "k8s.io/api/authentication/v1"
corev1 "k8s.io/api/core/v1"
@@ -47,11 +48,13 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
ctrlcfg "sigs.k8s.io/controller-runtime/pkg/client/config"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+ gopkcs12 "software.sslmate.com/src/go-pkcs12"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
"github.com/external-secrets/external-secrets/pkg/constants"
"github.com/external-secrets/external-secrets/pkg/metrics"
"github.com/external-secrets/external-secrets/pkg/utils"
+ "github.com/external-secrets/external-secrets/pkg/utils/metadata"
"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
)
@@ -63,23 +66,23 @@ const (
AnnotationClientID = "azure.workload.identity/client-id"
AnnotationTenantID = "azure.workload.identity/tenant-id"
managerLabel = "external-secrets"
-
- errUnexpectedStoreSpec = "unexpected store spec"
- errMissingAuthType = "cannot initialize Azure Client: no valid authType was specified"
- errPropNotExist = "property %s does not exist in key %s"
- errTagNotExist = "tag %s does not exist"
- errUnknownObjectType = "unknown Azure Keyvault object Type for %s"
- errUnmarshalJSONData = "error unmarshalling json data: %w"
- errDataFromCert = "cannot get use dataFrom to get certificate secret"
- errDataFromKey = "cannot get use dataFrom to get key secret"
- errMissingTenant = "missing tenantID in store config"
- errMissingClient = "missing clientID: either serviceAccountRef or service account annotation '%s' is missing"
- errMissingSecretRef = "missing secretRef in provider config"
- errMissingClientIDSecret = "missing accessKeyID/secretAccessKey in store config"
- errMultipleClientID = "multiple clientID found. Check secretRef and serviceAccountRef"
- errMultipleTenantID = "multiple tenantID found. Check secretRef, 'spec.provider.azurekv.tenantId', and serviceAccountRef"
- errFindSecret = "could not find secret %s/%s: %w"
- errFindDataKey = "no data for %q in secret '%s/%s'"
+ managedBy = "managed-by"
+
+ errUnexpectedStoreSpec = "unexpected store spec"
+ errMissingAuthType = "cannot initialize Azure Client: no valid authType was specified"
+ errPropNotExist = "property %s does not exist in key %s"
+ errTagNotExist = "tag %s does not exist"
+ errUnknownObjectType = "unknown Azure Keyvault object Type for %s"
+ errUnmarshalJSONData = "error unmarshalling json data: %w"
+ errDataFromCert = "cannot get use dataFrom to get certificate secret"
+ errDataFromKey = "cannot get use dataFrom to get key secret"
+ errMissingTenant = "missing tenantID in store config"
+ errMissingClient = "missing clientID: either serviceAccountRef or service account annotation '%s' is missing"
+ errMissingSecretRef = "missing secretRef in provider config"
+ errMissingClientIDSecret = "missing accessKeyID/secretAccessKey in store config"
+ errInvalidClientCredentials = "both clientSecret and clientCredentials set"
+ errMultipleClientID = "multiple clientID found. Check secretRef and serviceAccountRef"
+ errMultipleTenantID = "multiple tenantID found. Check secretRef, 'spec.provider.azurekv.tenantId', and serviceAccountRef"
errInvalidStore = "invalid store"
errInvalidStoreSpec = "invalid store spec"
@@ -91,7 +94,6 @@ const (
errMissingWorkloadEnvVars = "missing environment variables. AZURE_CLIENT_ID, AZURE_TENANT_ID and AZURE_FEDERATED_TOKEN_FILE must be set"
errReadTokenFile = "unable to read token file %s: %w"
- errMissingSAAnnotation = "missing service account annotation: %s"
)
// https://github.com/external-secrets/external-secrets/issues/644
@@ -121,6 +123,10 @@ type Azure struct {
namespace string
}
+type PushSecretMetadataSpec struct {
+ ExpirationDate string `json:"expirationDate,omitempty"`
+}
+
func init() {
esv1beta1.Register(&Azure{}, &esv1beta1.SecretStoreProvider{
AzureKV: &esv1beta1.AzureKVProvider{},
@@ -175,7 +181,7 @@ func newClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Cl
case esv1beta1.AzureWorkloadIdentity:
authorizer, err = az.authorizerForWorkloadIdentity(ctx, NewTokenProvider)
default:
- err = fmt.Errorf(errMissingAuthType)
+ err = errors.New(errMissingAuthType)
}
cl := keyvault.New()
@@ -196,18 +202,18 @@ func getProvider(store esv1beta1.GenericStore) (*esv1beta1.AzureKVProvider, erro
func (a *Azure) ValidateStore(store esv1beta1.GenericStore) (admission.Warnings, error) {
if store == nil {
- return nil, fmt.Errorf(errInvalidStore)
+ return nil, errors.New(errInvalidStore)
}
spc := store.GetSpec()
if spc == nil {
- return nil, fmt.Errorf(errInvalidStoreSpec)
+ return nil, errors.New(errInvalidStoreSpec)
}
if spc.Provider == nil {
- return nil, fmt.Errorf(errInvalidStoreProv)
+ return nil, errors.New(errInvalidStoreProv)
}
p := spc.Provider.AzureKV
if p == nil {
- return nil, fmt.Errorf(errInvalidAzureProv)
+ return nil, errors.New(errInvalidAzureProv)
}
if p.AuthSecretRef != nil {
if p.AuthSecretRef.ClientID != nil {
@@ -241,9 +247,9 @@ func canDelete(tags map[string]*string, err error) (bool, error) {
if aerr.StatusCode == 404 {
return false, nil
}
- manager, ok := tags["managed-by"]
+ manager, ok := tags[managedBy]
if !ok || manager == nil || *manager != managerLabel {
- return false, fmt.Errorf("not managed by external-secrets")
+ return false, errors.New("not managed by external-secrets")
}
return true, nil
}
@@ -343,18 +349,24 @@ func (a *Azure) SecretExists(ctx context.Context, remoteRef esv1beta1.PushSecret
func getCertificateFromValue(value []byte) (*x509.Certificate, error) {
// 1st: try decode pkcs12
- _, localCert, err := pkcs12.Decode(value, "")
+ _, localCert, err := gopkcs12.Decode(value, "")
+ if err == nil {
+ return localCert, nil
+ }
+
+ // 2nd: try decode pkcs12 with chain
+ _, localCert, _, err = gopkcs12.DecodeChain(value, "")
if err == nil {
return localCert, nil
}
- // 2nd: try DER
+ // 3rd: try DER
localCert, err = x509.ParseCertificate(value)
if err == nil {
return localCert, nil
}
- // 3nd: parse PEM blocks
+ // 4th: parse PEM blocks
for {
block, rest := pem.Decode(value)
value = rest
@@ -366,7 +378,7 @@ func getCertificateFromValue(value []byte) (*x509.Certificate, error) {
return cert, nil
}
}
- return nil, fmt.Errorf("could not parse certificate value as PKCS#12, DER or PEM")
+ return nil, errors.New("could not parse certificate value as PKCS#12, DER or PEM")
}
func getKeyFromValue(value []byte) (any, error) {
@@ -399,15 +411,15 @@ func canCreate(tags map[string]*string, err error) (bool, error) {
return false, fmt.Errorf("unexpected api error: %w", err)
}
if err == nil {
- manager, ok := tags["managed-by"]
+ manager, ok := tags[managedBy]
if !ok || manager == nil || *manager != managerLabel {
- return false, fmt.Errorf("not managed by external-secrets")
+ return false, errors.New("not managed by external-secrets")
}
}
return true, nil
}
-func (a *Azure) setKeyVaultSecret(ctx context.Context, secretName string, value []byte) error {
+func (a *Azure) setKeyVaultSecret(ctx context.Context, secretName string, value []byte, expires *date.UnixTime) error {
secret, err := a.baseClient.GetSecret(ctx, *a.provider.VaultURL, secretName, "")
metrics.ObserveAPICall(constants.ProviderAzureKV, constants.CallAzureKVGetSecret, err)
ok, err := canCreate(secret.Tags, err)
@@ -419,17 +431,28 @@ func (a *Azure) setKeyVaultSecret(ctx context.Context, secretName string, value
}
val := string(value)
if secret.Value != nil && val == *secret.Value {
- return nil
+ if secret.Attributes != nil {
+ if (secret.Attributes.Expires == nil && expires == nil) ||
+ (secret.Attributes.Expires != nil && expires != nil && *secret.Attributes.Expires == *expires) {
+ return nil
+ }
+ }
}
+
secretParams := keyvault.SecretSetParameters{
Value: &val,
Tags: map[string]*string{
- "managed-by": pointer.To(managerLabel),
+ managedBy: pointer.To(managerLabel),
},
SecretAttributes: &keyvault.SecretAttributes{
Enabled: pointer.To(true),
},
}
+
+ if expires != nil {
+ secretParams.SecretAttributes.Expires = expires
+ }
+
_, err = a.baseClient.SetSecret(ctx, *a.provider.VaultURL, secretName, secretParams)
metrics.ObserveAPICall(constants.ProviderAzureKV, constants.CallAzureKVGetSecret, err)
if err != nil {
@@ -460,7 +483,7 @@ func (a *Azure) setKeyVaultCertificate(ctx context.Context, secretName string, v
params := keyvault.CertificateImportParameters{
Base64EncodedCertificate: &val,
Tags: map[string]*string{
- "managed-by": pointer.To(managerLabel),
+ managedBy: pointer.To(managerLabel),
},
}
_, err = a.baseClient.ImportCertificate(ctx, *a.provider.VaultURL, secretName, params)
@@ -516,7 +539,7 @@ func (a *Azure) setKeyVaultKey(ctx context.Context, secretName string, value []b
Key: &azkey,
KeyAttributes: &keyvault.KeyAttributes{},
Tags: map[string]*string{
- "managed-by": pointer.To(managerLabel),
+ managedBy: pointer.To(managerLabel),
},
}
_, err = a.baseClient.ImportKey(ctx, *a.provider.VaultURL, secretName, params)
@@ -529,15 +552,43 @@ func (a *Azure) setKeyVaultKey(ctx context.Context, secretName string, value []b
// PushSecret stores secrets into a Key vault instance.
func (a *Azure) PushSecret(ctx context.Context, secret *corev1.Secret, data esv1beta1.PushSecretData) error {
+ var (
+ value []byte
+ err error
+ expires *date.UnixTime
+ )
if data.GetSecretKey() == "" {
- return fmt.Errorf("pushing the whole secret is not yet implemented")
+ // Must convert secret values to string, otherwise data will be sent as base64 to Vault
+ secretStringVal := make(map[string]string)
+ for k, v := range secret.Data {
+ secretStringVal[k] = string(v)
+ }
+ value, err = utils.JSONMarshal(secretStringVal)
+ if err != nil {
+ return fmt.Errorf("failed to serialize secret content as JSON: %w", err)
+ }
+ } else {
+ value = secret.Data[data.GetSecretKey()]
+ }
+
+ metadata, err := metadata.ParseMetadataParameters[PushSecretMetadataSpec](data.GetMetadata())
+ if err != nil {
+ return fmt.Errorf("failed to parse push secret metadata: %w", err)
+ }
+
+ if metadata != nil && metadata.Spec.ExpirationDate != "" {
+ t, err := time.Parse(time.RFC3339, metadata.Spec.ExpirationDate)
+ if err != nil {
+ return fmt.Errorf("error parsing expiration date in metadata: %w. Expected format: YYYY-MM-DDTHH:MM:SSZ (RFC3339). Example: 2024-12-31T20:00:00Z", err)
+ }
+ unixTime := date.UnixTime(t)
+ expires = &unixTime
}
objectType, secretName := getObjType(esv1beta1.ExternalSecretDataRemoteRef{Key: data.GetRemoteKey()})
- value := secret.Data[data.GetSecretKey()]
switch objectType {
case defaultObjType:
- return a.setKeyVaultSecret(ctx, secretName, value)
+ return a.setKeyVaultSecret(ctx, secretName, value, expires)
case objectTypeCert:
return a.setKeyVaultCertificate(ctx, secretName, value)
case objectTypeKey:
@@ -648,7 +699,7 @@ func parseError(err error) error {
return err
}
-// Implements store.Client.GetSecret Interface.
+// GetSecret implements store.Client.GetSecret Interface.
// Retrieves a secret/Key/Certificate/Tag with the secret name defined in ref.Name
// The Object Type is defined as a prefix in the ref.Name , if no prefix is defined , we assume a secret is required.
func (a *Azure) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
@@ -749,9 +800,9 @@ func (a *Azure) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDa
return getSecretMapMap(data)
case objectTypeCert:
- return nil, fmt.Errorf(errDataFromCert)
+ return nil, errors.New(errDataFromCert)
case objectTypeKey:
- return nil, fmt.Errorf(errDataFromKey)
+ return nil, errors.New(errDataFromKey)
}
return nil, fmt.Errorf(errUnknownObjectType, secretName)
}
@@ -834,7 +885,7 @@ func (a *Azure) authorizerForWorkloadIdentity(ctx context.Context, tokenProvider
// First check if AuthSecretRef is set and clientID can be fetched from there
if a.provider.AuthSecretRef != nil {
if a.provider.AuthSecretRef.ClientID == nil {
- return nil, fmt.Errorf(errMissingClientIDSecret)
+ return nil, errors.New(errMissingClientIDSecret)
}
clientID, err = resolvers.SecretKeyRef(
ctx,
@@ -851,7 +902,7 @@ func (a *Azure) authorizerForWorkloadIdentity(ctx context.Context, tokenProvider
if val, found := sa.ObjectMeta.Annotations[AnnotationClientID]; found {
// If clientID is defined in both Annotations and AuthSecretRef, return an error
if clientID != "" {
- return nil, fmt.Errorf(errMultipleClientID)
+ return nil, errors.New(errMultipleClientID)
}
clientID = val
}
@@ -877,7 +928,7 @@ func (a *Azure) authorizerForWorkloadIdentity(ctx context.Context, tokenProvider
}
}
}
- // Check if spec.provider.azurekv.tenantId is set
+ // Check if spec.provider.azurekv.tenantID is set
if tenantID == "" && a.provider.TenantID != nil {
tenantID = *a.provider.TenantID
}
@@ -886,7 +937,7 @@ func (a *Azure) authorizerForWorkloadIdentity(ctx context.Context, tokenProvider
if val, found := sa.ObjectMeta.Annotations[AnnotationTenantID]; found {
// If tenantID is defined in both Annotations and AuthSecretRef, return an error
if tenantID != "" {
- return nil, fmt.Errorf(errMultipleTenantID)
+ return nil, errors.New(errMultipleTenantID)
}
tenantID = val
}
@@ -939,7 +990,7 @@ func NewTokenProvider(ctx context.Context, token, clientID, tenantID, aadEndpoin
cred := confidential.NewCredFromAssertionCallback(func(ctx context.Context, aro confidential.AssertionRequestOptions) (string, error) {
return token, nil
})
- cClient, err := confidential.New(fmt.Sprintf("%s%s/oauth2/token", aadEndpoint, tenantID), clientID, cred)
+ cClient, err := confidential.New(fmt.Sprintf("%s%s", aadEndpoint, tenantID), clientID, cred)
if err != nil {
return nil, err
}
@@ -974,36 +1025,86 @@ func (a *Azure) authorizerForManagedIdentity() (autorest.Authorizer, error) {
func (a *Azure) authorizerForServicePrincipal(ctx context.Context) (autorest.Authorizer, error) {
if a.provider.TenantID == nil {
- return nil, fmt.Errorf(errMissingTenant)
+ return nil, errors.New(errMissingTenant)
}
if a.provider.AuthSecretRef == nil {
- return nil, fmt.Errorf(errMissingSecretRef)
+ return nil, errors.New(errMissingSecretRef)
}
- if a.provider.AuthSecretRef.ClientID == nil || a.provider.AuthSecretRef.ClientSecret == nil {
- return nil, fmt.Errorf(errMissingClientIDSecret)
+ if a.provider.AuthSecretRef.ClientID == nil || (a.provider.AuthSecretRef.ClientSecret == nil && a.provider.AuthSecretRef.ClientCertificate == nil) {
+ return nil, errors.New(errMissingClientIDSecret)
}
+ if a.provider.AuthSecretRef.ClientSecret != nil && a.provider.AuthSecretRef.ClientCertificate != nil {
+ return nil, errors.New(errInvalidClientCredentials)
+ }
+
+ return a.getAuthorizerFromCredentials(ctx)
+}
+
+func (a *Azure) getAuthorizerFromCredentials(ctx context.Context) (autorest.Authorizer, error) {
clientID, err := resolvers.SecretKeyRef(
ctx,
a.crClient,
a.store.GetKind(),
- a.namespace, a.provider.AuthSecretRef.ClientID)
+ a.namespace, a.provider.AuthSecretRef.ClientID,
+ )
+
if err != nil {
return nil, err
}
- clientSecret, err := resolvers.SecretKeyRef(
- ctx,
- a.crClient,
- a.store.GetKind(),
- a.namespace, a.provider.AuthSecretRef.ClientSecret)
- if err != nil {
- return nil, err
+
+ if a.provider.AuthSecretRef.ClientSecret != nil {
+ clientSecret, err := resolvers.SecretKeyRef(
+ ctx,
+ a.crClient,
+ a.store.GetKind(),
+ a.namespace, a.provider.AuthSecretRef.ClientSecret,
+ )
+
+ if err != nil {
+ return nil, err
+ }
+
+ return getAuthorizerForClientSecret(
+ clientID,
+ clientSecret,
+ *a.provider.TenantID,
+ a.provider.EnvironmentType,
+ )
+ } else {
+ clientCertificate, err := resolvers.SecretKeyRef(
+ ctx,
+ a.crClient,
+ a.store.GetKind(),
+ a.namespace, a.provider.AuthSecretRef.ClientCertificate,
+ )
+
+ if err != nil {
+ return nil, err
+ }
+
+ return getAuthorizerForClientCertificate(
+ clientID,
+ []byte(clientCertificate),
+ *a.provider.TenantID,
+ a.provider.EnvironmentType,
+ )
}
- clientCredentialsConfig := kvauth.NewClientCredentialsConfig(clientID, clientSecret, *a.provider.TenantID)
- clientCredentialsConfig.Resource = kvResourceForProviderConfig(a.provider.EnvironmentType)
- clientCredentialsConfig.AADEndpoint = AadEndpointForType(a.provider.EnvironmentType)
+}
+
+func getAuthorizerForClientSecret(clientID, clientSecret, tenantID string, environmentType esv1beta1.AzureEnvironmentType) (autorest.Authorizer, error) {
+ clientCredentialsConfig := kvauth.NewClientCredentialsConfig(clientID, clientSecret, tenantID)
+ clientCredentialsConfig.Resource = kvResourceForProviderConfig(environmentType)
+ clientCredentialsConfig.AADEndpoint = AadEndpointForType(environmentType)
return clientCredentialsConfig.Authorizer()
}
+func getAuthorizerForClientCertificate(clientID string, certificateBytes []byte, tenantID string, environmentType esv1beta1.AzureEnvironmentType) (autorest.Authorizer, error) {
+ clientCertificateConfig := NewClientInMemoryCertificateConfig(clientID, certificateBytes, tenantID)
+ clientCertificateConfig.Resource = kvResourceForProviderConfig(environmentType)
+ clientCertificateConfig.AADEndpoint = AadEndpointForType(environmentType)
+ return clientCertificateConfig.Authorizer()
+}
+
func (a *Azure) Close(_ context.Context) error {
return nil
}
diff --git a/pkg/provider/azure/keyvault/keyvault_auth_test.go b/pkg/provider/azure/keyvault/keyvault_auth_test.go
index 3952574de98..fb670b5daf0 100644
--- a/pkg/provider/azure/keyvault/keyvault_auth_test.go
+++ b/pkg/provider/azure/keyvault/keyvault_auth_test.go
@@ -37,6 +37,32 @@ import (
var vaultURL = "https://local.vault.url"
+var mockCertificate = `
+-----BEGIN CERTIFICATE-----
+MIICBzCCAbGgAwIBAgIUSoCD1fgywDbmeRaGrkYzGWUd1wMwDQYJKoZIhvcNAQEL
+BQAwcTELMAkGA1UEBhMCQVoxGTAXBgNVBAgMEE1vY2sgQ2VydGlmaWNhdGUxMzAx
+BgNVBAoMKkV4dGVybmFsIFNlY3JldHMgT3BlcmF0b3IgTW9jayBDZXJ0aWZpY2F0
+ZTESMBAGA1UEAwwJTW9jayBDZXJ0MB4XDTI0MDUwODA4NDkzMFoXDTI1MDUwODA4
+NDkzMFowcTELMAkGA1UEBhMCQVoxGTAXBgNVBAgMEE1vY2sgQ2VydGlmaWNhdGUx
+MzAxBgNVBAoMKkV4dGVybmFsIFNlY3JldHMgT3BlcmF0b3IgTW9jayBDZXJ0aWZp
+Y2F0ZTESMBAGA1UEAwwJTW9jayBDZXJ0MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJB
+ALkU1YgMk1Dk149F/HsHA0TjzLwfDa9tT0cfqA1u0hoJkb2r9jdWUyiugGaEz/PU
+TGWrvp8aiXPrGuu5Y6PY27ECAwEAAaMhMB8wHQYDVR0OBBYEFAMB0YwnYjUm00og
+kGce8Yhr4I03MA0GCSqGSIb3DQEBCwUAA0EAr0BMs/3hIOdZc0WHZUCTZ0GGor3G
+ViYUPHOw8z6UZGPGN6qiAejmkT6uP3LkkSW+7TIIQ1pkQxcn5xfFJXBexw==
+-----END CERTIFICATE-----
+-----BEGIN PRIVATE KEY-----
+MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEAuRTViAyTUOTXj0X8
+ewcDROPMvB8Nr21PRx+oDW7SGgmRvav2N1ZTKK6AZoTP89RMZau+nxqJc+sa67lj
+o9jbsQIDAQABAkA35CnDpwCJykGqW5kuUeTT1fMK0FnioyDwuoeWXuQFxmB6Md89
++ABxyjAt3nmwRRVBrVFdNibb9asR5KFHwn1NAiEA4NlrSnJrY1xODIjEXf0fLTwu
+wpyUO1lX585OjYDiOYsCIQDSuP4ttH/1Hg3f9veEE4RgDEk+QcisrzF8q4Oa5sDP
+MwIgfejiTtcR0ZsPza8Mn0EuIyuPV8VMsItQUWtSy6R/ig8CIQC86cBmNUXp+HGz
+8fLg46ZvfVREjjFcLwwMmq83tdvxZQIgPAbezuRCrduH19xgMO8BXndS5DAovgvE
+/MpQnEyQtVA=
+-----END PRIVATE KEY-----
+`
+
func TestNewClientManagedIdentityNoNeedForCredentials(t *testing.T) {
namespace := "internal"
identityID := "1234"
@@ -405,7 +431,7 @@ func TestAuth(t *testing.T) {
},
},
{
- name: "correct cluster secret store",
+ name: "correct cluster secret store with ClientSecret",
objects: []client.Object{&corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: "password",
@@ -432,6 +458,94 @@ func TestAuth(t *testing.T) {
},
},
},
+ {
+ name: "bad config: both clientSecret and clientCredentials are configured",
+ expErr: "both clientSecret and clientCredentials set",
+ objects: []client.Object{&corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "password",
+ Namespace: "foo",
+ },
+ Data: map[string][]byte{
+ "id": []byte("foo"),
+ "certificate": []byte("bar"),
+ "secret": []byte("bar"),
+ },
+ }},
+ store: &esv1beta1.ClusterSecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.ClusterSecretStoreKind,
+ },
+ Spec: esv1beta1.SecretStoreSpec{Provider: &esv1beta1.SecretStoreProvider{}},
+ },
+ provider: &esv1beta1.AzureKVProvider{
+ AuthType: &authType,
+ VaultURL: &vaultURL,
+ TenantID: pointer.To("mytenant"),
+ AuthSecretRef: &esv1beta1.AzureKVAuth{
+ ClientID: &v1.SecretKeySelector{Name: "password", Namespace: pointer.To("foo"), Key: "id"},
+ ClientCertificate: &v1.SecretKeySelector{Name: "password", Namespace: pointer.To("foo"), Key: "certificate"},
+ ClientSecret: &v1.SecretKeySelector{Name: "password", Namespace: pointer.To("foo"), Key: "secret"},
+ },
+ },
+ },
+ {
+ name: "bad config: no valid client certificate in pem file",
+ expErr: "failed to get oauth token from certificate auth: failed to decode certificate: no certificate found in PEM file",
+ objects: []client.Object{&corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "password",
+ Namespace: "foo",
+ },
+ Data: map[string][]byte{
+ "id": []byte("foo"),
+ "certificate": []byte("bar"),
+ },
+ }},
+ store: &esv1beta1.ClusterSecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.ClusterSecretStoreKind,
+ },
+ Spec: esv1beta1.SecretStoreSpec{Provider: &esv1beta1.SecretStoreProvider{}},
+ },
+ provider: &esv1beta1.AzureKVProvider{
+ AuthType: &authType,
+ VaultURL: &vaultURL,
+ TenantID: pointer.To("mytenant"),
+ AuthSecretRef: &esv1beta1.AzureKVAuth{
+ ClientID: &v1.SecretKeySelector{Name: "password", Namespace: pointer.To("foo"), Key: "id"},
+ ClientCertificate: &v1.SecretKeySelector{Name: "password", Namespace: pointer.To("foo"), Key: "certificate"},
+ },
+ },
+ },
+ {
+ name: "correct configuration with certificate authentication",
+ objects: []client.Object{&corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "password",
+ Namespace: "foo",
+ },
+ Data: map[string][]byte{
+ "id": []byte("foo"),
+ "certificate": []byte(mockCertificate),
+ },
+ }},
+ store: &esv1beta1.ClusterSecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.ClusterSecretStoreKind,
+ },
+ Spec: esv1beta1.SecretStoreSpec{Provider: &esv1beta1.SecretStoreProvider{}},
+ },
+ provider: &esv1beta1.AzureKVProvider{
+ AuthType: &authType,
+ VaultURL: &vaultURL,
+ TenantID: pointer.To("mytenant"),
+ AuthSecretRef: &esv1beta1.AzureKVAuth{
+ ClientID: &v1.SecretKeySelector{Name: "password", Namespace: pointer.To("foo"), Key: "id"},
+ ClientCertificate: &v1.SecretKeySelector{Name: "password", Namespace: pointer.To("foo"), Key: "certificate"},
+ },
+ },
+ },
} {
t.Run(row.name, func(t *testing.T) {
k8sClient := clientfake.NewClientBuilder().WithObjects(row.objects...).Build()
diff --git a/pkg/provider/azure/keyvault/keyvault_certificate.go b/pkg/provider/azure/keyvault/keyvault_certificate.go
new file mode 100644
index 00000000000..77788a6be50
--- /dev/null
+++ b/pkg/provider/azure/keyvault/keyvault_certificate.go
@@ -0,0 +1,121 @@
+// /*
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+// */
+package keyvault
+
+import (
+ "crypto/rsa"
+ "crypto/x509"
+ "encoding/pem"
+ "errors"
+ "fmt"
+
+ "github.com/Azure/go-autorest/autorest"
+ "github.com/Azure/go-autorest/autorest/adal"
+ "github.com/Azure/go-autorest/autorest/azure"
+)
+
+// ClientInMemoryCertificateConfig struct includes a Certificate field to hold the certificate data as a byte slice.
+type ClientInMemoryCertificateConfig struct {
+ ClientID string
+ Certificate []byte // Certificate data as a byte slice
+ TenantID string
+ AuxTenants []string
+ AADEndpoint string
+ Resource string
+}
+
+func NewClientInMemoryCertificateConfig(clientID string, certificate []byte, tenantID string) ClientInMemoryCertificateConfig {
+ return ClientInMemoryCertificateConfig{
+ ClientID: clientID,
+ Certificate: certificate,
+ TenantID: tenantID,
+ Resource: azure.PublicCloud.ResourceManagerEndpoint,
+ AADEndpoint: azure.PublicCloud.ActiveDirectoryEndpoint,
+ }
+}
+
+// ServicePrincipalToken creates a adal.ServicePrincipalToken from client certificate using the certificate byte slice.
+func (ccc ClientInMemoryCertificateConfig) ServicePrincipalToken() (*adal.ServicePrincipalToken, error) {
+ oauthConfig, err := adal.NewOAuthConfig(ccc.AADEndpoint, ccc.TenantID)
+ if err != nil {
+ return nil, err
+ }
+ // Use the byte slice directly instead of reading from a file
+ certificate, rsaPrivateKey, err := loadCertificateFromBytes(ccc.Certificate)
+
+ if err != nil {
+ return nil, fmt.Errorf("failed to decode certificate: %w", err)
+ }
+ return adal.NewServicePrincipalTokenFromCertificate(*oauthConfig, ccc.ClientID, certificate, rsaPrivateKey, ccc.Resource)
+}
+
+func loadCertificateFromBytes(certificateBytes []byte) (*x509.Certificate, *rsa.PrivateKey, error) {
+ var cert *x509.Certificate
+ var privateKey *rsa.PrivateKey
+ var err error
+
+ // Extract certificate and private key
+ for {
+ block, rest := pem.Decode(certificateBytes)
+ if block == nil {
+ break
+ }
+ if block.Type == "CERTIFICATE" {
+ cert, err = x509.ParseCertificate(block.Bytes)
+ if err != nil {
+ return nil, nil, fmt.Errorf("failed to parse PEM certificate: %w", err)
+ }
+ } else {
+ privateKey, err = parsePrivateKey(block.Bytes)
+ if err != nil {
+ return nil, nil, fmt.Errorf("failed to extract private key from PEM certificate: %w", err)
+ }
+ }
+ certificateBytes = rest
+ }
+
+ if cert == nil {
+ return nil, nil, errors.New("no certificate found in PEM file")
+ }
+
+ if privateKey == nil {
+ return nil, nil, errors.New("no private key found in PEM file")
+ }
+
+ return cert, privateKey, nil
+}
+
+func parsePrivateKey(der []byte) (*rsa.PrivateKey, error) {
+ if key, err := x509.ParsePKCS1PrivateKey(der); err == nil {
+ return key, nil
+ }
+ if key, err := x509.ParsePKCS8PrivateKey(der); err == nil {
+ switch key := key.(type) {
+ case *rsa.PrivateKey:
+ return key, nil
+ default:
+ return nil, errors.New("found unknown private key type in PKCS#8 wrapping")
+ }
+ }
+ return nil, errors.New("failed to parse private key")
+}
+
+// Implementation of the AuthorizerConfig interface.
+func (ccc ClientInMemoryCertificateConfig) Authorizer() (autorest.Authorizer, error) {
+ spToken, err := ccc.ServicePrincipalToken()
+ if err != nil {
+ return nil, fmt.Errorf("failed to get oauth token from certificate auth: %w", err)
+ }
+ return autorest.NewBearerAuthorizer(spToken), nil
+}
diff --git a/pkg/provider/azure/keyvault/keyvault_test.go b/pkg/provider/azure/keyvault/keyvault_test.go
index 02bb11e31fc..e6cbe08d693 100644
--- a/pkg/provider/azure/keyvault/keyvault_test.go
+++ b/pkg/provider/azure/keyvault/keyvault_test.go
@@ -22,10 +22,14 @@ import (
"fmt"
"reflect"
"testing"
+ "time"
- "github.com/Azure/azure-sdk-for-go/services/keyvault/2016-10-01/keyvault"
+ "github.com/Azure/azure-sdk-for-go/services/keyvault/v7.0/keyvault"
"github.com/Azure/go-autorest/autorest"
+ "github.com/Azure/go-autorest/autorest/date"
+ "gopkg.in/yaml.v2"
corev1 "k8s.io/api/core/v1"
+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
pointer "k8s.io/utils/ptr"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
@@ -33,6 +37,7 @@ import (
"github.com/external-secrets/external-secrets/pkg/provider/azure/keyvault/fake"
testingfake "github.com/external-secrets/external-secrets/pkg/provider/testing/fake"
"github.com/external-secrets/external-secrets/pkg/utils"
+ "github.com/external-secrets/external-secrets/pkg/utils/metadata"
)
type secretManagerTestCase struct {
@@ -61,9 +66,12 @@ type secretManagerTestCase struct {
setValue []byte
expectedSecret string
// for testing secretmap
- expectedData map[string][]byte
-
+ expectedData map[string][]byte
expectedExistence bool
+ // for testing pushing multi-key k8s secrets
+ secret *corev1.Secret
+ // for testing changes in expiration date for akv secrets
+ newExpiry *date.UnixTime
}
func makeValidSecretManagerTestCase() *secretManagerTestCase {
@@ -132,6 +140,9 @@ const (
foo = "foo"
bar = "bar"
errStore = "Azure.ValidateStore() error = %v, wantErr %v"
+ externalSecrets = "external-secrets"
+ notFoundMessage = "Not Found"
+ forbiddenMessage = "Forbidden"
)
func getTagMap() map[string]*string {
@@ -168,7 +179,7 @@ func TestAzureKeyVaultDeleteSecret(t *testing.T) {
}
smtc.secretOutput = keyvault.SecretBundle{
Tags: map[string]*string{
- "managed-by": pointer.To("external-secrets"),
+ managedBy: pointer.To(externalSecrets),
},
Value: pointer.To("foo"),
}
@@ -179,8 +190,8 @@ func TestAzureKeyVaultDeleteSecret(t *testing.T) {
smtc.pushData = testingfake.PushSecretData{
RemoteKey: secretName,
}
- smtc.apiErr = autorest.DetailedError{StatusCode: 404, Method: "GET", Message: "Not Found"}
- smtc.deleteErr = autorest.DetailedError{StatusCode: 404, Method: "DELETE", Message: "Not Found"}
+ smtc.apiErr = autorest.DetailedError{StatusCode: 404, Method: "GET", Message: notFoundMessage}
+ smtc.deleteErr = autorest.DetailedError{StatusCode: 404, Method: "DELETE", Message: notFoundMessage}
}
secretNotManaged := func(smtc *secretManagerTestCase) {
@@ -199,7 +210,7 @@ func TestAzureKeyVaultDeleteSecret(t *testing.T) {
RemoteKey: secretName,
}
smtc.expectError = "boom"
- smtc.apiErr = fmt.Errorf("boom")
+ smtc.apiErr = errors.New("boom")
}
secretNoDeletePermissions := func(smtc *secretManagerTestCase) {
@@ -208,7 +219,7 @@ func TestAzureKeyVaultDeleteSecret(t *testing.T) {
}
smtc.secretOutput = keyvault.SecretBundle{
Tags: map[string]*string{
- "managed-by": pointer.To("external-secrets"),
+ managedBy: pointer.To(externalSecrets),
},
Value: pointer.To("foo"),
}
@@ -230,7 +241,7 @@ func TestAzureKeyVaultDeleteSecret(t *testing.T) {
}
smtc.certOutput = keyvault.CertificateBundle{
Tags: map[string]*string{
- "managed-by": pointer.To("external-secrets"),
+ managedBy: pointer.To(externalSecrets),
},
}
smtc.deleteCertificateOutput = keyvault.DeletedCertificateBundle{}
@@ -240,7 +251,7 @@ func TestAzureKeyVaultDeleteSecret(t *testing.T) {
RemoteKey: certName,
}
smtc.apiErr = autorest.DetailedError{StatusCode: 404, Method: "GET", Message: "Certificate Not Found"}
- smtc.deleteErr = autorest.DetailedError{StatusCode: 404, Method: "DELETE", Message: "Not Found"}
+ smtc.deleteErr = autorest.DetailedError{StatusCode: 404, Method: "DELETE", Message: notFoundMessage}
}
certNotManaged := func(smtc *secretManagerTestCase) {
@@ -257,7 +268,7 @@ func TestAzureKeyVaultDeleteSecret(t *testing.T) {
RemoteKey: certName,
}
smtc.expectError = "crash"
- smtc.apiErr = fmt.Errorf("crash")
+ smtc.apiErr = errors.New("crash")
}
certNoDeletePermissions := func(smtc *secretManagerTestCase) {
@@ -266,7 +277,7 @@ func TestAzureKeyVaultDeleteSecret(t *testing.T) {
}
smtc.certOutput = keyvault.CertificateBundle{
Tags: map[string]*string{
- "managed-by": pointer.To("external-secrets"),
+ managedBy: pointer.To(externalSecrets),
},
}
smtc.expectError = "No certificate delete Permissions"
@@ -287,7 +298,7 @@ func TestAzureKeyVaultDeleteSecret(t *testing.T) {
}
smtc.keyOutput = keyvault.KeyBundle{
Tags: map[string]*string{
- "managed-by": pointer.To("external-secrets"),
+ managedBy: pointer.To(externalSecrets),
},
}
smtc.deleteKeyOutput = keyvault.DeletedKeyBundle{}
@@ -296,8 +307,8 @@ func TestAzureKeyVaultDeleteSecret(t *testing.T) {
smtc.pushData = testingfake.PushSecretData{
RemoteKey: keyName,
}
- smtc.apiErr = autorest.DetailedError{StatusCode: 404, Method: "GET", Message: "Not Found"}
- smtc.deleteErr = autorest.DetailedError{StatusCode: 404, Method: "DELETE", Message: "Not Found"}
+ smtc.apiErr = autorest.DetailedError{StatusCode: 404, Method: "GET", Message: notFoundMessage}
+ smtc.deleteErr = autorest.DetailedError{StatusCode: 404, Method: "DELETE", Message: notFoundMessage}
}
keyNotManaged := func(smtc *secretManagerTestCase) {
@@ -314,7 +325,7 @@ func TestAzureKeyVaultDeleteSecret(t *testing.T) {
RemoteKey: keyName,
}
smtc.expectError = "tls timeout"
- smtc.apiErr = fmt.Errorf("tls timeout")
+ smtc.apiErr = errors.New("tls timeout")
}
keyNoDeletePermissions := func(smtc *secretManagerTestCase) {
@@ -323,7 +334,7 @@ func TestAzureKeyVaultDeleteSecret(t *testing.T) {
}
smtc.keyOutput = keyvault.KeyBundle{
Tags: map[string]*string{
- "managed-by": pointer.To("external-secrets"),
+ managedBy: pointer.To(externalSecrets),
},
}
smtc.expectError = errNoPermission
@@ -377,6 +388,8 @@ func TestAzureKeyVaultDeleteSecret(t *testing.T) {
}
func TestAzureKeyVaultPushSecret(t *testing.T) {
p12Cert, _ := base64.StdEncoding.DecodeString("MIIQaQIBAzCCEC8GCSqGSIb3DQEHAaCCECAEghAcMIIQGDCCBk8GCSqGSIb3DQEHBqCCBkAwggY8AgEAMIIGNQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIoJ3l+zBtWI8CAggAgIIGCBqkhjPsUaowPQrDumYb2OySFN7Jt91IbIeCt1W3Lk99ueJbZ4+xNUiOD+ZDLLJJI/EDtq+0b+TgWHjx92q/IEUj2woQV2rg1W8EW815MmstyD0YRnw7KvoEKBH+CsWiR/JcC/IVoiV1od0dWFfWGSBtWY5xLiaBWUX6xV8zcBVz1fkB+pHOofkStW2up6G2sQos1WwIptAvz6VpS16xLUmZ1whZZvPhqz1GPfexJSavBWEe7YcoxVd/q8LLGQmQCfV7zXwyUX3WnHATkesYPMSTDPuRWXMOrJRjy2zinQP5XweNY2DeZ2bRV6y3v8eQlQNmKBXteNj5H5lJFkOD7BA6xwYlzj3KGB37Qf7kl6R46liT2tlYp/T9eX1ejC0GqICOroPrAy1J5/r9Jlst/39K20omD7M7DGbnqhEWNUeXoXpT6m/UiXLA+0ns5TBZqt4gwC8n8qgjYVvuxvn5tY3gERzkCa6PYzxBfasjM47hHEbsQ1gQORan7OQqTBjbwjeFC4ObMc4u48qxi/cyzMsPgbgE9pQoz2eF5BC6qcJr5mxL/0RWK+Zpn0or9tK4vqf2czLKrWsMcl5sfShSELXY3+jAsUscMbo0LfRgTwsVZGPgOC1cKJlGky734WFj2l9dHVxiRInz6yuWobIT/fmlvPUhjEXNPc0p7vrPvU3/susH+zilbSrp0rY9Y8t70ixGsHPbSHTk8MapukoFnKy2RxcYZQ4cLLMRBo0BA+ugAO7/pa2qGYawzl+U6ydmBftSxTs2gm4SjDnKWoe67r0Q1FHQEWd6rCA40dzAEiCmClCSqzggDKJYnxqub3sqh3Z2Ap9EEZdWBb/Qxryw5h5H3HAOblwudftsyaXsNPf6nDrknANHZyuwkWuh5XYSkKfG8mz6B8+l5A217nYWn0P4i1+WYgnyojJ+m/ZnaNy1+pWXHy1IugoRkfZaVp3NDmwgjK+dnu6rL3/XhJbXlrOk3UEYImX1yMzIWDv/urdWr3bR/cfwM3XwVUy55QUayLIzxRWfWOLuZ8+ZKw8cJ5YGNUa9AgQ3Fs6Lfp7Qn11SdG4adCEJl6DhsugwZokfy6JqBAv0ywbZ94LKvRc1ItM/crfy/5Io1+GsinnF7lsybsZJFGB6tVNWzgZh92dluzUKIRppMG1ZhUmq/4yaJgZsXYDkAxuPWQ2iSpldijmeuBnr/Oct1BpTwM5ogUS3WCHyZajfS/vIGTzz/q8+VnR9W57hvBKulSCS7G06QsFOvr6yOexb9bJJtgsu1sGjqXqyw0SKbFU9AMRunRVezp/r1LwJ+O/8O4ZCB40o3kSJM4tFvj80zVIz8VoWME7JjwAt04v+o9evavxt6p5yaSpH6pzHbvP6cT6YnJqQbYA9J/sDyLt5caq3/OeiJe4tb1pXmJ6dtwFxFygobKnGZjHsL+yRHrIPvNaqztGRzTu5gwEddMZ38nE0IGOhPVnE6WQC1admI/KUUdVOOATD6kJxSwGYGxpsWXX0KOcy9vb3ykeafmHoJU2S64KpxClH8BfOn7Bn4ypab7rNHs76FmqZYmTV9rjHdCgMqI62pB0TKK925q/RQuX+Rn/8J4mMOOjbDQwlndYbljWq0b9tbcTHpZntnmN/KZbydggrKwb0A9PonIGxqoPs+/MrJtCmlgjhjjHE8N3a10apN/NmN/B4TlfBAr47a/2eelTX642kU2DJ2f00mEeDvwY1lkRCjx+80EiY7nUj9cFfPptNdyQbiVDthkS0rXSbyobDgt53g7KU6/UvTdaRWK5Ks9Q5NZ9c44RaHJ/Y7ukWFrsZDCpcQ2v3gn0A9mQPoZGvziMd1Mh7pOJNR2jrpmodGA9j6MMVuYFKu0GbheEhf++UrDOti40GXcPO+o1NAbTClXeIhDEl81cE1rrK+pPvZEB9m/FV7Osp8NmHQDY+z2rPKa5luO6g77/HM9fJrEGBv19ByQcOFuvOQi0RICUp5sIJD+GO3TBGO7WANpUZvB2cezkBbTa/sVAINTXSD007tOo4WfJTBrQbXAbpQ+04B/2yolFvtbYL4rOcMIIJwQYJKoZIhvcNAQcBoIIJsgSCCa4wggmqMIIJpgYLKoZIhvcNAQwKAQKgggluMIIJajAcBgoqhkiG9w0BDAEDMA4ECM7kJUu/1hDPAgIIAASCCUgs+wJaAYsjcSK7oETqGlVmKzCLwkqvstEYmYlJDihNrj0MWHQqmMP/sfdrnqIHVrLnl3vWRN0CBEtzPZGIM5BqYW1puS8mHXowz+8epz6TLRDpiKM2M29+BfAmTkZwlppfuKpu2MoXgd3LLspAQT10pLjoP66OSj+PfUpCbU82+YjjK7PSxog5OrYmuf4Tfohl8bWcFj6mIiaUYiVuF7mRLq3oUY5mE61EjMGp118JKVCG/8sS4MRZ69ulowDZEdrPOCvXzG+gK3bjeMW4aboIaIZ7UxoUy/AYQNdcYjAiUIRWrZx3s7UMa90R7ZvpWRYEEenko95WEUezaing2vVdImMphmjOIpP0Fkm+WTIQHoznE2+ppET1MtIwLyB0PjLptjFtK5orXNqplFWsN6+X5B6ATG0KCwKcsX7fmrkbDpO3B/suVAGk4SdQsV4xrlHhUneUl4hiZ6v2M9MIC+ZMRuGxmuej7znRxV6IRuVVIOqWuwGVVOQpGC4sCOc2Ej0WQeHQCxVK4EWlGL7JE9ux4Ds//40LC2mUihJXiG01ZI/v6eez1GrPeoOeTtHU7+5N7eU4f00S0XSVQGOhUwlp1E9c7DkSPA4lJ7MfTYUFLeP4R+ITpXXbdco65mwH2WFWPbTAKG1rabHj2D5DvHEoBZEsgcD4klhPnZIEBh6gFg67MZB9XNiofSiLzeSKDgfyeTG1MCctUWTa+vy1mrue4rREuRQMC4h0NMyPJ4LlVYutFfEncH2iGmB8t4CVM5CzZ0hXqDxHEgddU02ix/aIzizXqWgpPN0vkHp/Hv+/wyRvjwuiljmE8otRRFMinoIigmLKQKueJQpLWAZAvBjmCZdKTG8sjJAeo0ufOJQdi/EuCmDWR3YkXKi/RX7ub6cnc9hFb+zDGiplLPTyYqOnEPVut8fdA0kmUuAkelLpSbJcv6h3/tS/IJzH2vMCz26J152UaY6Zh0AqD1hl+wA5q5qgDER0jeFY11KypNfEgYxNhr/BcvuNYvN1/1T/wuvEIviMYhJPaSXXbtqpBzIjpkvxOzm9LeC9wqRM1Gq1HrSHwUUeRl8AeMpsRmcmRRy222ZM7p6b0T90l/AKcPLmNxQVYTy5+DeWMC/YaBFHPVMiakKEmPZjeR3Vbb63EJ5DCoAN3xh6NmpANXmXAl7z6ID0hVjNV/Ji8Y+tuJczh0IyMQncDBRw76cdup9QIk2D/pKcj9M7ul2Jx2xwBqntJbvFQqjhIhaSzLKMQtaC+qgcL/C/ANFey8IN5zUUver9RdYyEnRNf4OPl/mq7kUs8znnu5wGGOyxHuvHMFUtJfuII3P7YDSltK2QP1uhefhMfEvNYL9QqosN3740nQ8TCPvZFzzoBC8Psn6OvNXnWipz3WCZ+5u+fOXzawpNKvPHWz/D4O4dmMu9/DpxKb8UOLv/+YFEkqkGNDhS91dgyI672JqC4TQ9ijmNwtdgQt+OtOmllUO3cRP5I4nxCLjAJ5bBYmFV7kSdfWJEjkeCUGMKmwP88sXxeAV0D7qGFG0kdNgMow7WE8AI+lKo8bgBpmR8LQlD0Zt/LBlgGk1uOXolXTNaEGXUMj7h3zS46C3qR/UraHTq+vaNrLqY3qYJaVXdvhhShVDEhH6jLFFYJYCBtWCnhZ3lKkFJnIY+n+25lEQNMwR4sNOLxmUP+kzkt6qSjTRj+u1gK4NptkhFck7lFigAlHozlzg1mnKPvXcD2w3B+Qt6smAQb31rxD6P/byFVEjMFFH1LHNaSrmJNt2/Hmlgd1+2lmVieHF0OnptCDt/MxGjlZYD9/MHBDvWC6LgyGAGL3hub/C/wX5ngOYNq7SZJ1xPsonppKsWD/ixwlzXKu0MQS05CjMqnJCUW7YWl8F+2c2WcAnKA8MN4oONJbv29afj35I/mInT20PptaUH3vJg1VrbU4gWyJWw2/ap63Y2mTMwF2MRuuvIZQTlSwAXHaSZT1weqNX37NFVQLEx1GIiMSBXu+ogZEZWuKwlzB2F2OQ4DuhWgxmTA8Fh0md/IG0sc96wBb3E1Jj80UOeIMIsOO3nCA5Wa5+btUaVueIqGHM9L3IGn2jk/PdidEW5Anp7aT8f8korjBKNF/qc7Hk0V0QDvzxXbuHIE2neoZVemgPteu4tFFI5N/wtXAp3BBQi1ozdqWaBBT/fbYiWesp6fe83f6KNaVXTnjGUnkv4ougvZDi99e+plpSFgjMv180/kfyC57PfX/KLbuK6M6nmVykZSzBdxGqe7V2JUR32dYNRZeiNI6PZO2HumyM7/h8adcP2yw9NseW9D4M2wihsY/ozcU/N+Fv+/WDMd+p7Ekl7oN/PERRZcL5bpjq+Oh7cv5mIH443K/tUni1wVrs8Njft/VQfubU2HY0UcFuX0IHc8/yp9NhqFgdMVTLQWTW9RRkl/9XleMco7qqEdhJCK8dHFBAwsK6SB6aUtY4rpopltVKbgnmAmCwkMcg9Q3Bx9DFJ0SVgqQdrNnJ0koJE9BWG96SreVBW+BOCqYED9sZI7DBFc/Hnb3pDwmqV2gr4gl+bzzHfOQwADVDIe6OcT0b3t4iOVhpd6G1LT/df4IdZLxcXi5PPbpwvjFmo8jJpT8DKya0KjW3E25Q6+qQQ9vZzc4d31yUog30tGJun1HHg1A+3KSo67awfgxG7er/viMe+Nx1dLPVlj+wi3X1JJvZlBXJ4yhfaSnzOa5u1ZxAGTz1OuHYkz7USuyJlf5qYV/oCyyypwaQ5DUpzcISgQGdOe4HVA6gTMLHWbX05MCHdfBFRa64c92/nxA0OS4m8xruRgsZwxwLDtG2IHXxcA/Tfam0Rqd5+UfWWyxLSHF3/u5gpLARwPsH59Tb28MhFmVFsELOHt1VoTntQU0qJ4ZljyUwP7Y3u0TmGhj0bEv3s7eqntKUz7zpGnLyxbu1tef4EJvFMYLBNIkkB3bb68i2HCXkoLJRyRH6VT3j9ahea/acgt5U8WASlMH41jURGFdCBWHdk+aIkyqDrJ9KtZFT6h88vUWt9iiAgJInLTL+tJ2j3dMHVvT0WkcAt8w6uXLYT7AGAbKjetqwLiU6JEXfCdZfUVQG50ztLwcfuTlzCO4d9vhkiuy/NIpH9NoONGwCYSfYyx+ycxZjMnLSsJcgys2aANdLGpLnQhy3WY8QxJTAjBgkqhkiG9w0BCRUxFgQUilZxcWgYWs3WodyrZQAAsliFtB4wMTAhMAkGBSsOAwIaBQAEFLCnG3FfSE655zJaBGibla7sAnVEBAguHlNaj8V3VQICCAA=")
+ p12CertChainLegacy, _ := base64.StdEncoding.DecodeString("MIIWQQIBAzCCFgcGCSqGSIb3DQEHAaCCFfgEghX0MIIV8DCCDCcGCSqGSIb3DQEHBqCCDBgwggwUAgEAMIIMDQYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIrcQY81xUTjACAggAgIIL4DOy4Al54XhNHaqt0bF9FXvIta4UvQZCiCSdm4Jwjbu/pMCDi7qthVyhldB4w6SaW6epS/o35LcEvVef6JJY08i7xDvyPFiD5pI2D5TExKwu1nhTqEJpEf9TJJ8YUT0Q8w9ZNLLFw+a2VynLYnUkPtzvRSKw0K/x0YqKlgNf8f40ZDkRpXdKFogc6v2kfQW/iNWyxKkE7Mdmqgk+H4BmkmK9Yot/ifEPYYWGbKheXrbdkT7VKQ5fLEFvSBQ+/Tj6LVYAQWX6hWVaW5GvYFrDgFDcoeqY+CZ9uQ0u14x/AfaIqBq2wMIUIGuPXjtdIgs2i3WWg/8aPlrttKfCygyo24V3l4sQWdzChZJDRnceoW7sKXl//MciE0iFTBzPlcu5/4hgUb6MgbbwYFOlHxT0ec57GEeZGM+Reo94EDG2fI2dS2DLJlMua4C5IXVD4SorQkIrk9g7w9K7ByUercnLuw0063HRyeeWEVcb9z53mztGE0/8PnQUc2CoA3nTeef6UEwesbhN0FhyXYwU1nGDrEb0lKXhOTVexpFmx25dGbVNyH2YjwkASWDAM7wbUilek0HReAU1oPxRd9fR91AEYV26y6W7jWQpGkxjKRQ9kYpGEho8uNk56kb66zQf8BoVjtfdbwFgTuYMxRodhrwLgeRfzLEcMHOxqml5mgHw/sfWLVR4715G/CNdKKZPGmT7qgRlTX2Z5AuGFv2zJLMRZfuEcRbK2WqBpHYZziTmtWH1cTVkcfV73AEo0WTTXKFvHurnmnKvVfVFJfaFp8xyziKOcN+qbYiPB4ukKKX+WLZk/PDbZ/63D2u98dvkcvceRTn/tM7GARK0A9DzSTHdsG/3UPfsWRcQsQD8MaHN5vYIzBocte/WXw/7Hk6mzy6HxYFlq2pnomFbnepuJr2tkhNXEwTqjY2yHdJaE7n8tLTY+sCXCngFPDwk+VaizFS3VHgzV4l8EXpnM95j/Hkg/AV/Yj7PtgxR6gp/Cft7XzU37kPLjhe7NrLZ29Av/hrS+bJgGlQSRAa23yqSnZ05RrY4NqQ0eWqjY7l3hSSmNo5H+TqSsyn0htlglMOJSyV27Dipujr46bju+er6uIayRIFtu5GjAC8BWjLsLeKtcNOQF69GTBmb5SgH7mZ6h+p8acYUqRSBwfKTbC+20MSQg8JNeIZ1hGLJ7dD3mZxRHs9iwAYTaqVyEP1GB2z2vIT/yCzk7r9FIor2WItVkSqmUWtgW0wOJbSYkZnaHwk+5AQSEv3+Y3fMep86oYIxBGwBS2Tf0QSxEY7JI0DIdK03snt8XDv3UUASC6Z++tOJhqFmXsx9o7inurNZ8zaQzrZ8HwRWOXRX1U2QLVbSlFipkVlbIXWIz2008B/iojs5Ome0whF9apHbfKx6bu1EQ+wOUJwPPWIBQwm24hQL1Vha60f0+Uf5mzgIVKiPbYcHT8TCZ6W6QcPXVXpbLYJzqBeMDAAFc5ykJCcbpZNGedJpZHD1Zna606OaKH/PUER3uiuDi34/Sl+2QFqjJ9zJR6EYHyec2EMj0W/G6SVkAK7IYekLsafR6mXLu5XGhI+4H6g9kyihrHZw6Co27iiALNdYSHNH+Jhhe5SL+qWOa0wx+OrJ6RYU69qAsOM0sahl/lLEHKotGiLzQOjYlXTKScao4VRRm7RuDeuBK90WzIJanjxQH5KTEbnFNJYCG33wpeYD+2OVyQ2OGqnSHKLKmcYDkXsoGsIpIr90dUPjrBKJ/zx5XtMM1UeDZytFm2Hl1zAFID2ei8HwTKK1nZ4jPlOVkebqBro4hhK3dEwBctSWfWU+VHuHCKo2cELcRjKK3WFGoK02vZ7UtUjs8NEwRK34xifTvd94t1S/nrst4UbC8DylJhUHOI3VSXXFw1quIQQmiVgXKv5ojwHtOOOtOaeJQDO55pkZgR90WE1Hg2fSiUZl2dRgIP1KoBSN7hAP0XwGvdYljArVPN6qVZ4BN2rGwunAY7rMF2mCuwTF2SizMNtV5R0Hlm1G1pvXOTHsGerYCfilPv/LjNB7J0YcGvTFGQxhqLlnbPih9FrNW86A2vIy6wf8B6qk9MbBro4qwEXlvutGQ1M2Kj6CzuVH13KbQ3s9NySOYzndRN2Ny7YFxfOvL+TvLD+r/COM5DgTLB5Nrd7m94aNGw7UQgU8kGMAvTL5OrLTkYCnqCmdUEcoPfXQveQ0F2dCNtgLud9S/ibZ+F3C0Zcv+mkEVv5i7C1IvIRLwmMCX6eWYjJpQmKt/aC9mLyoCWm74gBFZMyDvxJNEWYXgXQyPt+6wzn0JLKf+Aktp/rtg2Huz9sLAt4ckWVLLdjn+dUZ3NrqqPebNgTf1a00FTmATuh3IxzIGmKIPE8aIiayfFCwepQm8xECdK6hXwkFOxvDFHG+zqO6yT1nPKa3wBWKBF2fZyiWM9WJ/4N/JQhUe9Zsf2dTgSFoQT7XSbsuPqBzWoVJU42tNg2SefTXurX2az4+g+b/D97Ac3bnI3kC6lrLFrsLSA6LDd8zLYn0Qtn/Tw7BICNHg7Cawv1722eIKAJ0FadcPRM3CkKeTQyHt94gsXT3b35DuOkozqmI4dr6qgTko0ST9RvFN8cw+V0VYA2F1ZuYjntvMVX9Eb71JUjx9hg1EkzgOLz63mdLqZIFc8Nz5CR5fYiemvY4nYJeF3J6dgCFHVuKC5LMVoVC9uH21Jtj9vILmcPTNi5Vs6LQGPmKknQVyJyk8pMshi7h3iS7+DtkG4zR9RkCGd60JZvtVJ+GJRginJ3eoQrkI6FTSE2Og9O5N75nb9k41eqcwMy18/I5J3A26g4Bmco99nPX0ui1xzMPuLoMHdIr7eTkwp5J2U3AfWTBXwjTi91yiMPB0M6kXzWvb2buBj6/2lUuOXXwt0UFMP9jY5D6tLP0+4sJPQ4mQMjfy2lqp8HnncGqpg7R0D4DDfD7rxZKsv+eCW4m7Vc8Gvx3V4O/lLVZj9kadHsJvJpvlx1i9TlqustK/dD3oHw5HLDdNKswzSBAwzBY0zHGAXd3uBp7E3qV4x8kluTrhNOmH2bAXyfZXX5q3kcTb0ShQEMOMtJshp+TxisiC8x2K7b+x75MfZstzX+BYE4W0MgxasSi25ixQ7se+Xa6guflQ3SRn0kvJ+iquidDvK6Q8ZD5ffyvyBoQRnYGCnDvTmAEwwEMitOKhj0EEIk7xlOuAzCx9O7fl2wtydtfZ/K4GelGMfTrrNRBEy23lJ06uT9jIHJTqwMgFOAfI1gn4b0x6cSRd1ge9jS17LS/uEp+RvRuNULAiB1Yt7T9YDASVIdHzfNdBb9olXop+MbLiqX2cqhv+uEn2Mosvu42aL8HcpxUf1LSQf5aiS40GkvZ2u6fmAHQKJVbfDk0ZnHipPBVP4juIp7/HCjxu7YA/NXtNaM3GnLuUZBbxksy/Zd1opFDd91EYAommSP24SPV3pr2w1YVHLiTgP3C99hWy6EEyI1QbkoymiYkj531Ntjf33JbOEUCExjNQyHtmSJO0i/KWmO+PqkzqU4xopGUoP0GmgbDvt/C+ft6uNS+MrI3jORfC8nnYw9QJj4ndAjcvxIFJi+i9Vz1GBks1Qf1CGN3OYmgGB5l+kpQZ3MLOcgrOCWCzN/YEevaZlsmggHQ9/YcOHEtLf685VkQkysKGDdo9+X9DqMwQLtOYzWJGuZFgqYizMcUs5nMWWCRBX/n2uQzENG8XzzGOR6VjhtxI9VWhcQUybRpvA8StOMWO0/6thW0KUZ1XNp2Ntue7b0gwMdwVchaclxlMquKJIX08jLB+mnEVB0CsV07GLwC6qlZed/E4rpwmmEnz6R3C7kNvW6nvpV9+1vKMKNUaJkpwgcp0t/Ux6dEg+jY6izMzUVCsYLkgPdRIbupp0I3MpjZX7F/iRLloz9xSvG/ObJet5MktSLfcHh7rMdL6dvDJJIoexIkwvKLztXQsa7fmIKI0Wdf9Tu39WBcDQ7iZlNxP0nxOB0D29rpF6A48hSz+tnqii44hEnm7gPDnvDnMnaIvcr+cBmrGhMh+UgTgabo5Q2W8KAwggnBBgkqhkiG9w0BBwGgggmyBIIJrjCCCaowggmmBgsqhkiG9w0BDAoBAqCCCW4wgglqMBwGCiqGSIb3DQEMAQMwDgQIYVYc19McXBgCAggABIIJSHMe+svM6dSohtIIhjRo4PdXqotUwKpV/+aSFiOhpWfTBXsII7CEMi6xTfbr+rkqf83X8KeRyFP1SpCVxP4YakkXLnb+iYj0yT1K50JMScyHGjGeP+nwNKErQWIg5JIz/lLzDcVprybylKtTZWDAZTQD+j+kxKatSDhoLNUVqAhw3VbVFGJnvyX+dT3g4JCnOVmmqLPYND8g+0JKVNDL0DzqGx9rlKUySSRjxsM1Mlqyf8PJ+G/i0i/VX5mr7N9oBCa4li0KF2nb2vsJdXHt2XsAmW6+Igq4w8eda6oVNW7Mo6sXy5hqV1bUmc3wTpodrSK2JvEN9k1DpPeNI6H4SELM+vMOaoL4eOQRoDkY1hUNsRbfQhSBaLSi2h7fS4qaaKnxmadQ0lcHS5pb+PJhhaMv8J4ust2BF/xLZVCfVwhhZJXIA/6NCt52e1RqYtXMcAqXdRv6DalwL4ukcg+TIUIIC4l3HDYpWkXJQeSJhWF5UzsceJbcG5+Hyx96VFa35hq9p3Pp3Q93l7plK0HZ/CQztgGh3g/IHXpNSGuXIP7DCTH9X9E5aWY68BpP0o9AYfP1ImV6iyQKZSkZ3iD1TueNNcPFT5445b0dPRyj4FbnBu699IE4kW99vW0qh6TZWUAY7OfscnY9rFra+NB1VdaqiSRi5eVH5H80gURLi56XDXsMUYqXv/LnBdseizc6F8fuOY16+8+nl9T/mAtHdCpn5ySD5P7ijZYRyVb+e3pp4H9BVEJQK8KQKGukzul8UiSpkDfHHQSIiCJaxi6/Jzef4hQx0kE2ZvzSRPHpt6G/HHbo5v2EYSkSf6uDL2g8tRuCu0q2z/pUVEvHDklCGgyfez3j3cmpXSFBJP42OYJTHAVHjxJOho232McXXwj6EqJEEgTO2mHmL/4+m98Ap6yKp+VHvtm/vkMbvx5/aKDFyglzx2MfybBe7OMmyVkmBWRG5wfKacq0g5kj2w35AVJQ3xvxbmzbA3WDV5SDC3m4kIZ/QkAs9EGB9+qbl+rshwEyc9AoEV+fwDk9tN+vrDAwdtl4kFhz0YiLrtYqYiILVw6P4/S2b7xDz34yIcUIZRpn1E3o5CMVKEOD+ncq4foWlOF5LH2ofVNqD+B4xDU9KgANV29DpevbPQGj6KUp0eDGvFtQS+RI8PK9+1c6bcs2et3nrtHKkscbDwZmMwY9pvdZDiQl5r8Q1kBDM1UEYXpm2EgTH/FatY1W4tbgZKJXFCuZ4j6h+uB/tlMT1JMDY01KKAo6MOEdRmBNnbApja5IpLxQogIWvbkqo6eksluk+VTvXA0EuwUbSNwAeXJrKrYYocaw+E1j2ivP5yxEk4ugSJRC6ykRbi1K0Z6OukedPJeBmUdXUMqj9DdN+ZFwUji8DXd9MK1gIUDy3bPGC8mCNCGYdZETY7yklB7imqgj756I1dy9kTxAqlTFpnJh60vnwHHz8od3w9G6LLstRcDzUZj3ecKAR2+oXbhY+fvEu72aYChRPY7u3yOl3VbOJUe5RBuKMKUhAPWWlgXs8dKNw5vZOyByY4RB6U0N7rNdcSH1AtJChEfCHv1W+6lc2tfeUsI27Ryiarg082+cSVc/04/vdDpPmo86Xg184AgMm/6ycXOcwRk2yVzN0gfjLIH0qs1qz4Sl/y0HWhj0wj92nXXqWEyBkUPG3O//KZt2Vemqc8Z7tTd8VfltFSpUkDXeFMFwJTA9JOXbE4z7fK1HuqEcb5Xx+iuvFBCEdse4d9j6tY00fR8FamJj/1n+UydQMhrSgNHKu7PHofwPDHQPaeumJWXdLYkzVeSCPvpVbMjSXVOID1PYaeCetzR3JYvC7SsOhwTuBLEt6Jg82c8icGQtLCm5WXuGqJ538Q3hqduVagQCM94atggnX87JDNU+R2GzsQPQX62LK2dYF4vZUWJxQvc+CFcenS4AV8xMzgzyNhTQSwp+P70jdoGuTZGj1j+0rc65mcax05D1t5Dfe7VIlonGTQWZexeo9wq1FeZkDi2dBGDt7WaRMsCN6UGdU92vSo0Ez/PizojZYnX9n9gLKd+71iOn0saIuO6BAdYN5+nd7FRecv+W2uTwdGKQMKHJPA+4qWBjUNJAptWt454g8UrfrLkMsW85mNsQLrWmQvH89ul5JnNgn5zGdkAoO/RTigYHTndtX+m2D1obL2lUzDQiPNITUKdr0eSCHjMw6znSMqwlvsEg6638FYT3HOj3v7x5CY7EF/7GrmT/JiNeWkL2zPsr+dDKkG90B0pLBTPn72eizkUKfH17d75PXzOW2JrLUhuHTSD7vDjNF5A+Xhtlsao4ykICEHRWi0aK1NMdRhjxzUTpSPe4YizohIGtGTSccm3lGtav3xRXOQst+MEC5+mwlEp55smiDih7DD6vogr/Tmd1VNG4Fym7IQ12pwkk2P4SFX4xJZY3yTeXR7+GByxRup47ousD6b1xllJHLqeVJJpBy0R/jlNxwuErahB5UeWeeN/VKFjVN1FncrIbX4AMcgTtIKxGCTpsvPtfnyCbxrE1/TwBiUCdeg+zCbNJD0Yq5/3/cKt7tCbuhkbH6DOMr5skRJtAxV1NhoyrzO90w9vp2JQYovLvFVTM0YeuiT4PWRh4pl30pE3+6D1wZPT1N61L3/3Sf6TF4VAalKWmukLnwfL9pDgkJfVGHf4dq9rOLiqCHd0gTmReJDYEpYWvglDbWJs7vC3WNn7zg/FmV3f5IC2t01qf9P02Og+h70usZj/cajjsIn4TsYKGt9xFDioZDs6pJXylzxcjHLzo140iZJVyLxTubtaP6B0oa2MpovGwt8q6ruafWIO6L/LzVwCi9NT6pGGSdeiqhnOZgLw4N5dBmyw3kQMPmhEErurC9DJaZOmslkCdtMp9e8juRUwD5j0tKBck2oK23bfCzjZnCG08DRCQrFZwmH7KtGu2TqDEY7gOVazNjaDf6B9vsA1cZcE+FvmMzM30hAOYBhc7/oI26WQEpxPRR5b4Tic90BrYd0SmvmIO0EbhNR0blg73J2Mrl/yIAn0xKn4VbYo6720k7n7mXYuRZnnGZNLeuBva/gQI5GkmcgWHbii+uIuLweBn6i0OGD4MgdGva9pvsUMfhAC7wQcaAvMW6Nn3uMMRJ8gNP+l6TIErKV5e0HloaavWRaCCtfMULTElMCMGCSqGSIb3DQEJFTEWBBSKVnFxaBhazdah3KtlAACyWIW0HjAxMCEwCQYFKw4DAhoFAAQUx9DoZdGgJeezPrDo6dX/AolmSWEECBpinZSGZMFdAgIIAA==")
+ p12CertChainModern, _ := base64.StdEncoding.DecodeString("MIIWzwIBAzCCFoUGCSqGSIb3DQEHAaCCFnYEghZyMIIWbjCCDGIGCSqGSIb3DQEHBqCCDFMwggxPAgEAMIIMSAYJKoZIhvcNAQcBMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAifH+TyzWyTWgICCAAwDAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEB0ps6oHS/gRSCV/29VYGeuAggvgU9Ff3wIo7PqEzv+03ljYRq9iA6GNFcLRJeNDWjB+FjXq4DPMbyMR0rUu14XgGbtmwEijgvthQ0dSNw/KL3l4y3PaEZLx0ognzgEGoqvtViLLckvzp+1naLD+G1PPAPIxXUPdniOfMECkD+yuU2UjWKtmiZjdDeDNETe7kY9/2ZLO7mkuYPcbasFLWAN5DchK8tt6bGmtJwdXrecUVmpPF0G5kzvUCvF4q2d0zRqKj5vBx3GIiTWMX/tNRSbXImySg4D81kM011QRLsVWgd/su+JdrHxwtWAYnlMJXyKakljgP80nNK7fVq/6Teo5sFqG8ske4B4WxCalwUm1bkNcIx6odDDZytrT+iROEHWLWtDZxWYn8v+Dn3fBiP5lmscqktguAWHyfKj8qU3iUfof9DKC+fDlrPfAteLCILwRNvSgG6EaoOVFTg2yv9GFzkAWa0GP4hX9umRvfg1s/2bSHFPvxNPNnOQjy30C8BJTgb96uzXET3hko6XTQ8Gk799NCOhFrLTPxJk4LG6YJZaiA1v//I9bepylsSZC0soFgoZQYeJJTBRdomZKgEBc9hJvxknkekKdWk393Xn+8Jt/KjWM7p2Wj1qqGL7TSPOXmMZFScLDnMW6bZQBtfXhQoOhbPMM1txjGTyRQs0qiCAJR3iAlj5zEoCIGOtbG9Ma5rUyPLAzaUz/XOiDxaG9fdvPhiYLjiuZgpF1+r/45j8Z+NoqvIY0ylORKbeXjG5nsJM/NQMCh0w8bEK5DdUJ4HgeRpQj77p1AZW6fNWX3nbJd1ZEme7gvYQXtfjX9dNYuGO5UOgNHjfGIpwIlRNFb02U6oPZggDqoyJcZ19BqA3PTFt0jgsi5MENHXhG3/X9DbI5JGCb3Zlsvkn7rPuHcIPnrWMRBU7YInyOzP0qiaJxGn9zwYZKwzTgR83rFmbJoDQZ3mn2PaTjq7bDbC0hM3c3YWT0We6nw5WU3kTgkPoZ9pm5JbugNlY0RUxdmxzOwv5/CrhEuJVw27TvROZKDtr0ytUqtw3gmg3s9yqJLxzt6pDvr55RW9g06ZjGmviLOW4YBAVsHWK3ZXyDAjaI4V+AamZZ8yag70E/E/2rDTKmTtGYDKdUf5BLVSToCCzDWUgAv8VWrWnsk5i//F2APk5tYhsauCG8Okj4H2Om2DUGEfoXoAcweVl6O5VZ53dSFYWrxbFFt56REQSPRf3HbFvKQtQcxNxMYfotB9uFigyJdaVkZ9RUGykIhHJO6JS68u5WHBxIN+3w9JH2fTCmbHcn+hw6qh9D8yLfA+GPC7db0cOGxcuPOXGJCU0NERZoRZDgPhk/DDkIbXF44lqe1/kDFjvy9OakjAFuLrNoZ6UUytrivU6N4fDtXVCg02Wudg6magIPvmeFISm4cWkQ/iAu0YD0IUFGrqWKN+RZCFlohOtePwozhkXkks/ehn8f/eczyIY8Sp4dejxZoK2nMIn+Q75hkUE47yx2ifc7joOjGbLCCvH8JONld6u22Kgao1E+APiMKTwV5Pf8feJStwd+pvaptM1qlccdU8e93zjkWC3msGqjx2HYacX/mgc09mt7oMW8nKZEyBuFGAUEvbglTr7wccT1emzWNaLx+YhUHHC+pP1i8Eiw/KAs3DnUjT43g403+H7EUnq9Zkscv23NJJTSHwwLKIJs9vkSFG54iOpKbz516kKJGi7twRu0QQbq4oGcLYWIe1AjiF/Pfh4itWTviVZ1GididDgbucpBRS+FPUcmwld4uyxBdcIKoVQmfTr+S1HnPyRFEQYuZmqAvdwko6w1es6xKj1lAz4Q33W32rHgxxOF0EL5ofoH9mo2pfjFrGqZ6OhnKIKkG4wg05VHVTTpsuz0Osk0thHl2TpAHif5CGqDAUh34tHTyioUds5A+1HJSXL0+6wRO/CjxfRmlFjvVpmFzcUea+xxKGCXAvDj4+KLn+R5P/4Q5prE6FVxluBksRJ5VVybZRip2xCVrrW8KF7Ag/oT7oTIE3ZZ/0XV7+qgS5S9pmJpJoQzdLY1r7IOF4iDXHpAOlv1k0hZADIQapyb6Ofjj8++bTDcwWDJLTpZB24B+LbDK/j0Lg74tGBNH/KdxGd6EXl1VR1A9D6ZFqGsVceGp9s5OsW99I+vNIj9mL8kxGLnEgaNGaaJezTu7d+t2gdD+xqzF+XkSUkNH80MaejFiudVmLubeefGOofsFSk0ihRZKB7Ho2bBgEtgXV3ptG4Dlo6HayRU8eGeQgfUH36xUGHfCbcv/b7mqKPpUrTuaxGlHZaMHELZkR8LC83jGylmd7LLYqPNIwahVCaa2p5etD/gfubUgzY5K3UypGWjpr9tq8zpbTSszSKx9wmocY1tvfdxDBlxN4EoObRNrZZU9FswTtAJjZ3ExBFSyAdvNgajtn7YYHOsESOIjvYdMAAqzk6FYptkbovmhk7Nai17DEGdJllIVHamkLTrd2o1fCR4pZ2/LYj5daWIkjvmy+c7zf7NhfhN21dmcm6KnRp0e0McNSclq2siiig01n/8qhhEimiSOdqxbAkXFLylWCxgjmHZR0jbEaG6GZ6SwS0oOhmhX+OC52Xrip+BZ0OGJ7Wohbgn+6k/ygoo4LkDmgqX/kyWQEH8ftdgt+8UCIiDvpkjiSVO6FuwnZfqlfYvlyqfpbKm2tETT7fsfJyc52Fg4cd4H048ufUXsrmuwcgVtjhknPiRyO3w5Rp72ViNQMtTegplFiJkqh+C1g912q9X9gCtOyOVLnqUoG1Mv7XiHbTpNTQS+E1YLyt8bkdhGpZ3vwNwcCH8arKD41z8w6190c7OFp0miQFzK4YQfGHy54p9nx92469LDUUXEzVlIPuDEFz7D7Peck0c1vml6ayWwixwAZb4zbrrpS+AEPsEyv1/eaeNktYQuWpTOLeoXlMKApvhZW97buwngbKxfHe7MpXsAdVL4ctGIkVoQfQaHskfRLws4UwtxUm96AtWvSGm+Wr4ebBRJVQ3fQ/ZAYkwZ9x3HrdTV+hw6xwsE4Q4Cis4XQRWJ0czzUzKHaVjWBKQd+0jOtK5444XsGFuKWQzau/jnah+BxEv1MKOi1fBhUA/eU3RdgfQJOkOiS2NokChNgs2wd1+AJj9RS+TvSWUoqEItg8VjRGBqghLfghlcqvPjvTqQ1r9Q1QrlgmO7ve+2hP9DQ6zyqfk7f1hDqPCHiKt3QXvmU7WYkYUPA4fBwQZVI3JVRux4vjHLqeqitMeAWtu6QxRglYuQg0zcdOdoUlKZRxpIc6zfXQ0w+IyVzIyCOHkWFnxe5ocGWBt/P32A1Swl9d/DuVG+awkQj4hYARWH4FX8MODa1XQNg+4VNaYXkEGKqQtwTjTBUZWfcG8wDHj7AiaHlotn3HS2Oefjb0zmi02IUOqU8G3WBP1IOBVTXrjXbPAu8Waxhq9TuPsFw8CWm+7EW0zwxtkQExcDR63UWq/QPi7qOB0+tIyI8sZ2AmcykwdcoNnIH8RkurXZr1fg3Q68uh2wDfvEIxU5rQI363TwTt4wSTlhwATJA0IvTOmPqeYpm6mXNXCOosqGOQdhgVUGZgGFZLIHfupnh/XXjHh96qa3s9cMcveGzMGnavmTz1HukevIisn35+oj++WgLbGDPHqEDJPbz688PqdrSfwAp1RCAn8iKxlL6NCnsjdAeL84lEupsuPlpv66//7mgkMVMtKx90cBy2lmh6puiunZBb0LCT6kwpoNaA6yfFEGbwLaV3rtgsnpgw1henP8UVANUStyjPuH2VsA95dN0ruK0tHUxctUVQgSHN47vxMqCBccQym9/xqyTFb0H/8sS7bCmpeHD7slVIW9czKBkM95tUXp7if5S8tobP8nfDKqR0PPVzspkx/7R+BEEsYLCGL3GzWwWqF8aT6qKWZnTmQ8FesKBPsfMr4XxGKdSwFMyhOA22/TqaaPYXhjCT0cWU7QzfwKIo10Cr9leGifV9Ns6gcK2hK8x0z2rfyw8w2wR4CjOZ9h0XsdJCQitE7gdZ+ywi4rfrLuQlISBI6WU1KHSz3lTo0qNLg8+8jKDCCCgQGCSqGSIb3DQEHAaCCCfUEggnxMIIJ7TCCCekGCyqGSIb3DQEMCgECoIIJsTCCCa0wVwYJKoZIhvcNAQUNMEowKQYJKoZIhvcNAQUMMBwECIdXYYUDreXCAgIIADAMBggqhkiG9w0CCQUAMB0GCWCGSAFlAwQBKgQQzqrgoXCbrvu61MjtyU5btQSCCVDTGsOH9sBaGwtp6QR3Pakx6JVZwwCdF+oKZ+gWGMVPqY+3sw/yMLIPSKhxpE+SRiHMtHu6gTCUOHbxwssgjTcwFtK+7P0KI2h+RqQsycrw6i8haWQeeJjeFUEi8dumZluyXCABlVv0p1bG/mQI5suzBObem+10gr1hK1fEpbGGptM2oS+no9/FPpdcraz7DZuJrEDt13nXuxvjgxAMcuknXFMCizx9IOSr1t0TYC1DXIuWkw//Ve6WWbK1eiSaW6L41JhBRQhfzOuyvd8odKZ8hz0bYA9s/l6CkhKVDadB/8CGez+/j5uNbzO4ZcOPbg8MCVv2JGw7ibuBjHW/SanLAtCVyVxfWLwaBqBimm0JLM9SKPOxdBoimzTlm7hJoymg4rg+pWuh1C+EO98WBxSJ/B92IngLoQp0mpxEjRZZbBJz5A/ClQQZHTwidGEUk+NstHTq5ydBHkZCTxrOVj1CrzZaKiEzjagaLnYm5I8hwhVFIPaSO9SJo89+oTiBx4YUFPe6JgUJZXHwXlRWyCDSDYDEPh+58QEW8lLOhwFIoN/TJj/H7XQXc35uCvWunUzX0hMXY2zlNeSNVTD03JZuNzeg12aa4gv9Yot/eyjWq5RJKjzQ39dwhmJz1O5yTLi+CshwIzrpwbJN+s6SzQuqd4NFzQnL16ezPil1mxd3k7+akOyIEv0Dyos9KB2sw2LxeWMPa4UNOqUvtghLO+lAbIxos0wUjOorPWOJhmbRP3ikl7INSphHkWnlxGu6sjkRbehA+27Scz8zLOMgJKMAZX5DapuQkfmTQU3IJH6dS35ZCMZgpVYW/rrSxsWV4FKuktNUHYH7i6Xn2MjztA9F26YVfxRo/HU8xU3Jl4DWOugOhnMQaT2yziGO421kE/eV0Kd8NLJb41mhX+igzX7kK26k7CKSkf9b1+kS1mpHHJW7eu70Tw+2hghS/lskPIq3kjDH6OuinZqQKLZvqIFMr3vSF3TtEIinaGAgnWrN392LDzS49vfBOy0qWz/dVYZEZYJcr2BCtktr6GquC1fhO+rRDYLYGFo0ACoOtgPo9VdTFVHFcTxqmcEVvaPqKjrTof8ecq6lJsQMzYRWE4xTCj33bpLQXYTX4Dc5kNlPmvV00uYgm7fhjThDAR72uBw8jchXUP3NXv/K4RDVppGbPptxA0DXgyAge+PltaXh86JgYdIeKlSSuOYAVzso3HF1HFnidyiZn9DWREh33OLQiUK/tAPPGIT0H5Fkwpn1OWCTqB5Y1ufUeM1rsgDq4emXRPNjFjkjh5kosCQDX3t2fZCJmZ3ncTZk4dbhnP20HcvJ4egqouzEzj0+/XImAL8UbyeDkx2iGli0ifepzlDfxB0V7H0Ilj7gS2TwvNViRVm7MkpcYJnDiP4VNP1MrQiM5a48Zjz9UUkm/xo67M1RYMfa7YJOaL/Zy2UvUMuxI68hJTnjpIp1S8v3siqbOi+PKKkUo5BuSFUV8f6/14lerjnM5v3+DJV6fL0R5sSDP+v9BQ5nz1OYODlmCescKUKigHOnaSu9imFGvan0ODr9JovED+VsuEVgEyvlA7KfOmXPGefIkTdNH1MXeoZbrxujVYvlJJM3QYqSU3xXGMxuWm2wjj+YIzCMZ0AzbJnES+8LhWZ1NenY9s7YeNldxSHgN2iBSHNBmUQDHdWTqWv4uiRql56phfONvCEdQwUeaK5Eg1xLisG0r3n/ItIfL3Cb+6HiZdcxF+uplL7UCw5D4BaXTF6IEOaUjyMQJR2vtK0aLniox6xymKRXaPOkE5pu2gT5t0P1yZk6Kx38+S6Zi0S+scFl+wPKw0vLedPI0leKpe//kVMNVjHxlsrhSwmNwdHnThx7RgLcbY7IrAV2ShP8WOBqLtN0wTvS0H7oEseba2DF3dNVuJ/uGMyQ3Xs1WrChjshkGWleV8jSBp1O4lBdwjZhPFoSBRe3DKjty/10dlBsHFgi4qUlOqXMxqlux9neOjak8r3GFEX5+qok+rCiFGIVIMrrVZCsFkl3wvOwtRlDWQsfQQj1RXbm4NpSUFqfd89c4d2/v0eBq8Gb1V4HKZuQxTHn5495YxuF5bJdXSa8ISQjAccQ8nCrkRXlDpKeZjDV97X1t1TzHResaWuEaOE/fWsqkF2jclZsgRpx0vyqWGK5xEQBRfhavJcHk78AewDv7p7ApmGtlcpOjNV9mANLFv13SfofHrPaDEwk1Lim5iMqWZYYmW1izU/07w9uz73QS2ecM54+7MAQU2u/BXdzlVDg3wdX9umL0HCsa8f60g/O4Lpf7G2m00sGA/UJPyWHRTiYkOX5AB7ZvfH//Sqie+TKgkDPQCScJZiw4XuLqNQtJVpnRSJpMrs1IQ5zEn3WXUCfL5BVOODi/aPP9jMWzCirr7fAZW0AVlsi2iWob6Vms02uW4cz3iewtUX12mkwVeFvKpTHT9s1Rj+P2f5ruU/tnTY1EJy358qe56clhFkMV8wKrtqi5JXNuQ22Fc9GurgrPD+VrN8xXvNLy1PjGZ8alSYkj74G43M3XUiqWxron3Z1h2MGx7p0i4nBzESpGimFQ4wBliGs9sVEnyRkelqH20fb/LXqNiq44++ruhAZSe8MRVjeFHqfWLiHe8DIuASRo2fR+I5cuL+55H6Na6GSh5RvU/JMJb0F+RatYCjHe5D4D6HGP2y9BIUE9eXwmWTsES6JxK2h6s+xCYYCFuoAfXH6laTfNmDNbUk8ovPsuW+lCaYvYQ/ZS9322iSZQCN/e+P0UYwljCrF7CnCswQ4nwg2jjdCka0jWmgED9/ZxX++cVyFvzjid5Tv6w/FBGLJ256yusoSlKfu4v5+J1Yn6b6Bhp8IDcC/mSA/rW5iiW1j884Bmek88KvGyIulwkXq0+ewpI7w7ixxCBWAIYIMwvyitis36JJjNLcXYwtWkAXPB1/qA0gpeJdXm0nFla0nVzbTGdXVjLzgnNkCNEhGoMCvyP+TjMzh1vgsbtgOmrZREiPCk5egNiWC6LyE8CuXlhmxD+TYCkwuLyQ0BrCgfX+rzmRT/+1U9kxAmZONbHyHx2wwm9P5ngFWxyMY1sgkM4sH908RosdNlAx1hDMz94IIsf85+90XGE/Huz03NPAqx0WWefSVUwr3kr0/MsbIRG8Sicb5vOKonJDaGIFQf5UelU3iIzElMCMGCSqGSIb3DQEJFTEWBBSKVnFxaBhazdah3KtlAACyWIW0HjBBMDEwDQYJYIZIAWUDBAIBBQAEINWmSkGtwKFXIYc/rCyFLZYfec67XU8EGxppguPfMI0lBAjIgLYmJTkMGAICCAA=")
goodKey, _ := base64.StdEncoding.DecodeString("LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRZ0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQ1N3d2dna29BZ0VBQW9JQ0FRQ1pITzRvNkpteU9aZGYKQXQ3RFdqR2tHdzdENVVIU1BHZXQyTjg2cnBGWXcrZThnL3dSeDBnZDBzRk9pelBBREdjcnpmdWE5Z3ZFcDRWcwpXb2FHbmN3UXhqdnMrZ1orWmQ2UkVPNHRLNzRURmYxaWZibmowUHE2OENlQlFpaG8xbDNwM2UwQy8yemVJMjNiCnZWRHZlMm13VXE5aDY4UTFFUmdWMU1LaWJHU1Naak5DQzdkRGFQWmpKazViMFlWVFdxREViemREVnh2ZVVMNVIKcUZnL0RKQTMzVnE2VFQzQ2U5RjBIcEorb3graSs4cUxmWU5qZExSUDZlbEtLTU5naVhhNTFvdnQ5MjF4UkVGdgpYRXYvTUtqWTlhNkppNndIRSs0NmdvbFY4V2puK2xMRkRKVHh6WEFEN2p2NzVzaHY0WEczdFlaQ2J4cTMzZ2JtCm96c0VQZ3lTRGtCMm5zc0tIUEFhSVNPaWpjNDhiSXhwbDVocFJPWUZFblJDWnhablhQNjdLZVF1VWZXQkpoVWcKYWltc0JRK3p6cFB6ZjVUbjRnVExkWll2NU41V1V2djJJdUF5Qktha0ZhR1ZYTzFpZ2FDeVQvUTNBcEE2ZGx4Sgo1VW44SzY4dS9KSGFmWWZ5engwVnVoZk5zbmtiWkxWSEZsR2Rxd3JrU0tCWSs1eS9WWlpkeC9hSHNWWndVN3ZECmNlaGxlWlFNNGV2cm5tMUY3dk5xSHBUK3BHSnpNVWVUNGZMVFpabTBra1Y3ZXl5RGRMMDFEWXRXQk1TM2NEb1EKdU5vWElBMCtDeFZPOHcxcC9wbXF2UFQ3cmpad2pwYkVMUkp3MWs4R3ozU2FKb2VqaFBzWC9xNzNGWWdBc09PRApwTXJuK3ZpU2U0ZnJmR0VmZlEvYXJUVE5qK1BUb3dJREFRQUJBb0lDQUM3ek1CUmJQc1huNHdLL1hvK0ltTEE1Cm04MTEvemo0VE5LQ0xmRlFsa0VoMFcxOUMwNW9UVFRYNjI2cVFMUWpHWC9WS2RIYW9NRXNuVDBjaFNQQ1AxRGwKZUhxeU1FdVI4UzJLZzM1V2EzSnV5OFBueVppUi9GQldVOGJQQXBVakpxa1A1QjJITlZyb2drZGZSZklwWmI4cgptNXZyTDc4Vi9zeXk4UHZkUVBtalhSUmpnMDZvWU9VR1dnRE52cFJRdGZ1R0h1d0hTZ1JodmZwTUpNTXdsd2lLClY4Zkk1NmM3VUg3SzRTRHo1RCtWOWdYUDl2b0lUMEl4OTlkRnFLTnhnM1o0MDIrazcycE1BOFNpQ0t1M3dBN0gKUnozbUZsb1ZRbmV1ajI1TEdHQUo0bGVLQkNJaFhMZlgxWXpvdDQyWEU4ZkJZZW45SjdRNTRPUFlLY0NqUmpjSgp1M2NkamtIbmFWVFc1dDdLTDFuYVAxRmF0S0ZxSjY1V1Y0c3pxWDhPVkpzbWhLalNsNUhqTk1VeERuaFUraWRTCmsxaGNaa00zOWd2RGR1ekRHeHF0L2hHMWNJS3VtamxZb01WNDV4VWFoVHdhTjZnamlrTUxNdFgrb2c0MVAxU3cKa09hZTZ4enJFQmU1eXhqSnVDWFJzK2FFOXZhTmpIWmpnSTNKREJ0enNjeCtvRFZBMXoxWVBpR2t1NXBNYmxYUQpFMWlRQnlJOVRjeHMrazN0NWdIQ0d3Z2lOcXVnOVZJaXY1cTQ2R2VGRVdnQS8wZ2hEZ0hIRnNRSDJ4VEpGU2d6ClluTkRVNlZtQ1RYZEQ0QU5jS085Z0loQzdxYk9iazlUeS9zZkZIQjBrYUdCVjFFZGZ3a0R4LytYdXRacHNUN3IKdkl6SUVDd2JPTEUzZCtLb1grUUJBb0lCQVFESG9SVU42U1VmQ3I4Z2FsOFM3UDhscU1kZnhsQVNRcWRqOHY2WAp3V1o1MFJKVE9TRmxqN3dlb2FnTStDT3pEUHpoU3pMbE4vcVdnK2h1RFJGcXBWb08xTmlrZVdvZEVwajFyZG5qCmlLeFlEVUJKNjFCMk5GT3R6Qm9CZUgyOFpDR3dRUW93clZSNUh5dUlqOTRhTzBiRlNUWEJTdWx2d3NQeDZhR2cKaTV2Q0VITHB6ODZKV1BzcjYwSmxVSDk2Z2U3NXJNZEFuRTJ1UE5JVlRnR2grMHpOenZ2a21yZHRYRVR4QXpFZwo5d0RaNVFZTUNYTGVjV0RxaWtmQUpoaUFJTjdVWEtvajN0b1ZMMzh6Sm95WmNWT3ZLaVRIQXY1MCtyNGhVTzhiCjJmL1J2VllKMngybnJuSVR4L0s2Y2N3UUttb1dFNmJRdmg4SXJGTEI3aWN2cVJzUEFvSUJBUURFV1VGemRyRHgKN2w4VGg2bVV5ZlBIWWtOUU0vdDBqM3l3RDROQ2JuSlEvZGd2OGNqMVhGWTNkOWptdWZreGtrQ01WVC8rcVNrOQp1cm1JVVJDeGo5ZDJZcUtMYXZVcUVFWCtNVStIZ0VDOW4yTHluN0xXdVNyK2dFWVdkNllXUVNSVXpoS0xaN2RUCnliTnhmcnNtczNFSVJEZTkwcFV4ZGJ0eWpJSTlZd1NaRDdMUHVOQmc1cWNaTW1xWG9vSnQxdnJld1JINncwam8KM1pxTWMrVGFtNGxYc0xmU0pqTlAzd2IzZEE0ZDFvWWFIb29WWTVyK0dER1F5YnVKYllQZSt6d01NTkJhZ2dTVQpCL3J5NlBldVBTWVJnby9kTlR2TERDamJjbytXdFpncjRJaWxCVmpCbmwycEhzakVHYjZDV2Q2bXZCdlk3SWM5ClM3cXJLUGQrWE00dEFvSUJBR08wRkN2cWNkdmJKakl1Ym1XcGNKV0NnbkZYUHM2Zjg3Sjd2cVJVdDdYSHNmdFcKNFZNMFFxU1o0TEQ1amZyelZhbkFRUjh5b2psaWtFZkd4eGdZbGE0cXFEa2RXdDVDVjVyOHhZSmExSmoxcFZKRgo4TjNZcktKMCtkZ2FNZEpSd0hHalNrK2RnajhzVGpYYWhQZGMrNisxTE4vcFprV25aTzRCM2ZPdFJwSGFYVXBoCnU2bmxneTBnUnYwTEEyQlFYT2JlWUhYb212T1c5T1luRzdHbkxXanRJK205VERlV2llaEZ5OWZIQmVuTjlRTTIKQk9VTWczY2dzVTFLdVpuazBPWUhrZ0p3WDBPTmdWNHV0ckk4WTZ0c3hRbVFlVDQ3clpJK05lNFhKeW0rQXFiUgpoVEltY2x0bTFkaEExY2FOS0liMk1hNjRCZy95NFRKeW02ZTJNZ2tDZ2dFQkFKTGt5NmljVllqSjh1dGpoU1ZCCmFWWHpWN1M3RHhhRytwdWxIMmdseFBSKzFLd1owV1J1N2ptVk9mcHppOURnUDlZOU9TRkdZUXBEbGVZNzc2ZEgKbThSL3ltZFBYNWRXa1dhNGNXMUlNQ2N0QlJQTEVqcStVVUlScVYzSnFjSGdmbFBMeitmbmNpb0hMbTVzaDR0TwpsL085Ulk2SDZ3SVR1R2JjWTl1VkpxMTBKeXhzY2NqdEJubzlVNjJaOE1aSUhXdGxPaFJHNFZjRjQwZk10Snd2CjNMSjBEVEgxVGxJazRzdGlVZVZVeHdMbmNocktaL3hORVZmbTlKeStCL2hjTVBKVjJxcTd0cjBnczBmanJ0ajEKK25NRElLbzMxMEh6R09ZRWNSUXBTMjBZRUdLVSsyL3ZFTmNqcHNPL0Z0M2lha2FIV0xZVFRxSTI4N0oxZGFOZAp2d2tDZ2dFQUNqWTJIc0ErSlQvWlU1Q0k1NlFRNmlMTkdJeFNUYkxUMGJNbGNWTDJraGFFNTRMVGtld0I5enFTCk5xNVFacUhxbGk2anZiKzM4Q1FPUWxPWmd6clVtZlhIemNWQ1FwMUk1RjRmSGkyWUVVa3FJL2dWdlVGMUxCNUUKZE1KR1FZa3Jick83Qjc0eE50RUV3Mmh3UFUwcTRmby92eFZXV0pFdTNoMGpSL0llMDA3UGtPZ0p1K1R5ZWZBNwpQVkM4OFlQbmsyZ3ArUFpRdDljanhOL0V4enRweDZ4cUJzT0MvQWZIYU5BdFA0azM5MVc5NjN3eHVwbUE5SkdiCk4yM0NCRmVIZDJmTUViTWJuWDk1Q1NYNjNJVWNaNVRhZTdwQS9OZ094YkdzaGRSMHdFZldTMGNyT1VTdGt6aE0KT3lCekNZSk53d3Bld3cyOFpIMGgybHh6VVRHWStRPT0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=")
goodSecret := "old"
secretKey := "fakeSecretKey"
@@ -395,7 +408,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
}
smtc.secretOutput = keyvault.SecretBundle{
Tags: map[string]*string{
- "managed-by": pointer.To("external-secrets"),
+ managedBy: pointer.To(externalSecrets),
},
Value: &goodSecret,
}
@@ -408,9 +421,48 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
}
smtc.secretOutput = keyvault.SecretBundle{
Tags: map[string]*string{
- "managed-by": pointer.To("external-secrets"),
+ managedBy: pointer.To(externalSecrets),
+ },
+ Value: &goodSecret,
+ }
+ }
+ secretExpiryChange := func(smtc *secretManagerTestCase) {
+ newExpiry := date.UnixTime(time.Now().Add(24 * time.Hour))
+ oldExpiry := date.UnixTime(time.Now().Add(-1 * time.Hour))
+ mdata := &metadata.PushSecretMetadata[PushSecretMetadataSpec]{
+ APIVersion: metadata.APIVersion,
+ Kind: metadata.Kind,
+ Spec: PushSecretMetadataSpec{
+ ExpirationDate: time.Now().Add(24 * time.Hour).Format(time.RFC3339),
+ },
+ }
+ metadataRaw, _ := yaml.Marshal(mdata)
+ smtc.newExpiry = &newExpiry
+ smtc.setValue = []byte(goodSecret)
+ smtc.pushData = testingfake.PushSecretData{
+ SecretKey: secretKey,
+ RemoteKey: secretName,
+ Metadata: &apiextensionsv1.JSON{
+ Raw: metadataRaw,
+ },
+ }
+ smtc.secretOutput = keyvault.SecretBundle{
+ Tags: map[string]*string{
+ managedBy: pointer.To(externalSecrets),
+ },
+ Value: &goodSecret,
+ Attributes: &keyvault.SecretAttributes{
+ Expires: &oldExpiry,
+ },
+ }
+ smtc.setSecretOutput = keyvault.SecretBundle{
+ Tags: map[string]*string{
+ managedBy: pointer.To(externalSecrets),
},
Value: &goodSecret,
+ Attributes: &keyvault.SecretAttributes{
+ Expires: smtc.newExpiry,
+ },
}
}
secretWrongTags := func(smtc *secretManagerTestCase) {
@@ -421,12 +473,30 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
}
smtc.secretOutput = keyvault.SecretBundle{
Tags: map[string]*string{
- "managed-by": pointer.To("nope"),
+ managedBy: pointer.To("nope"),
},
Value: &goodSecret,
}
smtc.expectError = errNotManaged
}
+ wholeSecretNoKey := func(smtc *secretManagerTestCase) {
+ wholeSecretMap := map[string][]byte{"key1": []byte(`value1`), "key2": []byte(`value2`)}
+ wholeSecretString := `{"key1": "value1", "key2": "value2" }`
+ wholeSecret := &corev1.Secret{Data: wholeSecretMap}
+ smtc.secret = wholeSecret
+ smtc.pushData = testingfake.PushSecretData{
+ RemoteKey: secretName,
+ }
+ smtc.secretOutput = keyvault.SecretBundle{
+ Tags: map[string]*string{
+ managedBy: pointer.To(externalSecrets),
+ },
+ Value: &wholeSecretString,
+ }
+
+ smtc.expectedData = wholeSecretMap
+ }
+
secretNoTags := func(smtc *secretManagerTestCase) {
smtc.setValue = []byte(goodSecret)
smtc.pushData = testingfake.PushSecretData{
@@ -445,7 +515,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
SecretKey: secretKey,
RemoteKey: secretName,
}
- smtc.apiErr = autorest.DetailedError{StatusCode: 404, Method: "GET", Message: "Not Found"}
+ smtc.apiErr = autorest.DetailedError{StatusCode: 404, Method: "GET", Message: notFoundMessage}
}
failedGetSecret := func(smtc *secretManagerTestCase) {
smtc.setValue = []byte(goodSecret)
@@ -453,7 +523,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
SecretKey: secretKey,
RemoteKey: secretName,
}
- smtc.apiErr = autorest.DetailedError{StatusCode: 403, Method: "GET", Message: "Forbidden"}
+ smtc.apiErr = autorest.DetailedError{StatusCode: 403, Method: "GET", Message: forbiddenMessage}
smtc.expectError = errAPI
}
failedNotParseableError := func(smtc *secretManagerTestCase) {
@@ -462,7 +532,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
SecretKey: secretKey,
RemoteKey: secretName,
}
- smtc.apiErr = fmt.Errorf("crash")
+ smtc.apiErr = errors.New("crash")
smtc.expectError = "crash"
}
failedSetSecret := func(smtc *secretManagerTestCase) {
@@ -471,8 +541,8 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
SecretKey: secretKey,
RemoteKey: secretName,
}
- smtc.apiErr = autorest.DetailedError{StatusCode: 404, Method: "GET", Message: "Not Found"}
- smtc.setErr = autorest.DetailedError{StatusCode: 403, Method: "POST", Message: "Forbidden"}
+ smtc.apiErr = autorest.DetailedError{StatusCode: 404, Method: "GET", Message: notFoundMessage}
+ smtc.setErr = autorest.DetailedError{StatusCode: 403, Method: "POST", Message: forbiddenMessage}
smtc.expectError = "could not set secret example-1: #POST: Forbidden: StatusCode=403"
}
keySuccess := func(smtc *secretManagerTestCase) {
@@ -483,7 +553,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
}
smtc.keyOutput = keyvault.KeyBundle{
Tags: map[string]*string{
- "managed-by": pointer.To(managerLabel),
+ managedBy: pointer.To(managerLabel),
},
Key: &keyvault.JSONWebKey{},
}
@@ -496,7 +566,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
}
smtc.keyOutput = keyvault.KeyBundle{
Tags: map[string]*string{
- "managed-by": pointer.To(managerLabel),
+ managedBy: pointer.To(managerLabel),
},
Key: &keyvault.JSONWebKey{},
}
@@ -509,7 +579,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
}
smtc.keyOutput = keyvault.KeyBundle{
Tags: map[string]*string{
- "managed-by": pointer.To(managerLabel),
+ managedBy: pointer.To(managerLabel),
},
Key: &keyvault.JSONWebKey{},
}
@@ -522,7 +592,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
}
smtc.keyOutput = keyvault.KeyBundle{
Tags: map[string]*string{
- "managed-by": pointer.To(managerLabel),
+ managedBy: pointer.To(managerLabel),
},
Key: &keyvault.JSONWebKey{},
}
@@ -535,7 +605,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
}
smtc.keyOutput = keyvault.KeyBundle{
Tags: map[string]*string{
- "managed-by": pointer.To(managerLabel),
+ managedBy: pointer.To(managerLabel),
},
Key: &keyvault.JSONWebKey{},
}
@@ -562,7 +632,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
}
smtc.keyOutput = keyvault.KeyBundle{
Tags: map[string]*string{
- "managed-by": pointer.To("internal-secrets"),
+ managedBy: pointer.To("internal-secrets"),
},
Key: &keyvault.JSONWebKey{},
}
@@ -574,7 +644,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
SecretKey: secretKey,
RemoteKey: keyName,
}
- smtc.apiErr = autorest.DetailedError{StatusCode: 403, Method: "GET", Message: "Forbidden"}
+ smtc.apiErr = autorest.DetailedError{StatusCode: 403, Method: "GET", Message: forbiddenMessage}
smtc.expectError = errAPI
}
keyNotFound := func(smtc *secretManagerTestCase) {
@@ -583,7 +653,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
SecretKey: secretKey,
RemoteKey: keyName,
}
- smtc.apiErr = autorest.DetailedError{StatusCode: 404, Method: "GET", Message: "Not Found"}
+ smtc.apiErr = autorest.DetailedError{StatusCode: 404, Method: "GET", Message: notFoundMessage}
smtc.expectError = ""
}
importKeyFailed := func(smtc *secretManagerTestCase) {
@@ -592,8 +662,8 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
SecretKey: secretKey,
RemoteKey: keyName,
}
- smtc.apiErr = autorest.DetailedError{StatusCode: 404, Method: "GET", Message: "Not Found"}
- smtc.setErr = autorest.DetailedError{StatusCode: 403, Method: "POST", Message: "Forbidden"}
+ smtc.apiErr = autorest.DetailedError{StatusCode: 404, Method: "GET", Message: notFoundMessage}
+ smtc.setErr = autorest.DetailedError{StatusCode: 403, Method: "POST", Message: forbiddenMessage}
smtc.expectError = "could not import key keyname: #POST: Forbidden: StatusCode=403"
}
certP12Success := func(smtc *secretManagerTestCase) {
@@ -605,7 +675,33 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
smtc.certOutput = keyvault.CertificateBundle{
X509Thumbprint: pointer.To("123"),
Tags: map[string]*string{
- "managed-by": pointer.To("external-secrets"),
+ managedBy: pointer.To(externalSecrets),
+ },
+ }
+ }
+ certP12ChainLegacySuccess := func(smtc *secretManagerTestCase) {
+ smtc.setValue = p12CertChainLegacy
+ smtc.pushData = testingfake.PushSecretData{
+ SecretKey: secretKey,
+ RemoteKey: certName,
+ }
+ smtc.certOutput = keyvault.CertificateBundle{
+ X509Thumbprint: pointer.To("123"),
+ Tags: map[string]*string{
+ managedBy: pointer.To(externalSecrets),
+ },
+ }
+ }
+ certP12ChainModernSuccess := func(smtc *secretManagerTestCase) {
+ smtc.setValue = p12CertChainModern
+ smtc.pushData = testingfake.PushSecretData{
+ SecretKey: secretKey,
+ RemoteKey: certName,
+ }
+ smtc.certOutput = keyvault.CertificateBundle{
+ X509Thumbprint: pointer.To("123"),
+ Tags: map[string]*string{
+ managedBy: pointer.To(externalSecrets),
},
}
}
@@ -619,7 +715,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
smtc.certOutput = keyvault.CertificateBundle{
X509Thumbprint: pointer.To("123"),
Tags: map[string]*string{
- "managed-by": pointer.To("external-secrets"),
+ managedBy: pointer.To(externalSecrets),
},
}
}
@@ -634,7 +730,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
smtc.certOutput = keyvault.CertificateBundle{
X509Thumbprint: pointer.To("123"),
Tags: map[string]*string{
- "managed-by": pointer.To("external-secrets"),
+ managedBy: pointer.To(externalSecrets),
},
}
}
@@ -649,7 +745,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
smtc.certOutput = keyvault.CertificateBundle{
X509Thumbprint: pointer.To("123"),
Tags: map[string]*string{
- "managed-by": pointer.To("external-secrets"),
+ managedBy: pointer.To(externalSecrets),
},
}
}
@@ -664,7 +760,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
smtc.certOutput = keyvault.CertificateBundle{
X509Thumbprint: pointer.To("123"),
Tags: map[string]*string{
- "managed-by": pointer.To("external-secrets"),
+ managedBy: pointer.To(externalSecrets),
},
}
smtc.expectError = "could not import certificate certname: error"
@@ -681,7 +777,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
smtc.certOutput = keyvault.CertificateBundle{
Cer: &cert,
Tags: map[string]*string{
- "managed-by": pointer.To("external-secrets"),
+ managedBy: pointer.To(externalSecrets),
},
}
}
@@ -695,7 +791,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
smtc.certOutput = keyvault.CertificateBundle{
X509Thumbprint: pointer.To("123"),
Tags: map[string]*string{
- "managed-by": pointer.To("foobar"),
+ managedBy: pointer.To("foobar"),
},
}
smtc.expectError = "certificate certname: not managed by external-secrets"
@@ -744,6 +840,8 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
successCases := []*secretManagerTestCase{
makeValidSecretManagerTestCaseCustom(certP12Success),
+ makeValidSecretManagerTestCaseCustom(certP12ChainLegacySuccess),
+ makeValidSecretManagerTestCaseCustom(certP12ChainModernSuccess),
makeValidSecretManagerTestCaseCustom(certPEMSuccess),
makeValidSecretManagerTestCaseCustom(certPEMWithGarbageSuccess),
makeValidSecretManagerTestCaseCustom(certDERSuccess),
@@ -765,6 +863,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
makeValidSecretManagerTestCaseCustom(wrongTags),
makeValidSecretManagerTestCaseCustom(secretSuccess),
makeValidSecretManagerTestCaseCustom(secretNoChange),
+ makeValidSecretManagerTestCaseCustom(secretExpiryChange),
makeValidSecretManagerTestCaseCustom(secretWrongTags),
makeValidSecretManagerTestCaseCustom(secretNoTags),
makeValidSecretManagerTestCaseCustom(secretNotFound),
@@ -772,6 +871,7 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
makeValidSecretManagerTestCaseCustom(failedNotParseableError),
makeValidSecretManagerTestCaseCustom(failedSetSecret),
makeValidSecretManagerTestCaseCustom(typeNotSupported),
+ makeValidSecretManagerTestCaseCustom(wholeSecretNoKey),
}
sm := Azure{
@@ -779,17 +879,29 @@ func TestAzureKeyVaultPushSecret(t *testing.T) {
}
for k, v := range successCases {
sm.baseClient = v.mockClient
- secret := &corev1.Secret{
- Data: map[string][]byte{
- secretKey: v.setValue,
- },
+ if v.secret == nil {
+ v.secret = &corev1.Secret{
+ Data: map[string][]byte{
+ secretKey: v.setValue,
+ },
+ }
}
- err := sm.PushSecret(context.Background(), secret, v.pushData)
+ err := sm.PushSecret(context.Background(), v.secret, v.pushData)
if !utils.ErrorContains(err, v.expectError) {
if err == nil {
t.Errorf("[%d] unexpected error: , expected: '%s'", k, v.expectError)
} else {
- t.Errorf("[%d] unexpected error: %s, expected: '%s'", k, err.Error(), v.expectError)
+ t.Errorf(unexpectedError, k, err.Error(), v.expectError)
+ }
+ }
+ if len(v.expectedData) > 0 {
+ sm.baseClient = v.mockClient
+ out, err := sm.GetSecretMap(context.Background(), *v.ref)
+ if !utils.ErrorContains(err, v.expectError) {
+ t.Errorf(unexpectedError, k, err.Error(), v.expectError)
+ }
+ if err == nil && !reflect.DeepEqual(out, v.expectedData) {
+ t.Errorf(unexpectedSecretData, k, v.expectedData, out)
}
}
}
@@ -1162,7 +1274,7 @@ func TestAzureKeyVaultSecretManagerGetSecret(t *testing.T) {
sm.baseClient = v.mockClient
out, err := sm.GetSecret(context.Background(), *v.ref)
if !utils.ErrorContains(err, v.expectError) {
- t.Errorf("[%d] unexpected error: %s, expected: '%s'", k, err.Error(), v.expectError)
+ t.Errorf(unexpectedError, k, err.Error(), v.expectError)
}
if string(out) != v.expectedSecret {
t.Errorf("[%d] unexpected secret: expected %s, got %s", k, v.expectedSecret, string(out))
@@ -1321,10 +1433,10 @@ func TestAzureKeyVaultSecretManagerGetSecretMap(t *testing.T) {
sm.baseClient = v.mockClient
out, err := sm.GetSecretMap(context.Background(), *v.ref)
if !utils.ErrorContains(err, v.expectError) {
- t.Errorf("[%d] unexpected error: %s, expected: '%s'", k, err.Error(), v.expectError)
+ t.Errorf(unexpectedError, k, err.Error(), v.expectError)
}
if err == nil && !reflect.DeepEqual(out, v.expectedData) {
- t.Errorf("[%d] unexpected secret data: expected %#v, got %#v", k, v.expectedData, out)
+ t.Errorf(unexpectedSecretData, k, v.expectedData, out)
}
}
}
@@ -1353,11 +1465,8 @@ func TestAzureKeyVaultSecretManagerGetAllSecrets(t *testing.T) {
Attributes: &enabledAtt,
}
- secretList := make([]keyvault.SecretItem, 0)
- secretList = append(secretList, secretItem)
-
list := keyvault.SecretListResult{
- Value: &secretList,
+ Value: &[]keyvault.SecretItem{secretItem},
}
resultPage := keyvault.NewSecretListResultPage(list, getNextPage)
@@ -1385,11 +1494,8 @@ func TestAzureKeyVaultSecretManagerGetAllSecrets(t *testing.T) {
Attributes: &enabledAtt,
}
- secretList := make([]keyvault.SecretItem, 1)
- secretList = append(secretList, secretItemOne, secretItemTwo)
-
list := keyvault.SecretListResult{
- Value: &secretList,
+ Value: &[]keyvault.SecretItem{secretItemOne, secretItemTwo},
}
resultPage := keyvault.NewSecretListResultPage(list, getNextPage)
@@ -1413,11 +1519,8 @@ func TestAzureKeyVaultSecretManagerGetAllSecrets(t *testing.T) {
Tags: map[string]*string{"environment": &environment},
}
- secretList := make([]keyvault.SecretItem, 0)
- secretList = append(secretList, secretItem)
-
list := keyvault.SecretListResult{
- Value: &secretList,
+ Value: &[]keyvault.SecretItem{secretItem},
}
resultPage := keyvault.NewSecretListResultPage(list, getNextPage)
@@ -1443,11 +1546,8 @@ func TestAzureKeyVaultSecretManagerGetAllSecrets(t *testing.T) {
Tags: map[string]*string{"environment": &environment, "author": &author},
}
- secretList := make([]keyvault.SecretItem, 0)
- secretList = append(secretList, secretItem)
-
list := keyvault.SecretListResult{
- Value: &secretList,
+ Value: &[]keyvault.SecretItem{secretItem},
}
resultPage := keyvault.NewSecretListResultPage(list, getNextPage)
@@ -1637,7 +1737,7 @@ func TestAzureKeyVaultSecretExists(t *testing.T) {
}
smtc.secretOutput = keyvault.SecretBundle{
Tags: map[string]*string{
- "managed-by": pointer.To("external-secrets"),
+ "managed-by": pointer.To(externalSecrets),
},
Value: pointer.To("foo"),
}
@@ -1661,7 +1761,7 @@ func TestAzureKeyVaultSecretExists(t *testing.T) {
smtc.pushData = testingfake.PushSecretData{
RemoteKey: secretName,
}
- smtc.apiErr = autorest.DetailedError{StatusCode: 404, Method: "GET", Message: "Not Found"}
+ smtc.apiErr = autorest.DetailedError{StatusCode: 404, Method: "GET", Message: notFoundMessage}
smtc.expectedExistence = false
}
diff --git a/pkg/provider/beyondtrust/provider.go b/pkg/provider/beyondtrust/provider.go
new file mode 100644
index 00000000000..d1ae4cfa152
--- /dev/null
+++ b/pkg/provider/beyondtrust/provider.go
@@ -0,0 +1,428 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implieclient.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package beyondtrust
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "net/url"
+ "strings"
+ "time"
+
+ auth "github.com/BeyondTrust/go-client-library-passwordsafe/api/authentication"
+ "github.com/BeyondTrust/go-client-library-passwordsafe/api/logging"
+ managed_account "github.com/BeyondTrust/go-client-library-passwordsafe/api/managed_account"
+ "github.com/BeyondTrust/go-client-library-passwordsafe/api/secrets"
+ "github.com/BeyondTrust/go-client-library-passwordsafe/api/utils"
+ "github.com/cenkalti/backoff/v4"
+ v1 "k8s.io/api/core/v1"
+ ctrl "sigs.k8s.io/controller-runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ esoClient "github.com/external-secrets/external-secrets/pkg/utils"
+)
+
+const (
+ errNilStore = "nil store found"
+ errMissingStoreSpec = "store is missing spec"
+ errMissingProvider = "storeSpec is missing provider"
+ errInvalidProvider = "invalid provider spec. Missing field in store %s"
+ errInvalidHostURL = "invalid host URL"
+ errNoSuchKeyFmt = "no such key in secret: %q"
+ errInvalidRetrievalPath = "invalid retrieval path. Provide one path, separator and name"
+ errNotImplemented = "not implemented"
+)
+
+var (
+ errSecretRefAndValueConflict = errors.New("cannot specify both secret reference and value")
+ errMissingSecretName = errors.New("must specify a secret name")
+ errMissingSecretKey = errors.New("must specify a secret key")
+ ESOLogger = ctrl.Log.WithName("provider").WithName("beyondtrust")
+ maxFileSecretSizeBytes = 5000000
+)
+
+// Provider is a Password Safe secrets provider implementing NewClient and ValidateStore for the esv1beta1.Provider interface.
+type Provider struct {
+ apiURL string
+ retrievaltype string
+ authenticate auth.AuthenticationObj
+ log logging.LogrLogger
+ separator string
+}
+
+type AuthenticatorInput struct {
+ Config *esv1beta1.BeyondtrustProvider
+ HTTPClientObj utils.HttpClientObj
+ BackoffDefinition *backoff.ExponentialBackOff
+ APIURL string
+ ClientID string
+ ClientSecret string
+ APIKey string
+ Logger *logging.LogrLogger
+ RetryMaxElapsedTimeMinutes int
+}
+
+// Capabilities implements v1beta1.Provider.
+func (*Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
+ return esv1beta1.SecretStoreReadOnly
+}
+
+// Close implements v1beta1.SecretsClient.
+func (*Provider) Close(_ context.Context) error {
+ return nil
+}
+
+// DeleteSecret implements v1beta1.SecretsClient.
+func (*Provider) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
+ return errors.New(errNotImplemented)
+}
+
+// GetSecretMap implements v1beta1.SecretsClient.
+func (*Provider) GetSecretMap(_ context.Context, _ esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
+ return make(map[string][]byte), errors.New(errNotImplemented)
+}
+
+// PushSecret implements v1beta1.SecretsClient.
+func (*Provider) PushSecret(_ context.Context, _ *v1.Secret, _ esv1beta1.PushSecretData) error {
+ return errors.New(errNotImplemented)
+}
+
+// Validate implements v1beta1.SecretsClient.
+func (p *Provider) Validate() (esv1beta1.ValidationResult, error) {
+ timeout := 15 * time.Second
+ clientURL := p.apiURL
+
+ if err := esoClient.NetworkValidate(clientURL, timeout); err != nil {
+ ESOLogger.Error(err, "Network Validate", "clientURL:", clientURL)
+ return esv1beta1.ValidationResultError, err
+ }
+
+ return esv1beta1.ValidationResultReady, nil
+}
+
+func (*Provider) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
+ return false, errors.New(errNotImplemented)
+}
+
+// NewClient this is where we initialize the SecretClient and return it for the controller to use.
+func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
+ config := store.GetSpec().Provider.Beyondtrust
+ logger := logging.NewLogrLogger(&ESOLogger)
+
+ clientID, clientSecret, apiKey, err := loadCredentialsFromConfig(ctx, config, kube, namespace)
+ if err != nil {
+ return nil, fmt.Errorf("error loading credentials: %w", err)
+ }
+
+ certificate, certificateKey, err := loadCertificateFromConfig(ctx, config, kube, namespace)
+ if err != nil {
+ return nil, fmt.Errorf("error loading certificate: %w", err)
+ }
+
+ if err != nil {
+ return nil, fmt.Errorf("error loading secrets: %w", err)
+ }
+
+ clientTimeOutInSeconds, separator, retryMaxElapsedTimeMinutes := getConfigValues(config)
+
+ backoffDefinition := getBackoffDefinition(retryMaxElapsedTimeMinutes)
+
+ params := utils.ValidationParams{
+ ApiKey: apiKey,
+ ClientID: clientID,
+ ClientSecret: clientSecret,
+ ApiUrl: &config.Server.APIURL,
+ ClientTimeOutInSeconds: clientTimeOutInSeconds,
+ Separator: &separator,
+ VerifyCa: config.Server.VerifyCA,
+ Logger: logger,
+ Certificate: certificate,
+ CertificateKey: certificateKey,
+ RetryMaxElapsedTimeMinutes: &retryMaxElapsedTimeMinutes,
+ MaxFileSecretSizeBytes: &maxFileSecretSizeBytes,
+ }
+
+ if err := validateInputs(params); err != nil {
+ return nil, fmt.Errorf("error in Inputs: %w", err)
+ }
+
+ httpClient, err := utils.GetHttpClient(clientTimeOutInSeconds, config.Server.VerifyCA, certificate, certificateKey, logger)
+ if err != nil {
+ return nil, fmt.Errorf("error creating HTTP client: %w", err)
+ }
+
+ authenticatorInput := AuthenticatorInput{
+ Config: config,
+ HTTPClientObj: *httpClient,
+ BackoffDefinition: backoffDefinition,
+ APIURL: config.Server.APIURL,
+ ClientID: clientID,
+ ClientSecret: clientSecret,
+ APIKey: apiKey,
+ Logger: logger,
+ RetryMaxElapsedTimeMinutes: retryMaxElapsedTimeMinutes,
+ }
+
+ authenticate, err := getAuthenticator(authenticatorInput)
+
+ if err != nil {
+ return nil, fmt.Errorf("error authenticating: %w", err)
+ }
+
+ return &Provider{
+ apiURL: config.Server.APIURL,
+ retrievaltype: config.Server.RetrievalType,
+ authenticate: *authenticate,
+ log: *logger,
+ separator: separator,
+ }, nil
+}
+
+func loadCredentialsFromConfig(ctx context.Context, config *esv1beta1.BeyondtrustProvider, kube client.Client, namespace string) (string, string, string, error) {
+ var clientID, clientSecret, apiKey string
+ var err error
+
+ if config.Auth.APIKey != nil {
+ apiKey, err = loadConfigSecret(ctx, config.Auth.APIKey, kube, namespace)
+ if err != nil {
+ return "", "", "", fmt.Errorf("error loading apiKey: %w", err)
+ }
+ } else {
+ clientID, err = loadConfigSecret(ctx, config.Auth.ClientID, kube, namespace)
+ if err != nil {
+ return "", "", "", fmt.Errorf("error loading clientID: %w", err)
+ }
+
+ clientSecret, err = loadConfigSecret(ctx, config.Auth.ClientSecret, kube, namespace)
+ if err != nil {
+ return "", "", "", fmt.Errorf("error loading clientSecret: %w", err)
+ }
+ }
+
+ return clientID, clientSecret, apiKey, nil
+}
+
+func loadCertificateFromConfig(ctx context.Context, config *esv1beta1.BeyondtrustProvider, kube client.Client, namespace string) (string, string, error) {
+ var certificate, certificateKey string
+ var err error
+
+ if config.Auth.Certificate != nil && config.Auth.CertificateKey != nil {
+ certificate, err = loadConfigSecret(ctx, config.Auth.Certificate, kube, namespace)
+ if err != nil {
+ return "", "", fmt.Errorf("error loading Certificate: %w", err)
+ }
+
+ certificateKey, err = loadConfigSecret(ctx, config.Auth.CertificateKey, kube, namespace)
+ if err != nil {
+ return "", "", fmt.Errorf("error loading Certificate Key: %w", err)
+ }
+ }
+
+ return certificate, certificateKey, nil
+}
+
+func getConfigValues(config *esv1beta1.BeyondtrustProvider) (int, string, int) {
+ clientTimeOutInSeconds := 45
+ separator := "/"
+ retryMaxElapsedTimeMinutes := 15
+
+ if config.Server.ClientTimeOutSeconds != 0 {
+ clientTimeOutInSeconds = config.Server.ClientTimeOutSeconds
+ }
+
+ if config.Server.Separator != "" {
+ separator = config.Server.Separator
+ }
+
+ return clientTimeOutInSeconds, separator, retryMaxElapsedTimeMinutes
+}
+
+func getBackoffDefinition(retryMaxElapsedTimeMinutes int) *backoff.ExponentialBackOff {
+ backoffDefinition := backoff.NewExponentialBackOff()
+ backoffDefinition.InitialInterval = 1 * time.Second
+ backoffDefinition.MaxElapsedTime = time.Duration(retryMaxElapsedTimeMinutes) * time.Minute
+ backoffDefinition.RandomizationFactor = 0.5
+
+ return backoffDefinition
+}
+
+func validateInputs(params utils.ValidationParams) error {
+ return utils.ValidateInputs(params)
+}
+
+func getAuthenticator(input AuthenticatorInput) (*auth.AuthenticationObj, error) {
+ parametersObj := auth.AuthenticationParametersObj{
+ HTTPClient: input.HTTPClientObj,
+ BackoffDefinition: input.BackoffDefinition,
+ EndpointURL: input.APIURL,
+ ApiKey: input.APIKey,
+ Logger: input.Logger,
+ RetryMaxElapsedTimeSeconds: input.RetryMaxElapsedTimeMinutes,
+ }
+
+ if input.Config.Auth.APIKey != nil {
+ parametersObj.ApiKey = input.APIKey
+
+ return auth.AuthenticateUsingApiKey(parametersObj)
+ }
+
+ parametersObj.ClientID = input.ClientID
+ parametersObj.ClientSecret = input.ClientSecret
+
+ return auth.Authenticate(parametersObj)
+}
+
+func loadConfigSecret(ctx context.Context, ref *esv1beta1.BeyondTrustProviderSecretRef, kube client.Client, defaultNamespace string) (string, error) {
+ if ref.SecretRef == nil {
+ return ref.Value, nil
+ }
+
+ if err := validateSecretRef(ref); err != nil {
+ return "", err
+ }
+
+ namespace := defaultNamespace
+ if ref.SecretRef.Namespace != nil {
+ namespace = *ref.SecretRef.Namespace
+ }
+
+ ESOLogger.Info("using k8s secret", "name:", ref.SecretRef.Name, "namespace:", namespace)
+ objKey := client.ObjectKey{Namespace: namespace, Name: ref.SecretRef.Name}
+ secret := v1.Secret{}
+ err := kube.Get(ctx, objKey, &secret)
+ if err != nil {
+ return "", err
+ }
+
+ value, ok := secret.Data[ref.SecretRef.Key]
+ if !ok {
+ return "", fmt.Errorf(errNoSuchKeyFmt, ref.SecretRef.Key)
+ }
+
+ return string(value), nil
+}
+
+func validateSecretRef(ref *esv1beta1.BeyondTrustProviderSecretRef) error {
+ if ref.SecretRef != nil {
+ if ref.Value != "" {
+ return errSecretRefAndValueConflict
+ }
+ if ref.SecretRef.Name == "" {
+ return errMissingSecretName
+ }
+ if ref.SecretRef.Key == "" {
+ return errMissingSecretKey
+ }
+ }
+
+ return nil
+}
+
+func (p *Provider) GetAllSecrets(_ context.Context, _ esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
+ return nil, errors.New("GetAllSecrets not implemented")
+}
+
+// GetSecret reads the secret from the Password Safe server and returns it. The controller uses the value here to
+// create the Kubernetes secret.
+func (p *Provider) GetSecret(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
+ managedAccountType := !strings.EqualFold(p.retrievaltype, "SECRET")
+
+ retrievalPaths := utils.ValidatePaths([]string{ref.Key}, managedAccountType, p.separator, &p.log)
+
+ if len(retrievalPaths) != 1 {
+ return nil, errors.New(errInvalidRetrievalPath)
+ }
+
+ retrievalPath := retrievalPaths[0]
+
+ _, err := p.authenticate.GetPasswordSafeAuthentication()
+ if err != nil {
+ return nil, fmt.Errorf("error getting authentication: %w", err)
+ }
+
+ managedFetch := func() (string, error) {
+ ESOLogger.Info("retrieve managed account value", "retrievalPath:", retrievalPath)
+ manageAccountObj, _ := managed_account.NewManagedAccountObj(p.authenticate, &p.log)
+ return manageAccountObj.GetSecret(retrievalPath, p.separator)
+ }
+ unmanagedFetch := func() (string, error) {
+ ESOLogger.Info("retrieve secrets safe value", "retrievalPath:", retrievalPath)
+ secretObj, _ := secrets.NewSecretObj(p.authenticate, &p.log, maxFileSecretSizeBytes)
+ return secretObj.GetSecret(retrievalPath, p.separator)
+ }
+ fetch := unmanagedFetch
+ if managedAccountType {
+ fetch = managedFetch
+ }
+ returnSecret, err := fetch()
+ if err != nil {
+ if serr := p.authenticate.SignOut(); serr != nil {
+ return nil, errors.Join(err, serr)
+ }
+ return nil, fmt.Errorf("error getting secret/managed account: %w", err)
+ }
+ return []byte(returnSecret), nil
+}
+
+// ValidateStore validates the store configuration to prevent unexpected errors.
+func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnings, error) {
+ if store == nil {
+ return nil, errors.New(errNilStore)
+ }
+
+ spec := store.GetSpec()
+
+ if spec == nil {
+ return nil, errors.New(errMissingStoreSpec)
+ }
+
+ if spec.Provider == nil {
+ return nil, errors.New(errMissingProvider)
+ }
+
+ provider := spec.Provider.Beyondtrust
+ if provider == nil {
+ return nil, fmt.Errorf(errInvalidProvider, store.GetObjectMeta().String())
+ }
+
+ apiURL, err := url.Parse(provider.Server.APIURL)
+ if err != nil {
+ return nil, errors.New(errInvalidHostURL)
+ }
+
+ if provider.Auth.ClientID.SecretRef != nil {
+ return nil, err
+ }
+
+ if provider.Auth.ClientSecret.SecretRef != nil {
+ return nil, err
+ }
+
+ if apiURL.Host == "" {
+ return nil, errors.New(errInvalidHostURL)
+ }
+
+ return nil, nil
+}
+
+// registers the provider object to process on each reconciliation loop.
+func init() {
+ esv1beta1.Register(&Provider{}, &esv1beta1.SecretStoreProvider{
+ Beyondtrust: &esv1beta1.BeyondtrustProvider{},
+ })
+}
diff --git a/pkg/provider/beyondtrust/provider_test.go b/pkg/provider/beyondtrust/provider_test.go
new file mode 100644
index 00000000000..5ec5bda1689
--- /dev/null
+++ b/pkg/provider/beyondtrust/provider_test.go
@@ -0,0 +1,343 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+ http://www.apache.org/licenses/LICENSE-2.0
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implieclient.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package beyondtrust
+
+import (
+ "context"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "k8s.io/client-go/tools/clientcmd"
+ clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
+ kubeclient "sigs.k8s.io/controller-runtime/pkg/client"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+)
+
+const (
+ errTestCase = "Test case Failed"
+ fakeAPIURL = "https://example.com:443/BeyondTrust/api/public/v3/"
+ apiKey = "fakeapikey00fakeapikeydd0000000000065b010f20fakeapikey0000000008700000a93fb5d74fddc0000000000000000000000000000000000000;runas=test_user"
+ clientID = "12345678-25fg-4b05-9ced-35e7dd5093ae"
+ clientSecret = "12345678-25fg-4b05-9ced-35e7dd5093ae"
+)
+
+func createMockPasswordSafeClient(t *testing.T) kubeclient.Client {
+ server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ switch r.URL.Path {
+ case "/Auth/SignAppin":
+ _, err := w.Write([]byte(`{"UserId":1, "EmailAddress":"fake@beyondtrust.com"}`))
+ if err != nil {
+ t.Error(errTestCase)
+ }
+
+ case "/Auth/Signout":
+ _, err := w.Write([]byte(``))
+ if err != nil {
+ t.Error(errTestCase)
+ }
+
+ case "/secrets-safe/secrets":
+ _, err := w.Write([]byte(`[{"SecretType": "FILE", "Password": "credential_in_sub_3_password","Id": "12345678-07d6-4955-175a-08db047219ce","Title": "credential_in_sub_3"}]`))
+ if err != nil {
+ t.Error(errTestCase)
+ }
+
+ case "/secrets-safe/secrets/12345678-07d6-4955-175a-08db047219ce/file/download":
+ _, err := w.Write([]byte(`fake_password`))
+ if err != nil {
+ t.Error(errTestCase)
+ }
+
+ default:
+ http.NotFound(w, r)
+ }
+ }))
+ t.Cleanup(server.Close)
+
+ clientConfig := clientcmd.NewDefaultClientConfig(clientcmdapi.Config{
+ Clusters: map[string]*clientcmdapi.Cluster{
+ "test": {
+ Server: server.URL,
+ },
+ },
+ AuthInfos: map[string]*clientcmdapi.AuthInfo{
+ "test": {
+ Token: "token",
+ },
+ },
+ Contexts: map[string]*clientcmdapi.Context{
+ "test": {
+ Cluster: "test",
+ AuthInfo: "test",
+ },
+ },
+ CurrentContext: "test",
+ }, &clientcmd.ConfigOverrides{})
+
+ restConfig, err := clientConfig.ClientConfig()
+ assert.Nil(t, err)
+ c, err := kubeclient.New(restConfig, kubeclient.Options{})
+ assert.Nil(t, err)
+
+ return c
+}
+
+func TestNewClient(t *testing.T) {
+ type args struct {
+ store esv1beta1.SecretStore
+ kube kubeclient.Client
+ provider esv1beta1.Provider
+ }
+ tests := []struct {
+ name string
+ nameSpace string
+ args args
+ validateErrorNil bool
+ validateErrorText bool
+ expectedErrorText string
+ }{
+ {
+ name: "Client ok",
+ nameSpace: "test",
+ args: args{
+ store: esv1beta1.SecretStore{
+ Spec: esv1beta1.SecretStoreSpec{
+ Provider: &esv1beta1.SecretStoreProvider{
+ Beyondtrust: &esv1beta1.BeyondtrustProvider{
+ Server: &esv1beta1.BeyondtrustServer{
+ APIURL: fakeAPIURL,
+ RetrievalType: "SECRET",
+ },
+
+ Auth: &esv1beta1.BeyondtrustAuth{
+ ClientID: &esv1beta1.BeyondTrustProviderSecretRef{
+ Value: clientID,
+ },
+ ClientSecret: &esv1beta1.BeyondTrustProviderSecretRef{
+ Value: clientSecret,
+ },
+ },
+ },
+ },
+ },
+ },
+ kube: createMockPasswordSafeClient(t),
+ provider: &Provider{},
+ },
+ validateErrorNil: true,
+ validateErrorText: false,
+ },
+ {
+ name: "Bad Client Id",
+ nameSpace: "test",
+ args: args{
+ store: esv1beta1.SecretStore{
+ Spec: esv1beta1.SecretStoreSpec{
+ Provider: &esv1beta1.SecretStoreProvider{
+ Beyondtrust: &esv1beta1.BeyondtrustProvider{
+ Server: &esv1beta1.BeyondtrustServer{
+ APIURL: fakeAPIURL,
+ RetrievalType: "SECRET",
+ },
+
+ Auth: &esv1beta1.BeyondtrustAuth{
+ ClientID: &esv1beta1.BeyondTrustProviderSecretRef{
+ Value: "6138d050",
+ },
+ ClientSecret: &esv1beta1.BeyondTrustProviderSecretRef{
+ Value: clientSecret,
+ },
+ },
+ },
+ },
+ },
+ },
+ kube: createMockPasswordSafeClient(t),
+ provider: &Provider{},
+ },
+ validateErrorNil: false,
+ validateErrorText: true,
+ expectedErrorText: "error in Inputs: Key: 'UserInputValidaton.ClientId' Error:Field validation for 'ClientId' failed on the 'min' tag",
+ },
+ {
+ name: "Bad Client Secret",
+ nameSpace: "test",
+ args: args{
+ store: esv1beta1.SecretStore{
+ Spec: esv1beta1.SecretStoreSpec{
+ Provider: &esv1beta1.SecretStoreProvider{
+ Beyondtrust: &esv1beta1.BeyondtrustProvider{
+ Server: &esv1beta1.BeyondtrustServer{
+ APIURL: fakeAPIURL,
+ RetrievalType: "SECRET",
+ },
+
+ Auth: &esv1beta1.BeyondtrustAuth{
+ ClientSecret: &esv1beta1.BeyondTrustProviderSecretRef{
+ Value: "8i7U0Yulabon8mTc",
+ },
+ ClientID: &esv1beta1.BeyondTrustProviderSecretRef{
+ Value: clientID,
+ },
+ },
+ },
+ },
+ },
+ },
+ kube: createMockPasswordSafeClient(t),
+ provider: &Provider{},
+ },
+ validateErrorNil: false,
+ validateErrorText: true,
+ expectedErrorText: "error in Inputs: Key: 'UserInputValidaton.ClientSecret' Error:Field validation for 'ClientSecret' failed on the 'min' tag",
+ },
+ {
+ name: "Bad Separator",
+ nameSpace: "test",
+ args: args{
+ store: esv1beta1.SecretStore{
+ Spec: esv1beta1.SecretStoreSpec{
+ Provider: &esv1beta1.SecretStoreProvider{
+ Beyondtrust: &esv1beta1.BeyondtrustProvider{
+ Server: &esv1beta1.BeyondtrustServer{
+ APIURL: fakeAPIURL,
+ Separator: "//",
+ RetrievalType: "SECRET",
+ },
+ Auth: &esv1beta1.BeyondtrustAuth{
+ ClientID: &esv1beta1.BeyondTrustProviderSecretRef{
+ Value: clientID,
+ },
+ ClientSecret: &esv1beta1.BeyondTrustProviderSecretRef{
+ Value: clientSecret,
+ },
+ },
+ },
+ },
+ },
+ },
+ kube: createMockPasswordSafeClient(t),
+ provider: &Provider{},
+ },
+ validateErrorNil: false,
+ validateErrorText: true,
+ expectedErrorText: "error in Inputs: Key: 'UserInputValidaton.Separator' Error:Field validation for 'Separator' failed on the 'max' tag",
+ },
+ {
+ name: "Time Out",
+ nameSpace: "test",
+ args: args{
+ store: esv1beta1.SecretStore{
+ Spec: esv1beta1.SecretStoreSpec{
+ Provider: &esv1beta1.SecretStoreProvider{
+ Beyondtrust: &esv1beta1.BeyondtrustProvider{
+ Server: &esv1beta1.BeyondtrustServer{
+ APIURL: fakeAPIURL,
+ Separator: "/",
+ ClientTimeOutSeconds: 400,
+ RetrievalType: "SECRET",
+ },
+ Auth: &esv1beta1.BeyondtrustAuth{
+ ClientID: &esv1beta1.BeyondTrustProviderSecretRef{
+ Value: clientID,
+ },
+ ClientSecret: &esv1beta1.BeyondTrustProviderSecretRef{
+ Value: clientSecret,
+ },
+ },
+ },
+ },
+ },
+ },
+ kube: createMockPasswordSafeClient(t),
+ provider: &Provider{},
+ },
+ validateErrorNil: false,
+ validateErrorText: true,
+ expectedErrorText: "error in Inputs: Key: 'UserInputValidaton.ClientTimeOutinSeconds' Error:Field validation for 'ClientTimeOutinSeconds' failed on the 'lte' tag",
+ },
+ {
+ name: "ApiKey ok",
+ nameSpace: "test",
+ args: args{
+ store: esv1beta1.SecretStore{
+ Spec: esv1beta1.SecretStoreSpec{
+ Provider: &esv1beta1.SecretStoreProvider{
+ Beyondtrust: &esv1beta1.BeyondtrustProvider{
+ Server: &esv1beta1.BeyondtrustServer{
+ APIURL: fakeAPIURL,
+ RetrievalType: "SECRET",
+ },
+
+ Auth: &esv1beta1.BeyondtrustAuth{
+ APIKey: &esv1beta1.BeyondTrustProviderSecretRef{
+ Value: apiKey,
+ },
+ },
+ },
+ },
+ },
+ },
+ kube: createMockPasswordSafeClient(t),
+ provider: &Provider{},
+ },
+ validateErrorNil: true,
+ validateErrorText: false,
+ },
+ {
+ name: "Bad ApiKey",
+ nameSpace: "test",
+ args: args{
+ store: esv1beta1.SecretStore{
+ Spec: esv1beta1.SecretStoreSpec{
+ Provider: &esv1beta1.SecretStoreProvider{
+ Beyondtrust: &esv1beta1.BeyondtrustProvider{
+ Server: &esv1beta1.BeyondtrustServer{
+ APIURL: fakeAPIURL,
+ RetrievalType: "SECRET",
+ },
+
+ Auth: &esv1beta1.BeyondtrustAuth{
+ APIKey: &esv1beta1.BeyondTrustProviderSecretRef{
+ Value: "bad_api_key",
+ },
+ },
+ },
+ },
+ },
+ },
+ kube: createMockPasswordSafeClient(t),
+ provider: &Provider{},
+ },
+ validateErrorNil: false,
+ validateErrorText: true,
+ expectedErrorText: "error in Inputs: Key: 'UserInputValidaton.ApiKey' Error:Field validation for 'ApiKey' failed on the 'min' tag",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ _, err := tt.args.provider.NewClient(context.Background(), &tt.args.store, tt.args.kube, tt.nameSpace)
+ if err != nil && tt.validateErrorNil {
+ t.Errorf("ProviderBeyondtrust.NewClient() error = %v", err)
+ }
+
+ if err != nil && tt.validateErrorText {
+ assert.Equal(t, err.Error(), tt.expectedErrorText)
+ }
+ })
+ }
+}
diff --git a/pkg/provider/bitwarden/bitwarden_sdk.go b/pkg/provider/bitwarden/bitwarden_sdk.go
new file mode 100644
index 00000000000..3adb090ac72
--- /dev/null
+++ b/pkg/provider/bitwarden/bitwarden_sdk.go
@@ -0,0 +1,261 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package bitwarden
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "strings"
+
+ "sigs.k8s.io/controller-runtime/pkg/client"
+
+ "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+)
+
+// Defined Header Keys.
+const (
+ WardenHeaderAccessToken = "Warden-Access-Token"
+ WardenHeaderAPIURL = "Warden-Api-Url"
+ WardenHeaderIdentityURL = "Warden-Identity-Url"
+
+ restAPIURL = "/rest/api/1/secret"
+)
+
+type SecretResponse struct {
+ CreationDate string `json:"creationDate"`
+ ID string `json:"id"`
+ Key string `json:"key"`
+ Note string `json:"note"`
+ OrganizationID string `json:"organizationId"`
+ ProjectID *string `json:"projectId,omitempty"`
+ RevisionDate string `json:"revisionDate"`
+ Value string `json:"value"`
+}
+
+type SecretsDeleteResponse struct {
+ Data []SecretDeleteResponse `json:"data"`
+}
+
+type SecretDeleteResponse struct {
+ Error *string `json:"error,omitempty"`
+ ID string `json:"id"`
+}
+
+type SecretIdentifiersResponse struct {
+ Data []SecretIdentifierResponse `json:"data"`
+}
+
+type SecretIdentifierResponse struct {
+ ID string `json:"id"`
+ Key string `json:"key"`
+ OrganizationID string `json:"organizationId"`
+}
+
+type SecretCreateRequest struct {
+ Key string `json:"key"`
+ Note string `json:"note"`
+ // Organization where the secret will be created
+ OrganizationID string `json:"organizationId"`
+ // IDs of the projects that this secret will belong to
+ ProjectIDS []string `json:"projectIds,omitempty"`
+ Value string `json:"value"`
+}
+
+type SecretPutRequest struct {
+ ID string `json:"id"`
+ Key string `json:"key"`
+ Note string `json:"note"`
+ // Organization where the secret will be created
+ OrganizationID string `json:"organizationId"`
+ // IDs of the projects that this secret will belong to
+ ProjectIDS []string `json:"projectIds,omitempty"`
+ Value string `json:"value"`
+}
+
+// Client for the bitwarden SDK.
+type Client interface {
+ GetSecret(ctx context.Context, id string) (*SecretResponse, error)
+ DeleteSecret(ctx context.Context, ids []string) (*SecretsDeleteResponse, error)
+ CreateSecret(ctx context.Context, secret SecretCreateRequest) (*SecretResponse, error)
+ UpdateSecret(ctx context.Context, secret SecretPutRequest) (*SecretResponse, error)
+ ListSecrets(ctx context.Context, organizationID string) (*SecretIdentifiersResponse, error)
+}
+
+// SdkClient creates a client to talk to the bitwarden SDK server.
+type SdkClient struct {
+ apiURL string
+ identityURL string
+ token string
+ bitwardenSdkServerURL string
+
+ client *http.Client
+}
+
+func NewSdkClient(ctx context.Context, c client.Client, storeKind, namespace string, provider *v1beta1.BitwardenSecretsManagerProvider, token string) (*SdkClient, error) {
+ httpsClient, err := newHTTPSClient(ctx, c, storeKind, namespace, provider)
+ if err != nil {
+ return nil, fmt.Errorf("error creating https client: %w", err)
+ }
+
+ return &SdkClient{
+ apiURL: strings.TrimSuffix(provider.APIURL, "/"),
+ identityURL: strings.TrimSuffix(provider.IdentityURL, "/"),
+ bitwardenSdkServerURL: provider.BitwardenServerSDKURL,
+ token: token,
+ client: httpsClient,
+ }, nil
+}
+
+func (s *SdkClient) GetSecret(ctx context.Context, id string) (*SecretResponse, error) {
+ body := struct {
+ ID string `json:"id"`
+ }{
+ ID: id,
+ }
+ secretResp := &SecretResponse{}
+
+ if err := s.performHTTPRequestOperation(ctx, params{
+ method: http.MethodGet,
+ url: s.bitwardenSdkServerURL + restAPIURL,
+ body: body,
+ result: &secretResp,
+ }); err != nil {
+ return nil, fmt.Errorf("failed to get secret: %w", err)
+ }
+
+ return secretResp, nil
+}
+
+func (s *SdkClient) DeleteSecret(ctx context.Context, ids []string) (*SecretsDeleteResponse, error) {
+ body := struct {
+ IDs []string `json:"ids"`
+ }{
+ IDs: ids,
+ }
+
+ secretResp := &SecretsDeleteResponse{}
+ if err := s.performHTTPRequestOperation(ctx, params{
+ method: http.MethodDelete,
+ url: s.bitwardenSdkServerURL + restAPIURL,
+ body: body,
+ result: &secretResp,
+ }); err != nil {
+ return nil, fmt.Errorf("failed to delete secret: %w", err)
+ }
+
+ return secretResp, nil
+}
+
+func (s *SdkClient) CreateSecret(ctx context.Context, createReq SecretCreateRequest) (*SecretResponse, error) {
+ secretResp := &SecretResponse{}
+ if err := s.performHTTPRequestOperation(ctx, params{
+ method: http.MethodPost,
+ url: s.bitwardenSdkServerURL + restAPIURL,
+ body: createReq,
+ result: &secretResp,
+ }); err != nil {
+ return nil, fmt.Errorf("failed to create secret: %w", err)
+ }
+
+ return secretResp, nil
+}
+
+func (s *SdkClient) UpdateSecret(ctx context.Context, putReq SecretPutRequest) (*SecretResponse, error) {
+ secretResp := &SecretResponse{}
+ if err := s.performHTTPRequestOperation(ctx, params{
+ method: http.MethodPut,
+ url: s.bitwardenSdkServerURL + restAPIURL,
+ body: putReq,
+ result: &secretResp,
+ }); err != nil {
+ return nil, fmt.Errorf("failed to update secret: %w", err)
+ }
+
+ return secretResp, nil
+}
+
+func (s *SdkClient) ListSecrets(ctx context.Context, organizationID string) (*SecretIdentifiersResponse, error) {
+ body := struct {
+ ID string `json:"organizationID"`
+ }{
+ ID: organizationID,
+ }
+ secretResp := &SecretIdentifiersResponse{}
+ if err := s.performHTTPRequestOperation(ctx, params{
+ method: http.MethodGet,
+ url: s.bitwardenSdkServerURL + "/rest/api/1/secrets",
+ body: body,
+ result: &secretResp,
+ }); err != nil {
+ return nil, fmt.Errorf("failed to list secrets: %w", err)
+ }
+
+ return secretResp, nil
+}
+
+func (s *SdkClient) constructSdkRequest(ctx context.Context, method, url string, body []byte) (*http.Request, error) {
+ req, err := http.NewRequestWithContext(ctx, method, url, bytes.NewBuffer(body))
+ if err != nil {
+ return nil, fmt.Errorf("failed to construct request: %w", err)
+ }
+
+ req.Header.Set(WardenHeaderAccessToken, s.token)
+ req.Header.Set(WardenHeaderAPIURL, s.apiURL)
+ req.Header.Set(WardenHeaderIdentityURL, s.identityURL)
+
+ return req, nil
+}
+
+type params struct {
+ method string
+ url string
+ body any
+ result any
+}
+
+func (s *SdkClient) performHTTPRequestOperation(ctx context.Context, params params) error {
+ data, err := json.Marshal(params.body)
+ if err != nil {
+ return fmt.Errorf("failed to marshal body: %w", err)
+ }
+
+ req, err := s.constructSdkRequest(ctx, params.method, params.url, data)
+ if err != nil {
+ return fmt.Errorf("failed to construct request: %w", err)
+ }
+
+ resp, err := s.client.Do(req)
+ if err != nil {
+ return fmt.Errorf("failed to do request: %w", err)
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusOK {
+ content, _ := io.ReadAll(resp.Body)
+
+ return fmt.Errorf("failed to perform http request, got response: %s with status code %d", string(content), resp.StatusCode)
+ }
+
+ decoder := json.NewDecoder(resp.Body)
+ if err := decoder.Decode(¶ms.result); err != nil {
+ return fmt.Errorf("failed to decode response: %w", err)
+ }
+
+ return nil
+}
diff --git a/pkg/provider/bitwarden/bitwarden_sdk_test.go b/pkg/provider/bitwarden/bitwarden_sdk_test.go
new file mode 100644
index 00000000000..2906afcc54c
--- /dev/null
+++ b/pkg/provider/bitwarden/bitwarden_sdk_test.go
@@ -0,0 +1,167 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package bitwarden
+
+import (
+ "context"
+ "encoding/json"
+ "net/http"
+ "net/http/httptest"
+ "reflect"
+ "testing"
+)
+
+// The rest of the tests much look the same, it would be nice if I could find a way
+// to nicely unify the tests for all of them.
+
+func TestSdkClientCreateSecret(t *testing.T) {
+ type fields struct {
+ apiURL func(c *httptest.Server) string
+ identityURL func(c *httptest.Server) string
+ bitwardenSdkServerURL func(c *httptest.Server) string
+ token string
+ testServer func(response any) *httptest.Server
+ response any
+ }
+ type args struct {
+ ctx context.Context
+ createReq SecretCreateRequest
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ want *SecretResponse
+ wantErr bool
+ }{
+ {
+ name: "create secret is successful",
+ fields: fields{
+ testServer: func(response any) *httptest.Server {
+ testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ data, err := json.Marshal(response)
+ if err != nil {
+ http.Error(w, err.Error(), http.StatusInternalServerError)
+
+ return
+ }
+
+ w.Write(data)
+ }))
+
+ return testServer
+ },
+ apiURL: func(c *httptest.Server) string {
+ return c.URL
+ },
+ identityURL: func(c *httptest.Server) string {
+ return c.URL
+ },
+ bitwardenSdkServerURL: func(c *httptest.Server) string {
+ return c.URL
+ },
+ token: "token",
+ response: &SecretResponse{
+ ID: "id",
+ Key: "key",
+ Note: "note",
+ OrganizationID: "orgID",
+ RevisionDate: "2024-04-04",
+ Value: "value",
+ },
+ },
+ args: args{
+ ctx: context.Background(),
+ createReq: SecretCreateRequest{
+ Key: "key",
+ Note: "note",
+ OrganizationID: "orgID",
+ ProjectIDS: []string{projectID},
+ Value: "value",
+ },
+ },
+ want: &SecretResponse{
+ ID: "id",
+ Key: "key",
+ Note: "note",
+ OrganizationID: "orgID",
+ RevisionDate: "2024-04-04",
+ Value: "value",
+ },
+ },
+ {
+ name: "create secret fails",
+ fields: fields{
+ testServer: func(response any) *httptest.Server {
+ testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ http.Error(w, "nope", http.StatusInternalServerError)
+ }))
+
+ return testServer
+ },
+ apiURL: func(c *httptest.Server) string {
+ return c.URL
+ },
+ identityURL: func(c *httptest.Server) string {
+ return c.URL
+ },
+ bitwardenSdkServerURL: func(c *httptest.Server) string {
+ return c.URL
+ },
+ token: "token",
+ response: &SecretResponse{
+ ID: "id",
+ Key: "key",
+ Note: "note",
+ OrganizationID: "orgID",
+ RevisionDate: "2024-04-04",
+ Value: "value",
+ },
+ },
+ args: args{
+ ctx: context.Background(),
+ createReq: SecretCreateRequest{
+ Key: "key",
+ Note: "note",
+ OrganizationID: "orgID",
+ ProjectIDS: []string{projectID},
+ Value: "value",
+ },
+ },
+ wantErr: true,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ server := tt.fields.testServer(tt.fields.response)
+ defer server.Close()
+ s := &SdkClient{
+ apiURL: tt.fields.apiURL(server),
+ identityURL: tt.fields.identityURL(server),
+ bitwardenSdkServerURL: tt.fields.bitwardenSdkServerURL(server),
+ token: tt.fields.token,
+ client: server.Client(),
+ }
+ got, err := s.CreateSecret(tt.args.ctx, tt.args.createReq)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("CreateSecret() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("CreateSecret() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
diff --git a/pkg/provider/bitwarden/client.go b/pkg/provider/bitwarden/client.go
new file mode 100644
index 00000000000..8df7131801c
--- /dev/null
+++ b/pkg/provider/bitwarden/client.go
@@ -0,0 +1,379 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package bitwarden
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+
+ "gopkg.in/yaml.v3"
+ corev1 "k8s.io/api/core/v1"
+ "k8s.io/kube-openapi/pkg/validation/strfmt"
+ "k8s.io/utils/ptr"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ "github.com/external-secrets/external-secrets/pkg/utils"
+)
+
+const (
+ // NoteMetadataKey defines the note for the pushed secret.
+ NoteMetadataKey = "note"
+
+ errNoProvider = "store does not have a provider"
+)
+
+var (
+ errFailedToGetAllSecrets = "failed to get all secrets: %w"
+ errFailedToGetSecret = "failed to get secret: %w"
+)
+
+// PushSecret will write a single secret into the provider.
+// Note: We will refuse to overwrite ANY secrets, because we can never be completely
+// sure if it's the same secret we are trying to push. We only have the Name and the value
+// could be different. Therefore, we will always create a new secret. Except if, the value
+// the key, the note, and organization ID all match.
+// We only allow to push to a single project, because GET returns a single project ID
+// the secret belongs to even though technically Create allows multiple projects. This is
+// to ensure that we push to the same project always, and so we can determine reliably that
+// we don't need to push again.
+func (p *Provider) PushSecret(ctx context.Context, secret *corev1.Secret, data esv1beta1.PushSecretData) error {
+ spec := p.store.GetSpec()
+ if spec == nil || spec.Provider == nil {
+ return errors.New(errNoProvider)
+ }
+
+ if data.GetRemoteKey() == "" {
+ return errors.New("remote key must be defined")
+ }
+
+ var (
+ value []byte
+ err error
+ ok bool
+ )
+ if data.GetSecretKey() == "" {
+ decodedMap := make(map[string]string)
+ for k, v := range secret.Data {
+ decodedMap[k] = string(v)
+ }
+ value, err = utils.JSONMarshal(decodedMap)
+
+ if err != nil {
+ return fmt.Errorf("failed to marshal secret data: %w", err)
+ }
+ } else {
+ value, ok = secret.Data[data.GetSecretKey()]
+
+ if !ok {
+ return fmt.Errorf("failed to find secret key in secret with key: %s", data.GetSecretKey())
+ }
+ }
+
+ note, err := utils.FetchValueFromMetadata(NoteMetadataKey, data.GetMetadata(), "")
+ if err != nil {
+ return fmt.Errorf("failed to fetch note from metadata: %w", err)
+ }
+
+ // ListAll Secrets for an organization. If the key matches our key, we GetSecret that and do a compare.
+ remoteSecrets, err := p.bitwardenSdkClient.ListSecrets(ctx, spec.Provider.BitwardenSecretsManager.OrganizationID)
+ if err != nil {
+ return fmt.Errorf(errFailedToGetAllSecrets, err)
+ }
+
+ for _, d := range remoteSecrets.Data {
+ if d.Key != data.GetRemoteKey() {
+ continue
+ }
+
+ sec, err := p.bitwardenSdkClient.GetSecret(ctx, d.ID)
+ if err != nil {
+ return fmt.Errorf("failed to get secret: %w", err)
+ }
+
+ // If all pushed data matches, we won't push this secret.
+ if p.isExactlySameSecret(sec, data.GetRemoteKey(), note, spec.Provider.BitwardenSecretsManager.ProjectID, value) {
+ // we have a complete match, skip pushing.
+ return nil
+ } else if p.isOnlyValueDifferent(sec, data.GetRemoteKey(), note, spec.Provider.BitwardenSecretsManager.ProjectID, value) {
+ // only the value is different, update the existing secret.
+ _, err = p.bitwardenSdkClient.UpdateSecret(ctx, SecretPutRequest{
+ ID: sec.ID,
+ Key: data.GetRemoteKey(),
+ Note: note,
+ OrganizationID: spec.Provider.BitwardenSecretsManager.OrganizationID,
+ ProjectIDS: []string{spec.Provider.BitwardenSecretsManager.ProjectID},
+ Value: string(value),
+ })
+
+ return err
+ }
+ }
+
+ // no matching secret found, let's create it
+ _, err = p.bitwardenSdkClient.CreateSecret(ctx, SecretCreateRequest{
+ Key: data.GetRemoteKey(),
+ Note: note,
+ OrganizationID: spec.Provider.BitwardenSecretsManager.OrganizationID,
+ ProjectIDS: []string{spec.Provider.BitwardenSecretsManager.ProjectID},
+ Value: string(value),
+ })
+
+ return err
+}
+
+func (p *Provider) isExactlySameSecret(sec *SecretResponse, remoteKey, note, projectID string, value []byte) bool {
+ return sec.Key == remoteKey &&
+ sec.Value == string(value) &&
+ sec.Note == note &&
+ ptr.Deref(sec.ProjectID, "") == projectID
+}
+
+func (p *Provider) isOnlyValueDifferent(sec *SecretResponse, remoteKey, note, projectID string, value []byte) bool {
+ return sec.Key == remoteKey &&
+ sec.Value != string(value) &&
+ sec.Note == note &&
+ ptr.Deref(sec.ProjectID, "") == projectID
+}
+
+// GetSecret returns a single secret from the provider.
+func (p *Provider) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
+ if strfmt.IsUUID(ref.Key) {
+ resp, err := p.bitwardenSdkClient.GetSecret(ctx, ref.Key)
+ if err != nil {
+ return nil, fmt.Errorf(errFailedToGetSecret, err)
+ }
+
+ return []byte(resp.Value), nil
+ }
+
+ spec := p.store.GetSpec()
+ if spec == nil || spec.Provider == nil {
+ return nil, errors.New(errNoProvider)
+ }
+
+ secret, err := p.findSecretByRef(ctx, ref.Key, spec.Provider.BitwardenSecretsManager.ProjectID)
+ if err != nil {
+ return nil, fmt.Errorf(errFailedToGetSecret, err)
+ }
+
+ if secret == nil {
+ return nil, fmt.Errorf("no secret found for project id %s and name %s", spec.Provider.BitwardenSecretsManager.ProjectID, ref.Key)
+ }
+
+ // we found our secret, return the value for it
+ return []byte(secret.Value), nil
+}
+
+func (p *Provider) DeleteSecret(ctx context.Context, ref esv1beta1.PushSecretRemoteRef) error {
+ if strfmt.IsUUID(ref.GetRemoteKey()) {
+ return p.deleteSecret(ctx, ref.GetRemoteKey())
+ }
+
+ spec := p.store.GetSpec()
+ if spec == nil || spec.Provider == nil {
+ return errors.New(errNoProvider)
+ }
+
+ secret, err := p.findSecretByRef(ctx, ref.GetRemoteKey(), spec.Provider.BitwardenSecretsManager.ProjectID)
+ if err != nil {
+ return fmt.Errorf(errFailedToGetSecret, err)
+ }
+
+ if secret == nil {
+ return fmt.Errorf("no secret found for project id %s and name %s", spec.Provider.BitwardenSecretsManager.ProjectID, ref.GetRemoteKey())
+ }
+
+ return p.deleteSecret(ctx, secret.ID)
+}
+
+func (p *Provider) deleteSecret(ctx context.Context, id string) error {
+ resp, err := p.bitwardenSdkClient.DeleteSecret(ctx, []string{id})
+ if err != nil {
+ return fmt.Errorf("error deleting secret: %w", err)
+ }
+
+ var errs error
+ for _, data := range resp.Data {
+ if data.Error != nil {
+ errs = errors.Join(errs, fmt.Errorf("error deleting secret with id %s: %s", data.ID, *data.Error))
+ }
+ }
+
+ if errs != nil {
+ return fmt.Errorf("there were one or more errors deleting secrets: %w", errs)
+ }
+ return nil
+}
+
+func (p *Provider) SecretExists(ctx context.Context, ref esv1beta1.PushSecretRemoteRef) (bool, error) {
+ if strfmt.IsUUID(ref.GetRemoteKey()) {
+ _, err := p.bitwardenSdkClient.GetSecret(ctx, ref.GetRemoteKey())
+ if err != nil {
+ return false, fmt.Errorf(errFailedToGetSecret, err)
+ }
+
+ return true, nil
+ }
+
+ spec := p.store.GetSpec()
+ if spec == nil || spec.Provider == nil {
+ return false, errors.New(errNoProvider)
+ }
+
+ secret, err := p.findSecretByRef(ctx, ref.GetRemoteKey(), spec.Provider.BitwardenSecretsManager.ProjectID)
+ if err != nil {
+ return false, fmt.Errorf(errFailedToGetSecret, err)
+ }
+
+ if secret == nil {
+ return false, nil
+ }
+
+ return true, nil
+}
+
+// GetSecretMap returns multiple k/v pairs from the provider.
+func (p *Provider) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
+ data, err := p.GetSecret(ctx, ref)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := yaml.Unmarshal(data, map[string]any{}); err == nil {
+ return p.parseYamlSecretData(data)
+ }
+
+ kv := make(map[string]json.RawMessage)
+ if err := json.Unmarshal(data, &kv); err != nil {
+ return nil, fmt.Errorf("error unmarshalling secret: %w", err)
+ }
+
+ secretData := make(map[string][]byte)
+ for k, v := range kv {
+ var strVal string
+ err = json.Unmarshal(v, &strVal)
+ if err == nil {
+ secretData[k] = []byte(strVal)
+ } else {
+ secretData[k] = v
+ }
+ }
+
+ return secretData, nil
+}
+
+func (p *Provider) parseYamlSecretData(data []byte) (map[string][]byte, error) {
+ kv := make(map[string]any)
+ if err := yaml.Unmarshal(data, &kv); err != nil {
+ return nil, fmt.Errorf("error unmarshalling secret: %w", err)
+ }
+
+ secretData := make(map[string][]byte)
+ for k, v := range kv {
+ switch t := v.(type) {
+ case string:
+ secretData[k] = []byte(t)
+ case []byte:
+ secretData[k] = t
+ case map[string]any:
+ d, err := yaml.Marshal(t)
+ if err != nil {
+ return nil, fmt.Errorf("error marshaling secret: %w", err)
+ }
+ secretData[k] = bytes.TrimSpace(d)
+ default:
+ secretData[k] = []byte(fmt.Sprintf("%v", t)) // Convert to string and then []byte
+ }
+ }
+
+ return secretData, nil
+}
+
+// GetAllSecrets gets multiple secrets from the provider and loads into a kubernetes secret.
+// First load all secrets from secretStore path configuration
+// Then, gets secrets from a matching name or matching custom_metadata.
+func (p *Provider) GetAllSecrets(ctx context.Context, _ esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
+ spec := p.store.GetSpec()
+ if spec == nil {
+ return nil, errors.New(errNoProvider)
+ }
+
+ secrets, err := p.bitwardenSdkClient.ListSecrets(ctx, spec.Provider.BitwardenSecretsManager.OrganizationID)
+ if err != nil {
+ return nil, fmt.Errorf(errFailedToGetAllSecrets, err)
+ }
+
+ result := map[string][]byte{}
+ for _, d := range secrets.Data {
+ sec, err := p.bitwardenSdkClient.GetSecret(ctx, d.ID)
+ if err != nil {
+ return nil, fmt.Errorf(errFailedToGetSecret, err)
+ }
+
+ result[d.ID] = []byte(sec.Value)
+ }
+
+ return result, nil
+}
+
+// Validate validates the provider.
+func (p *Provider) Validate() (esv1beta1.ValidationResult, error) {
+ return esv1beta1.ValidationResultReady, nil
+}
+
+// Close closes the provider.
+func (p *Provider) Close(_ context.Context) error {
+ return nil
+}
+
+func (p *Provider) findSecretByRef(ctx context.Context, key, projectID string) (*SecretResponse, error) {
+ spec := p.store.GetSpec()
+ if spec == nil || spec.Provider == nil {
+ return nil, errors.New(errNoProvider)
+ }
+
+ // ListAll Secrets for an organization. If the key matches our key, we GetSecret that and do a compare.
+ secrets, err := p.bitwardenSdkClient.ListSecrets(ctx, spec.Provider.BitwardenSecretsManager.OrganizationID)
+ if err != nil {
+ return nil, fmt.Errorf(errFailedToGetAllSecrets, err)
+ }
+
+ var remoteSecret *SecretResponse
+ for _, d := range secrets.Data {
+ if d.Key != key {
+ continue
+ }
+
+ sec, err := p.bitwardenSdkClient.GetSecret(ctx, d.ID)
+ if err != nil {
+ return nil, fmt.Errorf(errFailedToGetSecret, err)
+ }
+
+ if sec.ProjectID != nil && *sec.ProjectID == projectID {
+ if remoteSecret != nil {
+ return nil, fmt.Errorf("more than one secret found for project %s with key %s", projectID, key)
+ }
+
+ // We don't break here because we WANT TO MAKE SURE that there is ONLY ONE
+ // such secret.
+ remoteSecret = sec
+ }
+ }
+
+ return remoteSecret, nil
+}
diff --git a/pkg/provider/bitwarden/client_test.go b/pkg/provider/bitwarden/client_test.go
new file mode 100644
index 00000000000..f7f1a44ab51
--- /dev/null
+++ b/pkg/provider/bitwarden/client_test.go
@@ -0,0 +1,1075 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package bitwarden
+
+import (
+ "context"
+ "reflect"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ corev1 "k8s.io/api/core/v1"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+ "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
+ "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+)
+
+const (
+ remoteID = "d8f29773-3019-4973-9bbc-66327d077fe2"
+ testKey = "this-is-a-name"
+)
+
+var projectID = "e8fc8f9c-2208-446e-9e89-9bc358f39b47"
+
+func TestProviderDeleteSecret(t *testing.T) {
+ type fields struct {
+ kube client.Client
+ namespace string
+ store v1beta1.GenericStore
+ mock func(c *FakeClient)
+ assertMock func(t *testing.T, c *FakeClient)
+ }
+ type args struct {
+ ctx context.Context
+ ref v1beta1.PushSecretRemoteRef
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ wantErr bool
+ }{
+ {
+ name: "delete secret is successfully with UUID",
+ fields: fields{
+ namespace: "default",
+ store: &v1beta1.SecretStore{
+ Spec: v1beta1.SecretStoreSpec{
+ Provider: &v1beta1.SecretStoreProvider{
+ BitwardenSecretsManager: &v1beta1.BitwardenSecretsManagerProvider{
+ OrganizationID: "orgid",
+ ProjectID: projectID,
+ },
+ },
+ },
+ },
+ mock: func(c *FakeClient) {
+ c.DeleteSecretReturnsOnCallN(0, &SecretsDeleteResponse{})
+ },
+ assertMock: func(t *testing.T, c *FakeClient) {
+ assert.Equal(t, 1, c.deleteSecretCalledN)
+ },
+ },
+ args: args{
+ ctx: context.TODO(),
+ ref: v1alpha1.PushSecretRemoteRef{
+ RemoteKey: remoteID,
+ },
+ },
+ },
+ {
+ name: "delete secret by name",
+ fields: fields{
+ namespace: "default",
+ store: &v1beta1.SecretStore{
+ Spec: v1beta1.SecretStoreSpec{
+ Provider: &v1beta1.SecretStoreProvider{
+ BitwardenSecretsManager: &v1beta1.BitwardenSecretsManagerProvider{
+ OrganizationID: "orgid",
+ ProjectID: projectID,
+ },
+ },
+ },
+ },
+ mock: func(c *FakeClient) {
+ c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
+ Data: []SecretIdentifierResponse{
+ {
+ ID: remoteID,
+ Key: testKey,
+ OrganizationID: "orgid",
+ },
+ },
+ })
+
+ c.GetSecretReturnsOnCallN(0, &SecretResponse{
+ ID: remoteID,
+ Key: "key",
+ Note: "note",
+ OrganizationID: "org",
+ Value: "value",
+ ProjectID: &projectID,
+ })
+ c.DeleteSecretReturnsOnCallN(0, &SecretsDeleteResponse{})
+ },
+ assertMock: func(t *testing.T, c *FakeClient) {
+ assert.Equal(t, 1, c.deleteSecretCalledN)
+ },
+ },
+ args: args{
+ ctx: context.TODO(),
+ ref: v1alpha1.PushSecretRemoteRef{
+ RemoteKey: remoteID,
+ },
+ },
+ },
+ {
+ name: "delete secret by name will not delete if something doesn't match",
+ fields: fields{
+ namespace: "default",
+ store: &v1beta1.SecretStore{
+ Spec: v1beta1.SecretStoreSpec{
+ Provider: &v1beta1.SecretStoreProvider{
+ BitwardenSecretsManager: &v1beta1.BitwardenSecretsManagerProvider{
+ OrganizationID: "orgid",
+ ProjectID: projectID,
+ },
+ },
+ },
+ },
+ mock: func(c *FakeClient) {
+ c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
+ Data: []SecretIdentifierResponse{
+ {
+ ID: remoteID,
+ Key: testKey,
+ OrganizationID: "orgid",
+ },
+ },
+ })
+
+ projectID := "another-project"
+ c.GetSecretReturnsOnCallN(0, &SecretResponse{
+ ID: remoteID,
+ Key: testKey,
+ Note: "note",
+ OrganizationID: "orgid",
+ Value: "value",
+ ProjectID: &projectID,
+ })
+ },
+ assertMock: func(t *testing.T, c *FakeClient) {
+ assert.Equal(t, 0, c.deleteSecretCalledN)
+ },
+ },
+ wantErr: true, // no secret found
+ args: args{
+ ctx: context.TODO(),
+ ref: v1alpha1.PushSecretRemoteRef{
+ RemoteKey: testKey,
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ fakeClient := &FakeClient{}
+ tt.fields.mock(fakeClient)
+
+ p := &Provider{
+ kube: tt.fields.kube,
+ namespace: tt.fields.namespace,
+ store: tt.fields.store,
+ bitwardenSdkClient: fakeClient,
+ }
+ if err := p.DeleteSecret(tt.args.ctx, tt.args.ref); (err != nil) != tt.wantErr {
+ t.Errorf("DeleteSecret() error = %v, wantErr %v", err, tt.wantErr)
+ }
+
+ tt.fields.assertMock(t, fakeClient)
+ })
+ }
+}
+
+func TestProviderGetAllSecrets(t *testing.T) {
+ type fields struct {
+ kube client.Client
+ namespace string
+ store v1beta1.GenericStore
+ mock func(c *FakeClient)
+ }
+ type args struct {
+ ctx context.Context
+ ref v1beta1.ExternalSecretFind
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ want map[string][]byte
+ wantErr bool
+ }{
+ {
+ name: "get all secrets",
+ fields: fields{
+ namespace: "default",
+ store: &v1beta1.SecretStore{
+ Spec: v1beta1.SecretStoreSpec{
+ Provider: &v1beta1.SecretStoreProvider{
+ BitwardenSecretsManager: &v1beta1.BitwardenSecretsManagerProvider{
+ OrganizationID: "orgid",
+ ProjectID: projectID,
+ },
+ },
+ },
+ },
+ mock: func(c *FakeClient) {
+ c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
+ Data: []SecretIdentifierResponse{
+ {
+ ID: remoteID,
+ Key: "key1",
+ OrganizationID: "orgid",
+ },
+ {
+ ID: "7c0d21ec-10d9-4972-bdf8-ec52df99cc86",
+ Key: "key2",
+ OrganizationID: "orgid",
+ },
+ },
+ })
+
+ c.GetSecretReturnsOnCallN(0, &SecretResponse{
+ ID: remoteID,
+ Key: "key1",
+ Value: "value1",
+ })
+ c.GetSecretReturnsOnCallN(1, &SecretResponse{
+ ID: "7c0d21ec-10d9-4972-bdf8-ec52df99cc86",
+ Key: "key2",
+ Value: "value2",
+ })
+ },
+ },
+ args: args{
+ ctx: context.TODO(),
+ ref: v1beta1.ExternalSecretFind{},
+ },
+ want: map[string][]byte{
+ remoteID: []byte("value1"),
+ "7c0d21ec-10d9-4972-bdf8-ec52df99cc86": []byte("value2"),
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ fakeClient := &FakeClient{}
+ tt.fields.mock(fakeClient)
+
+ p := &Provider{
+ kube: tt.fields.kube,
+ namespace: tt.fields.namespace,
+ store: tt.fields.store,
+ bitwardenSdkClient: fakeClient,
+ }
+ got, err := p.GetAllSecrets(tt.args.ctx, tt.args.ref)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("GetAllSecrets() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("GetAllSecrets() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestProviderGetSecret(t *testing.T) {
+ type fields struct {
+ kube func() client.Client
+ namespace string
+ store v1beta1.GenericStore
+ mock func(c *FakeClient)
+ }
+ type args struct {
+ ctx context.Context
+ ref v1beta1.ExternalSecretDataRemoteRef
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ want []byte
+ wantErr bool
+ }{
+ {
+ name: "get secret with UUID",
+ fields: fields{
+ kube: func() client.Client {
+ return fake.NewFakeClient()
+ },
+ namespace: "default",
+ store: &v1beta1.SecretStore{},
+ mock: func(c *FakeClient) {
+ c.GetSecretReturnsOnCallN(0, &SecretResponse{
+ ID: "id",
+ Key: "key",
+ Note: "note",
+ OrganizationID: "org",
+ Value: "value",
+ })
+ },
+ },
+ args: args{
+ ctx: context.Background(),
+ ref: v1beta1.ExternalSecretDataRemoteRef{
+ Key: remoteID,
+ },
+ },
+ want: []byte("value"),
+ },
+ {
+ name: "get secret by name",
+ fields: fields{
+ kube: func() client.Client {
+ return fake.NewFakeClient()
+ },
+ namespace: "default",
+ store: &v1beta1.SecretStore{
+ Spec: v1beta1.SecretStoreSpec{
+ Provider: &v1beta1.SecretStoreProvider{
+ BitwardenSecretsManager: &v1beta1.BitwardenSecretsManagerProvider{
+ OrganizationID: "orgid",
+ ProjectID: projectID,
+ },
+ },
+ },
+ },
+ mock: func(c *FakeClient) {
+ c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
+ Data: []SecretIdentifierResponse{
+ {
+ ID: remoteID,
+ Key: testKey,
+ OrganizationID: "orgid",
+ },
+ },
+ })
+
+ c.GetSecretReturnsOnCallN(0, &SecretResponse{
+ ID: remoteID,
+ Key: "key",
+ Note: "note",
+ OrganizationID: "org",
+ Value: "value",
+ ProjectID: &projectID,
+ })
+ },
+ },
+ args: args{
+ ctx: context.Background(),
+ ref: v1beta1.ExternalSecretDataRemoteRef{
+ Key: testKey,
+ },
+ },
+ want: []byte("value"),
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ fakeClient := &FakeClient{}
+ tt.fields.mock(fakeClient)
+
+ p := &Provider{
+ kube: tt.fields.kube(),
+ namespace: tt.fields.namespace,
+ store: tt.fields.store,
+ bitwardenSdkClient: fakeClient,
+ }
+ got, err := p.GetSecret(tt.args.ctx, tt.args.ref)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("GetSecret() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("GetSecret() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestProviderPushSecret(t *testing.T) {
+ type fields struct {
+ kube func() client.Client
+ namespace string
+ store v1beta1.GenericStore
+ mock func(c *FakeClient)
+ assertMock func(t *testing.T, c *FakeClient)
+ }
+ type args struct {
+ ctx context.Context
+ secret *corev1.Secret
+ data v1beta1.PushSecretData
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ wantErr bool
+ }{
+ {
+ name: "push secret is successful for a none existent remote secret",
+ args: args{
+ ctx: context.Background(),
+ secret: &corev1.Secret{
+ Data: map[string][]byte{
+ "key": []byte("value"),
+ },
+ },
+ data: v1alpha1.PushSecretData{
+ Match: v1alpha1.PushSecretMatch{
+ SecretKey: "key",
+ RemoteRef: v1alpha1.PushSecretRemoteRef{
+ RemoteKey: testKey,
+ },
+ },
+ },
+ },
+ fields: fields{
+ kube: func() client.Client {
+ return fake.NewFakeClient()
+ },
+ namespace: "default",
+ store: &v1beta1.SecretStore{
+ Spec: v1beta1.SecretStoreSpec{
+ Provider: &v1beta1.SecretStoreProvider{
+ BitwardenSecretsManager: &v1beta1.BitwardenSecretsManagerProvider{
+ OrganizationID: "orgid",
+ ProjectID: projectID,
+ },
+ },
+ },
+ },
+ mock: func(c *FakeClient) {
+ c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
+ Data: []SecretIdentifierResponse{
+ {
+ ID: remoteID,
+ Key: testKey,
+ OrganizationID: "orgid",
+ },
+ },
+ })
+ c.GetSecretReturnsOnCallN(0, &SecretResponse{
+ ID: remoteID,
+ Key: "no-match", // if this is this-is-a-name it would match
+ Note: "",
+ OrganizationID: "orgid",
+ Value: "value",
+ ProjectID: &projectID,
+ })
+ c.CreateSecretReturnsOnCallN(0, &SecretResponse{})
+ },
+ assertMock: func(t *testing.T, c *FakeClient) {
+ cargs := c.createSecretCallArguments[0]
+ assert.Equal(t, cargs, SecretCreateRequest{
+ Key: testKey,
+ Note: "",
+ OrganizationID: "orgid",
+ ProjectIDS: []string{projectID},
+ Value: "value",
+ })
+ },
+ },
+ },
+ {
+ name: "push entire secret succeeds",
+ args: args{
+ ctx: context.Background(),
+ secret: &corev1.Secret{
+ Data: map[string][]byte{
+ "key": []byte("value"),
+ },
+ },
+ data: v1alpha1.PushSecretData{
+ Match: v1alpha1.PushSecretMatch{
+ RemoteRef: v1alpha1.PushSecretRemoteRef{
+ RemoteKey: testKey,
+ },
+ },
+ },
+ },
+ fields: fields{
+ kube: func() client.Client {
+ return fake.NewFakeClient()
+ },
+ namespace: "default",
+ store: &v1beta1.SecretStore{
+ Spec: v1beta1.SecretStoreSpec{
+ Provider: &v1beta1.SecretStoreProvider{
+ BitwardenSecretsManager: &v1beta1.BitwardenSecretsManagerProvider{
+ OrganizationID: "orgid",
+ ProjectID: projectID,
+ },
+ },
+ },
+ },
+ mock: func(c *FakeClient) {
+ c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
+ Data: []SecretIdentifierResponse{
+ {
+ ID: remoteID,
+ Key: testKey,
+ OrganizationID: "orgid",
+ },
+ },
+ })
+ c.GetSecretReturnsOnCallN(0, &SecretResponse{
+ ID: remoteID,
+ Key: "no-match", // if this is this-is-a-name it would match
+ Note: "",
+ OrganizationID: "orgid",
+ Value: "value",
+ ProjectID: &projectID,
+ })
+ c.CreateSecretReturnsOnCallN(0, &SecretResponse{})
+ },
+ assertMock: func(t *testing.T, c *FakeClient) {
+ cargs := c.createSecretCallArguments[0]
+ assert.Equal(t, SecretCreateRequest{
+ Key: testKey,
+ Note: "",
+ OrganizationID: "orgid",
+ ProjectIDS: []string{projectID},
+ Value: `{"key":"value"}`,
+ }, cargs)
+ },
+ },
+ },
+ {
+ name: "push secret is successful for an existing remote secret but only the value differs will call update",
+ args: args{
+ ctx: context.Background(),
+ secret: &corev1.Secret{
+ Data: map[string][]byte{
+ "key": []byte("new-value"),
+ },
+ },
+ data: v1alpha1.PushSecretData{
+ Match: v1alpha1.PushSecretMatch{
+ SecretKey: "key",
+ RemoteRef: v1alpha1.PushSecretRemoteRef{
+ RemoteKey: testKey,
+ },
+ },
+ },
+ },
+ fields: fields{
+ kube: func() client.Client {
+ return fake.NewFakeClient()
+ },
+ namespace: "default",
+ store: &v1beta1.SecretStore{
+ Spec: v1beta1.SecretStoreSpec{
+ Provider: &v1beta1.SecretStoreProvider{
+ BitwardenSecretsManager: &v1beta1.BitwardenSecretsManagerProvider{
+ OrganizationID: "orgid",
+ ProjectID: projectID,
+ },
+ },
+ },
+ },
+ mock: func(c *FakeClient) {
+ c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
+ Data: []SecretIdentifierResponse{
+ {
+ ID: remoteID,
+ Key: testKey,
+ OrganizationID: "orgid",
+ },
+ },
+ })
+ c.GetSecretReturnsOnCallN(0, &SecretResponse{
+ ID: remoteID,
+ Key: testKey,
+ Note: "",
+ OrganizationID: "orgid",
+ Value: "value",
+ ProjectID: &projectID,
+ })
+ c.UpdateSecretReturnsOnCallN(0, &SecretResponse{})
+ },
+ assertMock: func(t *testing.T, c *FakeClient) {
+ pargs := c.updateSecretCallArguments[0]
+ assert.Equal(t, pargs, SecretPutRequest{
+ ID: remoteID,
+ Key: testKey,
+ Note: "",
+ OrganizationID: "orgid",
+ ProjectIDS: []string{projectID},
+ Value: "new-value",
+ })
+ },
+ },
+ },
+ {
+ name: "push secret will not push if the same secret already exists",
+ args: args{
+ ctx: context.Background(),
+ secret: &corev1.Secret{
+ Data: map[string][]byte{
+ "key": []byte("value"),
+ },
+ },
+ data: v1alpha1.PushSecretData{
+ Match: v1alpha1.PushSecretMatch{
+ SecretKey: "key",
+ RemoteRef: v1alpha1.PushSecretRemoteRef{
+ RemoteKey: testKey,
+ },
+ },
+ },
+ },
+ fields: fields{
+ kube: func() client.Client {
+ return fake.NewFakeClient()
+ },
+ namespace: "default",
+ store: &v1beta1.SecretStore{
+ Spec: v1beta1.SecretStoreSpec{
+ Provider: &v1beta1.SecretStoreProvider{
+ BitwardenSecretsManager: &v1beta1.BitwardenSecretsManagerProvider{
+ OrganizationID: "orgid",
+ ProjectID: projectID,
+ },
+ },
+ },
+ },
+ mock: func(c *FakeClient) {
+ c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
+ Data: []SecretIdentifierResponse{
+ {
+ ID: remoteID,
+ Key: testKey,
+ OrganizationID: "orgid",
+ },
+ },
+ })
+ c.GetSecretReturnsOnCallN(0, &SecretResponse{
+ ID: remoteID,
+ Key: testKey,
+ OrganizationID: "orgid",
+ Value: "value",
+ ProjectID: &projectID,
+ })
+ c.UpdateSecretReturnsOnCallN(0, &SecretResponse{})
+ },
+ assertMock: func(t *testing.T, c *FakeClient) {
+ assert.Equal(t, 0, c.createSecretCalledN)
+ assert.Equal(t, 0, c.updateSecretCalledN)
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ fakeClient := &FakeClient{}
+ tt.fields.mock(fakeClient)
+
+ p := &Provider{
+ kube: tt.fields.kube(),
+ namespace: tt.fields.namespace,
+ store: tt.fields.store,
+ bitwardenSdkClient: fakeClient,
+ }
+
+ if err := p.PushSecret(tt.args.ctx, tt.args.secret, tt.args.data); (err != nil) != tt.wantErr {
+ t.Errorf("PushSecret() error = %v, wantErr %v", err, tt.wantErr)
+ }
+
+ tt.fields.assertMock(t, fakeClient)
+ })
+ }
+}
+
+func TestProviderSecretExists(t *testing.T) {
+ type fields struct {
+ kube client.Client
+ namespace string
+ store v1beta1.GenericStore
+ mock func(c *FakeClient)
+ assertMock func(t *testing.T, c *FakeClient)
+ }
+ type args struct {
+ ctx context.Context
+ ref v1alpha1.PushSecretData
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ want bool
+ wantErr bool
+ }{
+ {
+ name: "secret exists",
+ fields: fields{
+ store: &v1beta1.SecretStore{
+ Spec: v1beta1.SecretStoreSpec{
+ Provider: &v1beta1.SecretStoreProvider{
+ BitwardenSecretsManager: &v1beta1.BitwardenSecretsManagerProvider{
+ OrganizationID: "orgid",
+ ProjectID: projectID,
+ },
+ },
+ },
+ },
+ mock: func(c *FakeClient) {
+ c.GetSecretReturnsOnCallN(0, &SecretResponse{})
+ },
+ assertMock: func(t *testing.T, c *FakeClient) {
+ assert.Equal(t, 0, c.listSecretsCalledN)
+ },
+ },
+ args: args{
+ ctx: nil,
+ ref: v1alpha1.PushSecretData{
+ Match: v1alpha1.PushSecretMatch{
+ RemoteRef: v1alpha1.PushSecretRemoteRef{
+ RemoteKey: remoteID,
+ },
+ },
+ },
+ },
+ want: true,
+ },
+ {
+ name: "secret exists by name",
+ fields: fields{
+ store: &v1beta1.SecretStore{
+ Spec: v1beta1.SecretStoreSpec{
+ Provider: &v1beta1.SecretStoreProvider{
+ BitwardenSecretsManager: &v1beta1.BitwardenSecretsManagerProvider{
+ OrganizationID: "orgid",
+ ProjectID: projectID,
+ },
+ },
+ },
+ },
+ mock: func(c *FakeClient) {
+ c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
+ Data: []SecretIdentifierResponse{
+ {
+ ID: remoteID,
+ Key: "name",
+ OrganizationID: "orgid",
+ },
+ },
+ })
+ c.GetSecretReturnsOnCallN(0, &SecretResponse{
+ ID: remoteID,
+ Key: "name",
+ OrganizationID: "orgid",
+ Value: "value",
+ ProjectID: &projectID,
+ })
+ },
+ },
+ args: args{
+ ctx: nil,
+ ref: v1alpha1.PushSecretData{
+ Match: v1alpha1.PushSecretMatch{
+ RemoteRef: v1alpha1.PushSecretRemoteRef{
+ RemoteKey: "name",
+ },
+ },
+ },
+ },
+ want: true,
+ },
+ {
+ name: "secret not found by name",
+ fields: fields{
+ store: &v1beta1.SecretStore{
+ Spec: v1beta1.SecretStoreSpec{
+ Provider: &v1beta1.SecretStoreProvider{
+ BitwardenSecretsManager: &v1beta1.BitwardenSecretsManagerProvider{
+ OrganizationID: "orgid",
+ ProjectID: projectID,
+ },
+ },
+ },
+ },
+ mock: func(c *FakeClient) {
+ c.ListSecretReturnsOnCallN(0, &SecretIdentifiersResponse{
+ Data: []SecretIdentifierResponse{
+ {
+ ID: remoteID,
+ Key: "name",
+ OrganizationID: "orgid",
+ },
+ },
+ })
+ projectIDDifferent := "different-project"
+ c.GetSecretReturnsOnCallN(0, &SecretResponse{
+ ID: remoteID,
+ Key: "name",
+ OrganizationID: "orgid",
+ Value: "value",
+ ProjectID: &projectIDDifferent,
+ })
+ },
+ },
+ args: args{
+ ctx: nil,
+ ref: v1alpha1.PushSecretData{
+ Match: v1alpha1.PushSecretMatch{
+ RemoteRef: v1alpha1.PushSecretRemoteRef{
+ RemoteKey: "name",
+ },
+ },
+ },
+ },
+ want: false,
+ },
+ {
+ name: "invalid name format should error",
+ fields: fields{
+ store: &v1beta1.SecretStore{
+ Spec: v1beta1.SecretStoreSpec{
+ Provider: &v1beta1.SecretStoreProvider{
+ BitwardenSecretsManager: &v1beta1.BitwardenSecretsManagerProvider{
+ OrganizationID: "orgid",
+ ProjectID: projectID,
+ },
+ },
+ },
+ },
+ mock: func(c *FakeClient) {
+ // no mocking needed
+ },
+ assertMock: func(t *testing.T, c *FakeClient) {
+ assert.Equal(t, 0, c.listSecretsCalledN)
+ },
+ },
+ args: args{
+ ctx: nil,
+ ref: v1alpha1.PushSecretData{
+ Match: v1alpha1.PushSecretMatch{
+ RemoteRef: v1alpha1.PushSecretRemoteRef{
+ RemoteKey: "name",
+ },
+ },
+ },
+ },
+ want: false,
+ wantErr: true, // invalid remote key format
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ fakeClient := &FakeClient{}
+ tt.fields.mock(fakeClient)
+
+ p := &Provider{
+ kube: tt.fields.kube,
+ namespace: tt.fields.namespace,
+ store: tt.fields.store,
+ bitwardenSdkClient: fakeClient,
+ }
+ got, err := p.SecretExists(tt.args.ctx, tt.args.ref)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("SecretExists() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != tt.want {
+ t.Errorf("SecretExists() got = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func TestProviderGetSecretMap(t *testing.T) {
+ type fields struct {
+ kube func() client.Client
+ namespace string
+ store v1beta1.GenericStore
+ mock func(c *FakeClient)
+ }
+ type args struct {
+ ctx context.Context
+ ref v1beta1.ExternalSecretDataRemoteRef
+ key string
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ want []byte
+ wantErr bool
+ }{
+ {
+ name: "get secret map",
+ fields: fields{
+ kube: func() client.Client {
+ return fake.NewFakeClient()
+ },
+ namespace: "default",
+ store: &v1beta1.SecretStore{},
+ mock: func(c *FakeClient) {
+ c.GetSecretReturnsOnCallN(0, &SecretResponse{
+ ID: remoteID,
+ Key: "key",
+ Note: "note",
+ OrganizationID: "org",
+ Value: `{"key": "value"}`,
+ })
+ },
+ },
+ args: args{
+ ctx: context.Background(),
+ ref: v1beta1.ExternalSecretDataRemoteRef{
+ Key: remoteID,
+ Property: "key",
+ },
+ key: "key",
+ },
+ want: []byte("value"),
+ },
+ {
+ name: "get secret map with yaml",
+ fields: fields{
+ kube: func() client.Client {
+ return fake.NewFakeClient()
+ },
+ namespace: "default",
+ store: &v1beta1.SecretStore{},
+ mock: func(c *FakeClient) {
+ c.GetSecretReturnsOnCallN(0, &SecretResponse{
+ ID: remoteID,
+ Key: "key",
+ Note: "note",
+ OrganizationID: "org",
+ Value: `key: value`,
+ })
+ },
+ },
+ args: args{
+ ctx: context.Background(),
+ ref: v1beta1.ExternalSecretDataRemoteRef{
+ Key: remoteID,
+ Property: "key",
+ },
+ key: "key",
+ },
+ want: []byte("value"),
+ },
+ {
+ name: "get secret map with nested yaml",
+ fields: fields{
+ kube: func() client.Client {
+ return fake.NewFakeClient()
+ },
+ namespace: "default",
+ store: &v1beta1.SecretStore{},
+ mock: func(c *FakeClient) {
+ c.GetSecretReturnsOnCallN(0, &SecretResponse{
+ ID: remoteID,
+ Key: "key",
+ Note: "note",
+ OrganizationID: "org",
+ Value: `key:
+ key2: value`,
+ })
+ },
+ },
+ args: args{
+ ctx: context.Background(),
+ ref: v1beta1.ExternalSecretDataRemoteRef{
+ Key: remoteID,
+ Property: "key",
+ },
+ key: "key",
+ },
+ want: []byte("key2: value"),
+ },
+ {
+ name: "get secret map with binary yaml data",
+ fields: fields{
+ kube: func() client.Client {
+ return fake.NewFakeClient()
+ },
+ namespace: "default",
+ store: &v1beta1.SecretStore{},
+ mock: func(c *FakeClient) {
+ c.GetSecretReturnsOnCallN(0, &SecretResponse{
+ ID: remoteID,
+ Key: "key",
+ Note: "note",
+ OrganizationID: "org",
+ Value: `key: value
+key2: !!binary VGhpcyBpcyBhIHRlc3Q=`,
+ })
+ },
+ },
+ args: args{
+ ctx: context.Background(),
+ ref: v1beta1.ExternalSecretDataRemoteRef{
+ Key: remoteID,
+ Property: "key2",
+ },
+ key: "key2",
+ },
+ want: []byte(`This is a test`),
+ },
+ {
+ name: "get secret map - missing key",
+ fields: fields{
+ kube: func() client.Client {
+ return fake.NewFakeClient()
+ },
+ namespace: "default",
+ store: &v1beta1.SecretStore{},
+ mock: func(c *FakeClient) {
+ c.GetSecretReturnsOnCallN(0, &SecretResponse{
+ ID: remoteID,
+ Key: "key",
+ Note: "note",
+ OrganizationID: "org",
+ Value: `{"key": "value"}`,
+ })
+ },
+ },
+ args: args{
+ ctx: context.Background(),
+ ref: v1beta1.ExternalSecretDataRemoteRef{
+ Key: remoteID,
+ Property: "nope",
+ },
+ },
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ fakeClient := &FakeClient{}
+ tt.fields.mock(fakeClient)
+
+ p := &Provider{
+ kube: tt.fields.kube(),
+ namespace: tt.fields.namespace,
+ store: tt.fields.store,
+ bitwardenSdkClient: fakeClient,
+ }
+ got, err := p.GetSecretMap(tt.args.ctx, tt.args.ref)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("GetSecret() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ assert.Equal(t, tt.want, got[tt.args.key])
+ })
+ }
+}
diff --git a/pkg/provider/bitwarden/fake_client.go b/pkg/provider/bitwarden/fake_client.go
new file mode 100644
index 00000000000..7c8bec2e1e1
--- /dev/null
+++ b/pkg/provider/bitwarden/fake_client.go
@@ -0,0 +1,137 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package bitwarden
+
+import (
+ "context"
+ "fmt"
+)
+
+type FakeClient struct {
+ getSecretCallArguments []string
+ getSecretReturnsOnCall map[int]*SecretResponse
+ getSecretCalledN int
+
+ deleteSecretCallArguments [][]string
+ deleteSecretReturnsOnCall map[int]*SecretsDeleteResponse
+ deleteSecretCalledN int
+
+ createSecretCallArguments []SecretCreateRequest
+ createSecretReturnsOnCall map[int]*SecretResponse
+ createSecretCalledN int
+
+ updateSecretCallArguments []SecretPutRequest
+ updateSecretReturnsOnCall map[int]*SecretResponse
+ updateSecretCalledN int
+
+ listSecretsCallArguments []string
+ listSecretsReturnsOnCall map[int]*SecretIdentifiersResponse
+ listSecretsCalledN int
+}
+
+func (c *FakeClient) GetSecretReturnsOnCallN(call int, ret *SecretResponse) {
+ if c.getSecretReturnsOnCall == nil {
+ c.getSecretReturnsOnCall = make(map[int]*SecretResponse)
+ }
+
+ c.getSecretReturnsOnCall[call] = ret
+}
+
+func (c *FakeClient) GetSecret(ctx context.Context, id string) (*SecretResponse, error) {
+ ret, ok := c.getSecretReturnsOnCall[c.getSecretCalledN]
+ if !ok {
+ return nil, fmt.Errorf("get secret no canned responses set for call %d", c.getSecretCalledN)
+ }
+
+ c.getSecretCallArguments = append(c.getSecretCallArguments, id)
+ c.getSecretCalledN++
+ return ret, nil
+}
+
+func (c *FakeClient) DeleteSecretReturnsOnCallN(call int, ret *SecretsDeleteResponse) {
+ if c.deleteSecretReturnsOnCall == nil {
+ c.deleteSecretReturnsOnCall = make(map[int]*SecretsDeleteResponse)
+ }
+
+ c.deleteSecretReturnsOnCall[call] = ret
+}
+
+func (c *FakeClient) DeleteSecret(ctx context.Context, ids []string) (*SecretsDeleteResponse, error) {
+ ret, ok := c.deleteSecretReturnsOnCall[c.deleteSecretCalledN]
+ if !ok {
+ return nil, fmt.Errorf("delete secret no canned responses set for call %d", c.deleteSecretCalledN)
+ }
+
+ c.deleteSecretCalledN++
+ c.deleteSecretCallArguments = append(c.deleteSecretCallArguments, ids)
+ return ret, nil
+}
+
+func (c *FakeClient) CreateSecretReturnsOnCallN(call int, ret *SecretResponse) {
+ if c.createSecretReturnsOnCall == nil {
+ c.createSecretReturnsOnCall = make(map[int]*SecretResponse)
+ }
+
+ c.createSecretReturnsOnCall[call] = ret
+}
+
+func (c *FakeClient) CreateSecret(ctx context.Context, secret SecretCreateRequest) (*SecretResponse, error) {
+ ret, ok := c.createSecretReturnsOnCall[c.createSecretCalledN]
+ if !ok {
+ return nil, fmt.Errorf("create secret no canned responses set for call %d", c.createSecretCalledN)
+ }
+
+ c.createSecretCalledN++
+ c.createSecretCallArguments = append(c.createSecretCallArguments, secret)
+ return ret, nil
+}
+
+func (c *FakeClient) UpdateSecretReturnsOnCallN(call int, ret *SecretResponse) {
+ if c.updateSecretReturnsOnCall == nil {
+ c.updateSecretReturnsOnCall = make(map[int]*SecretResponse)
+ }
+
+ c.updateSecretReturnsOnCall[call] = ret
+}
+
+func (c *FakeClient) UpdateSecret(ctx context.Context, secret SecretPutRequest) (*SecretResponse, error) {
+ ret, ok := c.updateSecretReturnsOnCall[c.updateSecretCalledN]
+ if !ok {
+ return nil, fmt.Errorf("secret update no canned responses set for call %d", c.updateSecretCalledN)
+ }
+
+ c.updateSecretCalledN++
+ c.updateSecretCallArguments = append(c.updateSecretCallArguments, secret)
+ return ret, nil
+}
+
+func (c *FakeClient) ListSecretReturnsOnCallN(call int, ret *SecretIdentifiersResponse) {
+ if c.listSecretsReturnsOnCall == nil {
+ c.listSecretsReturnsOnCall = make(map[int]*SecretIdentifiersResponse)
+ }
+
+ c.listSecretsReturnsOnCall[call] = ret
+}
+
+func (c *FakeClient) ListSecrets(ctx context.Context, organizationID string) (*SecretIdentifiersResponse, error) {
+ ret, ok := c.listSecretsReturnsOnCall[c.listSecretsCalledN]
+ if !ok {
+ return nil, fmt.Errorf("secret list no canned responses set for call %d", c.listSecretsCalledN)
+ }
+
+ c.listSecretsCalledN++
+ c.listSecretsCallArguments = append(c.listSecretsCallArguments, organizationID)
+ return ret, nil
+}
diff --git a/pkg/provider/bitwarden/provider.go b/pkg/provider/bitwarden/provider.go
new file mode 100644
index 00000000000..1568b51ca94
--- /dev/null
+++ b/pkg/provider/bitwarden/provider.go
@@ -0,0 +1,136 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package bitwarden
+
+import (
+ "context"
+ "crypto/tls"
+ "crypto/x509"
+ "errors"
+ "fmt"
+ "net/http"
+ "time"
+
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ "github.com/external-secrets/external-secrets/pkg/utils"
+ "github.com/external-secrets/external-secrets/pkg/utils/resolvers"
+)
+
+type Provider struct {
+ kube client.Client
+ namespace string
+ store esv1beta1.GenericStore
+ bitwardenSdkClient Client
+}
+
+func init() {
+ esv1beta1.Register(&Provider{}, &esv1beta1.SecretStoreProvider{BitwardenSecretsManager: &esv1beta1.BitwardenSecretsManagerProvider{}})
+}
+
+// NewClient creates a new Bitwarden Secret Manager client.
+func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
+ storeSpec := store.GetSpec()
+ if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.BitwardenSecretsManager == nil {
+ return nil, errors.New("no store type or wrong store type")
+ }
+
+ token, err := resolvers.SecretKeyRef(
+ ctx,
+ kube,
+ store.GetKind(),
+ namespace,
+ &storeSpec.Provider.BitwardenSecretsManager.Auth.SecretRef.Credentials,
+ )
+ if err != nil {
+ return nil, fmt.Errorf("could not resolve auth credentials: %w", err)
+ }
+
+ sdkClient, err := NewSdkClient(ctx,
+ kube,
+ store.GetKind(),
+ namespace,
+ storeSpec.Provider.BitwardenSecretsManager,
+ token,
+ )
+ if err != nil {
+ return nil, fmt.Errorf("could not create SdkClient: %w", err)
+ }
+
+ return &Provider{
+ kube: kube,
+ namespace: namespace,
+ store: store,
+ bitwardenSdkClient: sdkClient,
+ }, nil
+}
+
+// Capabilities returns the provider Capabilities (Read, Write, ReadWrite).
+func (p *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
+ return esv1beta1.SecretStoreReadWrite
+}
+
+// ValidateStore validates the store.
+func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnings, error) {
+ storeSpec := store.GetSpec()
+ if storeSpec == nil {
+ return admission.Warnings{}, errors.New("no store type or wrong store type")
+ }
+
+ if storeSpec.Provider == nil {
+ return admission.Warnings{}, errors.New("provider not configured")
+ }
+
+ bitwardenSpec := storeSpec.Provider.BitwardenSecretsManager
+ if bitwardenSpec == nil {
+ return admission.Warnings{}, errors.New("bitwarden spec not configured")
+ }
+
+ if bitwardenSpec.CAProvider == nil && bitwardenSpec.CABundle == "" {
+ return admission.Warnings{
+ "Neither CA nor CA bundle is configured; user is expected to provide certificate information via volume mount.",
+ }, nil
+ }
+
+ return nil, nil
+}
+
+// newHTTPSClient creates a new HTTPS client with the given cert.
+func newHTTPSClient(ctx context.Context, c client.Client, storeKind, namespace string, provider *esv1beta1.BitwardenSecretsManagerProvider) (*http.Client, error) {
+ cert, err := utils.FetchCACertFromSource(ctx, utils.CreateCertOpts{
+ CABundle: []byte(provider.CABundle),
+ CAProvider: provider.CAProvider,
+ StoreKind: storeKind,
+ Namespace: namespace,
+ Client: c,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ pool := x509.NewCertPool()
+ ok := pool.AppendCertsFromPEM(cert)
+ if !ok {
+ return nil, errors.New("failed to append caBundle")
+ }
+
+ tr := &http.Transport{
+ TLSClientConfig: &tls.Config{RootCAs: pool, MinVersion: tls.VersionTLS12},
+ }
+
+ return &http.Client{Transport: tr, Timeout: time.Second * 10}, nil
+}
diff --git a/pkg/provider/chef/chef.go b/pkg/provider/chef/chef.go
index 24e727ce785..31c6dbfcdf4 100644
--- a/pkg/provider/chef/chef.go
+++ b/pkg/provider/chef/chef.go
@@ -11,11 +11,13 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
+
package chef
import (
"context"
"encoding/json"
+ "errors"
"fmt"
"net/url"
"strings"
@@ -108,7 +110,7 @@ func (providerchef *Providerchef) NewClient(ctx context.Context, store v1beta1.G
if store.GetObjectKind().GroupVersionKind().Kind == v1beta1.ClusterSecretStoreKind {
if chefProvider.Auth.SecretRef.SecretKey.Namespace == nil {
- return nil, fmt.Errorf(errInvalidClusterStoreMissingPKNamespace)
+ return nil, errors.New(errInvalidClusterStoreMissingPKNamespace)
}
objectKey.Namespace = *chefProvider.Auth.SecretRef.SecretKey.Namespace
}
@@ -119,7 +121,7 @@ func (providerchef *Providerchef) NewClient(ctx context.Context, store v1beta1.G
secretKey := credentialsSecret.Data[chefProvider.Auth.SecretRef.SecretKey.Key]
if len(secretKey) == 0 {
- return nil, fmt.Errorf(errMissingSecretKey)
+ return nil, errors.New(errMissingSecretKey)
}
client, err := chef.NewClient(&chef.Config{
@@ -149,20 +151,20 @@ func (providerchef *Providerchef) Validate() (v1beta1.ValidationResult, error) {
_, err := providerchef.userService.Get(providerchef.clientName)
metrics.ObserveAPICall(ProviderChef, CallChefGetUser, err)
if err != nil {
- return v1beta1.ValidationResultError, fmt.Errorf(errStoreValidateFailed)
+ return v1beta1.ValidationResultError, errors.New(errStoreValidateFailed)
}
return v1beta1.ValidationResultReady, nil
}
// GetAllSecrets Retrieves a map[string][]byte with the Databag names as key and the Databag's Items as secrets.
func (providerchef *Providerchef) GetAllSecrets(_ context.Context, _ v1beta1.ExternalSecretFind) (map[string][]byte, error) {
- return nil, fmt.Errorf("dataFrom.find not suppported")
+ return nil, errors.New("dataFrom.find not suppported")
}
// GetSecret returns a databagItem present in the databag. format example: databagName/databagItemName.
func (providerchef *Providerchef) GetSecret(ctx context.Context, ref v1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
if utils.IsNil(providerchef.databagService) {
- return nil, fmt.Errorf(errUninitalizedChefProvider)
+ return nil, errors.New(errUninitalizedChefProvider)
}
key := ref.Key
@@ -178,7 +180,7 @@ func (providerchef *Providerchef) GetSecret(ctx context.Context, ref v1beta1.Ext
return getSingleDatabagItemWithContext(ctx, providerchef, databagName, databagItem, ref.Property)
}
- return nil, fmt.Errorf(errInvalidFormat)
+ return nil, errors.New(errInvalidFormat)
}
func getSingleDatabagItemWithContext(ctx context.Context, providerchef *Providerchef, dataBagName, databagItemName, propertyName string) ([]byte, error) {
@@ -200,7 +202,7 @@ func getSingleDatabagItemWithContext(ctx context.Context, providerchef *Provider
}
jsonByte, err := json.Marshal(ditem)
if err != nil {
- resultChan <- result{err: fmt.Errorf(errUnableToConvertToJSON)}
+ resultChan <- result{err: errors.New(errUnableToConvertToJSON)}
return
}
if propertyName != "" {
@@ -250,12 +252,12 @@ func getPropertyFromDatabagItem(jsonByte []byte, propertyName string) ([]byte, e
// databagItemName or Property not expected in key.
func (providerchef *Providerchef) GetSecretMap(ctx context.Context, ref v1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
if utils.IsNil(providerchef.databagService) {
- return nil, fmt.Errorf(errUninitalizedChefProvider)
+ return nil, errors.New(errUninitalizedChefProvider)
}
databagName := ref.Key
if strings.Contains(databagName, "/") {
- return nil, fmt.Errorf(errInvalidDataform)
+ return nil, errors.New(errInvalidDataform)
}
getAllSecrets := make(map[string][]byte)
providerchef.log.Info("fetching all items from", "databag:", databagName)
@@ -291,38 +293,38 @@ func (providerchef *Providerchef) ValidateStore(store v1beta1.GenericStore) (adm
// getChefProvider validates the incoming store and return the chef provider.
func getChefProvider(store v1beta1.GenericStore) (*v1beta1.ChefProvider, error) {
if store == nil {
- return nil, fmt.Errorf(errMissingStore)
+ return nil, errors.New(errMissingStore)
}
storeSpec := store.GetSpec()
if storeSpec == nil {
- return nil, fmt.Errorf(errMissingStoreSpec)
+ return nil, errors.New(errMissingStoreSpec)
}
provider := storeSpec.Provider
if provider == nil {
- return nil, fmt.Errorf(errMissingProvider)
+ return nil, errors.New(errMissingProvider)
}
chefProvider := storeSpec.Provider.Chef
if chefProvider == nil {
- return nil, fmt.Errorf(errMissingChefProvider)
+ return nil, errors.New(errMissingChefProvider)
}
if chefProvider.UserName == "" {
- return chefProvider, fmt.Errorf(errMissingUserName)
+ return chefProvider, errors.New(errMissingUserName)
}
if chefProvider.ServerURL == "" {
- return chefProvider, fmt.Errorf(errMissingServerURL)
+ return chefProvider, errors.New(errMissingServerURL)
}
if !strings.HasSuffix(chefProvider.ServerURL, "/") {
- return chefProvider, fmt.Errorf(errServerURLNoEndSlash)
+ return chefProvider, errors.New(errServerURLNoEndSlash)
}
// check valid URL
if _, err := url.ParseRequestURI(chefProvider.ServerURL); err != nil {
return chefProvider, fmt.Errorf(errInvalidURL, err)
}
if chefProvider.Auth == nil {
- return chefProvider, fmt.Errorf(errMissingAuth)
+ return chefProvider, errors.New(errMissingAuth)
}
if chefProvider.Auth.SecretRef.SecretKey.Key == "" {
- return chefProvider, fmt.Errorf(errMissingSecretKey)
+ return chefProvider, errors.New(errMissingSecretKey)
}
return chefProvider, nil
@@ -330,16 +332,16 @@ func getChefProvider(store v1beta1.GenericStore) (*v1beta1.ChefProvider, error)
// Not Implemented DeleteSecret.
func (providerchef *Providerchef) DeleteSecret(_ context.Context, _ v1beta1.PushSecretRemoteRef) error {
- return fmt.Errorf(errNotImplemented)
+ return errors.New(errNotImplemented)
}
// Not Implemented PushSecret.
func (providerchef *Providerchef) PushSecret(_ context.Context, _ *corev1.Secret, _ v1beta1.PushSecretData) error {
- return fmt.Errorf(errNotImplemented)
+ return errors.New(errNotImplemented)
}
func (providerchef *Providerchef) SecretExists(_ context.Context, _ v1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf(errNotImplemented)
+ return false, errors.New(errNotImplemented)
}
// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
diff --git a/pkg/provider/chef/chef_test.go b/pkg/provider/chef/chef_test.go
index 107e2471d98..63aceac4a09 100644
--- a/pkg/provider/chef/chef_test.go
+++ b/pkg/provider/chef/chef_test.go
@@ -278,31 +278,31 @@ func TestValidateStore(t *testing.T) {
testCases := []ValidateStoreTestCase{
{
store: makeSecretStore("", baseURL, makeAuth(authName, authNamespace, authKey)),
- err: fmt.Errorf("received invalid Chef SecretStore resource: missing username"),
+ err: errors.New("received invalid Chef SecretStore resource: missing username"),
},
{
store: makeSecretStore(name, "", makeAuth(authName, authNamespace, authKey)),
- err: fmt.Errorf("received invalid Chef SecretStore resource: missing serverurl"),
+ err: errors.New("received invalid Chef SecretStore resource: missing serverurl"),
},
{
store: makeSecretStore(name, baseURL, nil),
- err: fmt.Errorf("received invalid Chef SecretStore resource: cannot initialize Chef Client: no valid authType was specified"),
+ err: errors.New("received invalid Chef SecretStore resource: cannot initialize Chef Client: no valid authType was specified"),
},
{
store: makeSecretStore(name, baseInvalidURL, makeAuth(authName, authNamespace, authKey)),
- err: fmt.Errorf("received invalid Chef SecretStore resource: invalid serverurl: parse \"invalid base URL/\": invalid URI for request"),
+ err: errors.New("received invalid Chef SecretStore resource: invalid serverurl: parse \"invalid base URL/\": invalid URI for request"),
},
{
store: makeSecretStore(name, noEndSlashInvalidBaseURL, makeAuth(authName, authNamespace, authKey)),
- err: fmt.Errorf("received invalid Chef SecretStore resource: serverurl does not end with slash(/)"),
+ err: errors.New("received invalid Chef SecretStore resource: serverurl does not end with slash(/)"),
},
{
store: makeSecretStore(name, baseURL, makeAuth(authName, authNamespace, "")),
- err: fmt.Errorf("received invalid Chef SecretStore resource: missing Secret Key"),
+ err: errors.New("received invalid Chef SecretStore resource: missing Secret Key"),
},
{
store: makeSecretStore(name, baseURL, makeAuth(authName, authNamespace, authKey)),
- err: fmt.Errorf("received invalid Chef SecretStore resource: namespace not allowed with namespaced SecretStore"),
+ err: errors.New("received invalid Chef SecretStore resource: namespace should either be empty or match the namespace of the SecretStore for a namespaced SecretStore"),
},
{
store: &esv1beta1.SecretStore{
@@ -310,7 +310,7 @@ func TestValidateStore(t *testing.T) {
Provider: nil,
},
},
- err: fmt.Errorf("received invalid Chef SecretStore resource: missing provider"),
+ err: errors.New("received invalid Chef SecretStore resource: missing provider"),
},
{
store: &esv1beta1.SecretStore{
@@ -320,7 +320,7 @@ func TestValidateStore(t *testing.T) {
},
},
},
- err: fmt.Errorf("received invalid Chef SecretStore resource: missing chef provider"),
+ err: errors.New("received invalid Chef SecretStore resource: missing chef provider"),
},
}
pc := Providerchef{}
diff --git a/pkg/provider/conjur/auth_jwt.go b/pkg/provider/conjur/auth_jwt.go
index f9c9d1018bc..7bf16ecc142 100644
--- a/pkg/provider/conjur/auth_jwt.go
+++ b/pkg/provider/conjur/auth_jwt.go
@@ -16,13 +16,9 @@ package conjur
import (
"context"
- "crypto/tls"
- "crypto/x509"
+ "errors"
"fmt"
- "net/http"
- "time"
- "github.com/cyberark/conjur-api-go/conjurapi"
authenticationv1 "k8s.io/api/authentication/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -59,7 +55,7 @@ func (c *Client) getJWTToken(ctx context.Context, conjurJWTConfig *esv1beta1.Con
}
return jwtToken, nil
}
- return "", fmt.Errorf("missing ServiceAccountRef or SecretRef")
+ return "", errors.New("missing ServiceAccountRef or SecretRef")
}
// getJwtFromServiceAccountTokenRequest uses the TokenRequest API to get a JWT token for the given service account.
@@ -87,31 +83,3 @@ func (c *Client) getJwtFromServiceAccountTokenRequest(ctx context.Context, servi
}
return tokenResponse.Status.Token, nil
}
-
-// newClientFromJwt creates a new Conjur client using the given JWT Auth Config.
-func (c *Client) newClientFromJwt(ctx context.Context, config conjurapi.Config, jwtAuth *esv1beta1.ConjurJWT) (SecretsClient, error) {
- jwtToken, getJWTError := c.getJWTToken(ctx, jwtAuth)
- if getJWTError != nil {
- return nil, getJWTError
- }
-
- client, clientError := c.clientAPI.NewClientFromJWT(config, jwtToken, jwtAuth.ServiceID, jwtAuth.HostID)
- if clientError != nil {
- return nil, clientError
- }
-
- return client, nil
-}
-
-// newHTTPSClient creates a new HTTPS client with the given cert.
-func newHTTPSClient(cert []byte) (*http.Client, error) {
- pool := x509.NewCertPool()
- ok := pool.AppendCertsFromPEM(cert)
- if !ok {
- return nil, fmt.Errorf("can't append Conjur SSL cert")
- }
- tr := &http.Transport{
- TLSClientConfig: &tls.Config{RootCAs: pool, MinVersion: tls.VersionTLS12},
- }
- return &http.Client{Transport: tr, Timeout: time.Second * 10}, nil
-}
diff --git a/pkg/provider/conjur/client.go b/pkg/provider/conjur/client.go
index 8a413ba660a..3dd0c929d35 100644
--- a/pkg/provider/conjur/client.go
+++ b/pkg/provider/conjur/client.go
@@ -16,8 +16,8 @@ package conjur
import (
"context"
+ "errors"
"fmt"
- "strings"
"github.com/cyberark/conjur-api-go/conjurapi"
"github.com/cyberark/conjur-api-go/conjurapi/authn"
@@ -26,24 +26,17 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
- esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
"github.com/external-secrets/external-secrets/pkg/provider/conjur/util"
"github.com/external-secrets/external-secrets/pkg/utils"
"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
)
var (
- errConjurClient = "cannot setup new Conjur client: %w"
- errBadCertBundle = "caBundle failed to base64 decode: %w"
- errBadServiceUser = "could not get Auth.Apikey.UserRef: %w"
- errBadServiceAPIKey = "could not get Auth.Apikey.ApiKeyRef: %w"
-
+ errConjurClient = "cannot setup new Conjur client: %w"
+ errBadServiceUser = "could not get Auth.Apikey.UserRef: %w"
+ errBadServiceAPIKey = "could not get Auth.Apikey.ApiKeyRef: %w"
errGetKubeSATokenRequest = "cannot request Kubernetes service account token for service account %q: %w"
-
- errUnableToFetchCAProviderCM = "unable to fetch Server.CAProvider ConfigMap: %w"
- errUnableToFetchCAProviderSecret = "unable to fetch Server.CAProvider Secret: %w"
-
- errSecretKeyFmt = "cannot find secret data for key: %q"
+ errSecretKeyFmt = "cannot find secret data for key: %q"
)
// Client is a provider for Conjur.
@@ -68,62 +61,29 @@ func (c *Client) GetConjurClient(ctx context.Context) (SecretsClient, error) {
return nil, err
}
- cert, getCertErr := c.getCA(ctx, prov)
+ cert, getCertErr := utils.FetchCACertFromSource(ctx, utils.CreateCertOpts{
+ CABundle: []byte(prov.CABundle),
+ CAProvider: prov.CAProvider,
+ StoreKind: c.store.GetKind(),
+ Namespace: c.namespace,
+ Client: c.kube,
+ })
if getCertErr != nil {
return nil, getCertErr
}
config := conjurapi.Config{
ApplianceURL: prov.URL,
- SSLCert: cert,
+ SSLCert: string(cert),
}
if prov.Auth.APIKey != nil {
- config.Account = prov.Auth.APIKey.Account
- conjUser, secErr := resolvers.SecretKeyRef(
- ctx,
- c.kube,
- c.StoreKind,
- c.namespace, prov.Auth.APIKey.UserRef)
- if secErr != nil {
- return nil, fmt.Errorf(errBadServiceUser, secErr)
- }
- conjAPIKey, secErr := resolvers.SecretKeyRef(
- ctx,
- c.kube,
- c.StoreKind,
- c.namespace,
- prov.Auth.APIKey.APIKeyRef)
- if secErr != nil {
- return nil, fmt.Errorf(errBadServiceAPIKey, secErr)
- }
-
- conjur, newClientFromKeyError := c.clientAPI.NewClientFromKey(config,
- authn.LoginPair{
- Login: conjUser,
- APIKey: conjAPIKey,
- },
- )
-
- if newClientFromKeyError != nil {
- return nil, fmt.Errorf(errConjurClient, newClientFromKeyError)
- }
- c.client = conjur
- return conjur, nil
+ return c.conjurClientFromAPIKey(ctx, config, prov)
} else if prov.Auth.Jwt != nil {
- config.Account = prov.Auth.Jwt.Account
-
- conjur, clientFromJwtError := c.newClientFromJwt(ctx, config, prov.Auth.Jwt)
- if clientFromJwtError != nil {
- return nil, fmt.Errorf(errConjurClient, clientFromJwtError)
- }
-
- c.client = conjur
-
- return conjur, nil
+ return c.conjurClientFromJWT(ctx, config, prov)
} else {
// Should not happen because validate func should catch this
- return nil, fmt.Errorf("no authentication method provided")
+ return nil, errors.New("no authentication method provided")
}
}
@@ -139,7 +99,7 @@ func (c *Client) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef
}
func (c *Client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf("not implemented")
+ return false, errors.New("not implemented")
}
// Validate validates the provider.
@@ -152,68 +112,58 @@ func (c *Client) Close(_ context.Context) error {
return nil
}
-// configMapKeyRef returns the value of a key in a ConfigMap.
-func (c *Client) configMapKeyRef(ctx context.Context, cmRef *esmeta.SecretKeySelector) (string, error) {
- configMap := &corev1.ConfigMap{}
- ref := client.ObjectKey{
- Namespace: c.namespace,
- Name: cmRef.Name,
- }
- if (c.StoreKind == esv1beta1.ClusterSecretStoreKind) &&
- (cmRef.Namespace != nil) {
- ref.Namespace = *cmRef.Namespace
+func (c *Client) conjurClientFromAPIKey(ctx context.Context, config conjurapi.Config, prov *esv1beta1.ConjurProvider) (SecretsClient, error) {
+ config.Account = prov.Auth.APIKey.Account
+ conjUser, secErr := resolvers.SecretKeyRef(
+ ctx,
+ c.kube,
+ c.StoreKind,
+ c.namespace, prov.Auth.APIKey.UserRef)
+ if secErr != nil {
+ return nil, fmt.Errorf(errBadServiceUser, secErr)
}
- err := c.kube.Get(ctx, ref, configMap)
- if err != nil {
- return "", err
+ conjAPIKey, secErr := resolvers.SecretKeyRef(
+ ctx,
+ c.kube,
+ c.StoreKind,
+ c.namespace,
+ prov.Auth.APIKey.APIKeyRef)
+ if secErr != nil {
+ return nil, fmt.Errorf(errBadServiceAPIKey, secErr)
}
- keyBytes, ok := configMap.Data[cmRef.Key]
- if !ok {
- return "", err
- }
+ conjur, newClientFromKeyError := c.clientAPI.NewClientFromKey(config,
+ authn.LoginPair{
+ Login: conjUser,
+ APIKey: conjAPIKey,
+ },
+ )
- valueStr := strings.TrimSpace(keyBytes)
- return valueStr, nil
+ if newClientFromKeyError != nil {
+ return nil, fmt.Errorf(errConjurClient, newClientFromKeyError)
+ }
+ c.client = conjur
+ return conjur, nil
}
-// getCA try retrieve the CA bundle from the provider CABundle or from the CAProvider.
-func (c *Client) getCA(ctx context.Context, provider *esv1beta1.ConjurProvider) (string, error) {
- if provider.CAProvider != nil {
- var ca string
- var err error
- switch provider.CAProvider.Type {
- case esv1beta1.CAProviderTypeConfigMap:
- keySelector := esmeta.SecretKeySelector{
- Name: provider.CAProvider.Name,
- Namespace: provider.CAProvider.Namespace,
- Key: provider.CAProvider.Key,
- }
- ca, err = c.configMapKeyRef(ctx, &keySelector)
- if err != nil {
- return "", fmt.Errorf(errUnableToFetchCAProviderCM, err)
- }
- case esv1beta1.CAProviderTypeSecret:
- keySelector := esmeta.SecretKeySelector{
- Name: provider.CAProvider.Name,
- Namespace: provider.CAProvider.Namespace,
- Key: provider.CAProvider.Key,
- }
- ca, err = resolvers.SecretKeyRef(
- ctx,
- c.kube,
- c.StoreKind,
- c.namespace,
- &keySelector)
- if err != nil {
- return "", fmt.Errorf(errUnableToFetchCAProviderSecret, err)
- }
- }
- return ca, nil
+func (c *Client) conjurClientFromJWT(ctx context.Context, config conjurapi.Config, prov *esv1beta1.ConjurProvider) (SecretsClient, error) {
+ config.AuthnType = "jwt"
+ config.Account = prov.Auth.Jwt.Account
+ config.JWTHostID = prov.Auth.Jwt.HostID
+ config.ServiceID = prov.Auth.Jwt.ServiceID
+
+ jwtToken, getJWTError := c.getJWTToken(ctx, prov.Auth.Jwt)
+ if getJWTError != nil {
+ return nil, getJWTError
}
- certBytes, decodeErr := utils.Decode(esv1beta1.ExternalSecretDecodeBase64, []byte(provider.CABundle))
- if decodeErr != nil {
- return "", fmt.Errorf(errBadCertBundle, decodeErr)
+
+ config.JWTContent = jwtToken
+
+ conjur, clientError := c.clientAPI.NewClientFromJWT(config)
+ if clientError != nil {
+ return nil, fmt.Errorf(errConjurClient, clientError)
}
- return string(certBytes), nil
+
+ c.client = conjur
+ return conjur, nil
}
diff --git a/pkg/provider/conjur/conjur_api.go b/pkg/provider/conjur/conjur_api.go
index 4a0328361b3..c51ae4e8cf7 100644
--- a/pkg/provider/conjur/conjur_api.go
+++ b/pkg/provider/conjur/conjur_api.go
@@ -15,15 +15,8 @@ limitations under the License.
package conjur
import (
- "fmt"
- "net/http"
- "net/url"
- "strings"
- "time"
-
"github.com/cyberark/conjur-api-go/conjurapi"
"github.com/cyberark/conjur-api-go/conjurapi/authn"
- "github.com/cyberark/conjur-api-go/conjurapi/response"
)
// SecretsClient is an interface for the Conjur client.
@@ -36,7 +29,7 @@ type SecretsClient interface {
// SecretsClientFactory is an interface for creating a Conjur client.
type SecretsClientFactory interface {
NewClientFromKey(config conjurapi.Config, loginPair authn.LoginPair) (SecretsClient, error)
- NewClientFromJWT(config conjurapi.Config, jwtToken string, jwtServiceID, jwtHostID string) (SecretsClient, error)
+ NewClientFromJWT(config conjurapi.Config) (SecretsClient, error)
}
// ClientAPIImpl is an implementation of the ClientAPI interface.
@@ -47,49 +40,6 @@ func (c *ClientAPIImpl) NewClientFromKey(config conjurapi.Config, loginPair auth
}
// NewClientFromJWT creates a new Conjur client from a JWT token.
-// cannot use the built-in function "conjurapi.NewClientFromJwt" because it requires environment variables
-// see: https://github.com/cyberark/conjur-api-go/blob/b698692392a38e5d38b8440f32ab74206544848a/conjurapi/client.go#L130
-func (c *ClientAPIImpl) NewClientFromJWT(config conjurapi.Config, jwtToken, jwtServiceID, jwtHostID string) (SecretsClient, error) {
- jwtTokenString := fmt.Sprintf("jwt=%s", jwtToken)
-
- var httpClient *http.Client
- if config.IsHttps() {
- cert, err := config.ReadSSLCert()
- if err != nil {
- return nil, err
- }
- httpClient, err = newHTTPSClient(cert)
- if err != nil {
- return nil, err
- }
- } else {
- httpClient = &http.Client{Timeout: time.Second * 10}
- }
-
- var authnJwtURL string
- // If a hostID is provided, it must be included in the URL
- if jwtHostID != "" {
- authnJwtURL = strings.Join([]string{config.ApplianceURL, "authn-jwt", jwtServiceID, config.Account, url.PathEscape(jwtHostID), "authenticate"}, "/")
- } else {
- authnJwtURL = strings.Join([]string{config.ApplianceURL, "authn-jwt", jwtServiceID, config.Account, "authenticate"}, "/")
- }
-
- req, err := http.NewRequest("POST", authnJwtURL, strings.NewReader(jwtTokenString))
- if err != nil {
- return nil, err
- }
- req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
-
- resp, err := httpClient.Do(req)
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
-
- tokenBytes, err := response.DataResponse(resp)
- if err != nil {
- return nil, err
- }
-
- return conjurapi.NewClientFromToken(config, string(tokenBytes))
+func (c *ClientAPIImpl) NewClientFromJWT(config conjurapi.Config) (SecretsClient, error) {
+ return conjurapi.NewClientFromJwt(config)
}
diff --git a/pkg/provider/conjur/provider_test.go b/pkg/provider/conjur/provider_test.go
index 500e28d6d21..1235ced3526 100644
--- a/pkg/provider/conjur/provider_test.go
+++ b/pkg/provider/conjur/provider_test.go
@@ -271,7 +271,7 @@ func TestGetAllSecrets(t *testing.T) {
search: "^secret[1,2", // Missing `]`
},
want: want{
- err: fmt.Errorf("could not compile find.name.regexp [%s]: %w", "^secret[1,2", fmt.Errorf("error parsing regexp: missing closing ]: `[1,2`")),
+ err: fmt.Errorf("could not compile find.name.regexp [%s]: %w", "^secret[1,2", errors.New("error parsing regexp: missing closing ]: `[1,2`")),
values: nil,
},
},
@@ -415,7 +415,7 @@ func TestGetSecretMap(t *testing.T) {
},
},
want: want{
- err: fmt.Errorf("%w", fmt.Errorf("error getting secret json_map: cannot find secret data for key: \"key3\"")),
+ err: fmt.Errorf("%w", errors.New("error getting secret json_map: cannot find secret data for key: \"key3\"")),
val: nil,
},
},
@@ -458,8 +458,21 @@ func TestGetCA(t *testing.T) {
want want
}
- certData := "mycertdata"
- certDataEncoded := "bXljZXJ0ZGF0YQo="
+ certData := `-----BEGIN CERTIFICATE-----
+MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw
+CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp
+Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2
+MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
+bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG
+ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS
+7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp
+0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS
+B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49
+BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ
+LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4
+DXZDjC5Ty3zfDBeWUA==
+-----END CERTIFICATE-----`
+ certDataEncoded := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNHVENDQVorZ0F3SUJBZ0lRQ2VDVFphejMyY2k1UGh3TEJDb3U4ekFLQmdncWhrak9QUVFEQXpCT01Rc3cKQ1FZRFZRUUdFd0pWVXpFWE1CVUdBMVVFQ2hNT1JHbG5hVU5sY25Rc0lFbHVZeTR4SmpBa0JnTlZCQU1USFVScApaMmxEWlhKMElGUk1VeUJGUTBNZ1VETTROQ0JTYjI5MElFYzFNQjRYRFRJeE1ERXhOVEF3TURBd01Gb1hEVFEyCk1ERXhOREl6TlRrMU9Wb3dUakVMTUFrR0ExVUVCaE1DVlZNeEZ6QVZCZ05WQkFvVERrUnBaMmxEWlhKMExDQkoKYm1NdU1TWXdKQVlEVlFRREV4MUVhV2RwUTJWeWRDQlVURk1nUlVORElGQXpPRFFnVW05dmRDQkhOVEIyTUJBRwpCeXFHU000OUFnRUdCU3VCQkFBaUEySUFCTUZFb2M4UmwxQ2EzaU9DTlFmTjBNc1luZEx4ZjNjMVR6dmRsSEpTCjdjSTcrT3o2ZTJ0WUlPeVpyc244YUxOMXVkc0o3TWdUOVU3R0NoMW1NRXk3SDBjS1BHRVFRaWw4cFFnTzRDTHAKMHpWb3pwdGpuNFMxbVUxWW9JNzFWT2VWeWFOQ01FQXdIUVlEVlIwT0JCWUVGTUZSUlZCWnF6N25MRnI2SUNJUwpCNENJZkJGcU1BNEdBMVVkRHdFQi93UUVBd0lCaGpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUFvR0NDcUdTTTQ5CkJBTURBMmdBTUdVQ01RQ0phbzFINSt6OGJsVUQyV2RzSms2RHh2M0oreXNUdkxkNmpMUmwwbWxwWXhOak95WlEKTGdHaGVRYVJuVWkvd3I0Q01FZkRGWHV4b0pHWlNaT29QSHpvUmdhTExQSXhBSlNkWXNpSnZSbUVGT21sK3dHNApEWFpEakM1VHkzemZEQmVXVUE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t"
cases := map[string]testCase{
"UseCABundleSuccess": {
@@ -696,7 +709,7 @@ func (c *ConjurMockAPIClient) NewClientFromKey(_ conjurapi.Config, _ authn.Login
return &fake.ConjurMockClient{}, nil
}
-func (c *ConjurMockAPIClient) NewClientFromJWT(_ conjurapi.Config, _, _, _ string) (SecretsClient, error) {
+func (c *ConjurMockAPIClient) NewClientFromJWT(_ conjurapi.Config) (SecretsClient, error) {
return &fake.ConjurMockClient{}, nil
}
diff --git a/pkg/provider/conjur/util/provider.go b/pkg/provider/conjur/util/provider.go
index 374e856c5c4..d6012e62083 100644
--- a/pkg/provider/conjur/util/provider.go
+++ b/pkg/provider/conjur/util/provider.go
@@ -15,6 +15,7 @@ limitations under the License.
package util
import (
+ "errors"
"fmt"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
@@ -31,18 +32,18 @@ const (
// it returns the conjur provider or an error.
func GetConjurProvider(store esv1beta1.GenericStore) (*esv1beta1.ConjurProvider, error) {
if store == nil {
- return nil, fmt.Errorf(errNilStore)
+ return nil, errors.New(errNilStore)
}
spec := store.GetSpec()
if spec == nil {
- return nil, fmt.Errorf(errMissingStoreSpec)
+ return nil, errors.New(errMissingStoreSpec)
}
if spec.Provider == nil {
- return nil, fmt.Errorf(errMissingProvider)
+ return nil, errors.New(errMissingProvider)
}
if spec.Provider.Conjur == nil {
- return nil, fmt.Errorf(errMissingProvider)
+ return nil, errors.New(errMissingProvider)
}
prov := spec.Provider.Conjur
diff --git a/pkg/provider/conjur/validate.go b/pkg/provider/conjur/validate.go
index 5a072a3bad1..aa0fb5cba7e 100644
--- a/pkg/provider/conjur/validate.go
+++ b/pkg/provider/conjur/validate.go
@@ -16,6 +16,7 @@ limitations under the License.
package conjur
import (
+ "errors"
"fmt"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
@@ -33,7 +34,7 @@ func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnin
}
if prov.URL == "" {
- return nil, fmt.Errorf("conjur URL cannot be empty")
+ return nil, errors.New("conjur URL cannot be empty")
}
if prov.Auth.APIKey != nil {
err := validateAPIKeyStore(store, *prov.Auth.APIKey)
@@ -51,7 +52,7 @@ func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnin
// At least one auth must be configured
if prov.Auth.APIKey == nil && prov.Auth.Jwt == nil {
- return nil, fmt.Errorf("missing Auth.* configuration")
+ return nil, errors.New("missing Auth.* configuration")
}
return nil, nil
@@ -59,13 +60,13 @@ func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnin
func validateAPIKeyStore(store esv1beta1.GenericStore, auth esv1beta1.ConjurAPIKey) error {
if auth.Account == "" {
- return fmt.Errorf("missing Auth.ApiKey.Account")
+ return errors.New("missing Auth.ApiKey.Account")
}
if auth.UserRef == nil {
- return fmt.Errorf("missing Auth.Apikey.UserRef")
+ return errors.New("missing Auth.Apikey.UserRef")
}
if auth.APIKeyRef == nil {
- return fmt.Errorf("missing Auth.Apikey.ApiKeyRef")
+ return errors.New("missing Auth.Apikey.ApiKeyRef")
}
if err := utils.ValidateReferentSecretSelector(store, *auth.UserRef); err != nil {
return fmt.Errorf("invalid Auth.Apikey.UserRef: %w", err)
@@ -78,13 +79,13 @@ func validateAPIKeyStore(store esv1beta1.GenericStore, auth esv1beta1.ConjurAPIK
func validateJWTStore(store esv1beta1.GenericStore, auth esv1beta1.ConjurJWT) error {
if auth.Account == "" {
- return fmt.Errorf("missing Auth.Jwt.Account")
+ return errors.New("missing Auth.Jwt.Account")
}
if auth.ServiceID == "" {
- return fmt.Errorf("missing Auth.Jwt.ServiceID")
+ return errors.New("missing Auth.Jwt.ServiceID")
}
if auth.ServiceAccountRef == nil && auth.SecretRef == nil {
- return fmt.Errorf("must specify Auth.Jwt.SecretRef or Auth.Jwt.ServiceAccountRef")
+ return errors.New("must specify Auth.Jwt.SecretRef or Auth.Jwt.ServiceAccountRef")
}
if auth.SecretRef != nil {
if err := utils.ValidateReferentSecretSelector(store, *auth.SecretRef); err != nil {
diff --git a/pkg/provider/conjur/validate_test.go b/pkg/provider/conjur/validate_test.go
index 23a39f6ef87..f860d57de67 100644
--- a/pkg/provider/conjur/validate_test.go
+++ b/pkg/provider/conjur/validate_test.go
@@ -15,7 +15,7 @@ limitations under the License.
package conjur
import (
- "fmt"
+ "errors"
"testing"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
@@ -34,19 +34,19 @@ func TestValidateStore(t *testing.T) {
},
{
store: makeAPIKeySecretStore("", svcUser, svcApikey, svcAccount),
- err: fmt.Errorf("conjur URL cannot be empty"),
+ err: errors.New("conjur URL cannot be empty"),
},
{
store: makeAPIKeySecretStore(svcURL, "", svcApikey, svcAccount),
- err: fmt.Errorf("missing Auth.Apikey.UserRef"),
+ err: errors.New("missing Auth.Apikey.UserRef"),
},
{
store: makeAPIKeySecretStore(svcURL, svcUser, "", svcAccount),
- err: fmt.Errorf("missing Auth.Apikey.ApiKeyRef"),
+ err: errors.New("missing Auth.Apikey.ApiKeyRef"),
},
{
store: makeAPIKeySecretStore(svcURL, svcUser, svcApikey, ""),
- err: fmt.Errorf("missing Auth.ApiKey.Account"),
+ err: errors.New("missing Auth.ApiKey.Account"),
},
{
@@ -59,24 +59,24 @@ func TestValidateStore(t *testing.T) {
},
{
store: makeJWTSecretStore(svcURL, "conjur", "", jwtAuthnService, "", ""),
- err: fmt.Errorf("missing Auth.Jwt.Account"),
+ err: errors.New("missing Auth.Jwt.Account"),
},
{
store: makeJWTSecretStore(svcURL, "conjur", "", "", "", "myconjuraccount"),
- err: fmt.Errorf("missing Auth.Jwt.ServiceID"),
+ err: errors.New("missing Auth.Jwt.ServiceID"),
},
{
store: makeJWTSecretStore("", "conjur", "", jwtAuthnService, "", "myconjuraccount"),
- err: fmt.Errorf("conjur URL cannot be empty"),
+ err: errors.New("conjur URL cannot be empty"),
},
{
store: makeJWTSecretStore(svcURL, "", "", jwtAuthnService, "", "myconjuraccount"),
- err: fmt.Errorf("must specify Auth.Jwt.SecretRef or Auth.Jwt.ServiceAccountRef"),
+ err: errors.New("must specify Auth.Jwt.SecretRef or Auth.Jwt.ServiceAccountRef"),
},
{
store: makeNoAuthSecretStore(svcURL),
- err: fmt.Errorf("missing Auth.* configuration"),
+ err: errors.New("missing Auth.* configuration"),
},
}
p := Provider{}
diff --git a/pkg/provider/device42/device42.go b/pkg/provider/device42/device42.go
new file mode 100644
index 00000000000..00858e64a3b
--- /dev/null
+++ b/pkg/provider/device42/device42.go
@@ -0,0 +1,183 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package device42
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "time"
+
+ corev1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/types"
+ kclient "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ "github.com/external-secrets/external-secrets/pkg/utils"
+)
+
+const (
+ errNotImplemented = "not implemented"
+ errUninitializedProvider = "unable to get device42 client"
+ errCredSecretName = "credentials are empty"
+ errInvalidClusterStoreMissingSAKNamespace = "invalid clusterStore missing SAK namespace"
+ errFetchSAKSecret = "couldn't find secret on cluster: %w"
+ errMissingSAK = "missing credentials while setting auth"
+)
+
+type Client interface {
+ GetSecret(secretID string) (D42Password, error)
+}
+
+// Device42 Provider struct with reference to a Device42 client.
+type Device42 struct {
+ client Client
+}
+
+func (p *Device42) ValidateStore(esv1beta1.GenericStore) (admission.Warnings, error) {
+ return nil, nil
+}
+
+func (p *Device42) Capabilities() esv1beta1.SecretStoreCapabilities {
+ return esv1beta1.SecretStoreReadOnly
+}
+
+// Client for interacting with kubernetes.
+type device42Client struct {
+ kube kclient.Client
+ store *esv1beta1.Device42Provider
+ namespace string
+ storeKind string
+}
+type Provider struct{}
+
+func (c *device42Client) getAuth(ctx context.Context) (string, string, error) {
+ credentialsSecret := &corev1.Secret{}
+ credentialsSecretName := c.store.Auth.SecretRef.Credentials.Name
+ if credentialsSecretName == "" {
+ return "", "", errors.New(errCredSecretName)
+ }
+ objectKey := types.NamespacedName{
+ Name: credentialsSecretName,
+ Namespace: c.namespace,
+ }
+ // only ClusterStore is allowed to set namespace (and then it's required)
+ if c.storeKind == esv1beta1.ClusterSecretStoreKind {
+ if c.store.Auth.SecretRef.Credentials.Namespace == nil {
+ return "", "", errors.New(errInvalidClusterStoreMissingSAKNamespace)
+ }
+ objectKey.Namespace = *c.store.Auth.SecretRef.Credentials.Namespace
+ }
+
+ err := c.kube.Get(ctx, objectKey, credentialsSecret)
+ if err != nil {
+ return "", "", fmt.Errorf(errFetchSAKSecret, err)
+ }
+
+ username := credentialsSecret.Data["username"]
+ password := credentialsSecret.Data["password"]
+ if len(username) == 0 || len(password) == 0 {
+ return "", "", errors.New(errMissingSAK)
+ }
+
+ return string(username), string(password), nil
+}
+
+// NewDevice42Provider returns a reference to a new instance of a 'Device42' struct.
+func NewDevice42Provider() *Device42 {
+ return &Device42{}
+}
+
+func (p *Device42) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
+ storeSpec := store.GetSpec()
+ if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.Device42 == nil {
+ return nil, errors.New("no store type or wrong store type")
+ }
+ storeSpecDevice42 := storeSpec.Provider.Device42
+
+ cliStore := device42Client{
+ kube: kube,
+ store: storeSpecDevice42,
+ namespace: namespace,
+ storeKind: store.GetObjectKind().GroupVersionKind().Kind,
+ }
+
+ username, password, err := cliStore.getAuth(ctx)
+ if err != nil {
+ return nil, err
+ }
+ // Create a new client using credentials and options
+ p.client = NewAPI(storeSpecDevice42.Host, username, password, "443")
+
+ return p, nil
+}
+
+func (p *Device42) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
+ return false, errors.New(errNotImplemented)
+}
+
+func (p *Device42) Validate() (esv1beta1.ValidationResult, error) {
+ timeout := 15 * time.Second
+ url := fmt.Sprintf("https://%s:%s", p.client.(*API).baseURL, p.client.(*API).hostPort)
+
+ if err := utils.NetworkValidate(url, timeout); err != nil {
+ return esv1beta1.ValidationResultError, err
+ }
+ return esv1beta1.ValidationResultReady, nil
+}
+
+func (p *Device42) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
+ return errors.New(errNotImplemented)
+}
+
+func (p *Device42) GetAllSecrets(_ context.Context, _ esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
+ return nil, errors.New(errNotImplemented)
+}
+
+func (p *Device42) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
+ return errors.New(errNotImplemented)
+}
+
+func (p *Device42) GetSecret(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
+ if utils.IsNil(p.client) {
+ return nil, errors.New(errUninitializedProvider)
+ }
+
+ data, err := p.client.GetSecret(ref.Key)
+ if err != nil {
+ return nil, err
+ }
+ return []byte(data.Password), nil
+}
+
+func (p *Device42) GetSecretMap(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
+ data, err := p.client.GetSecret(ref.Key)
+ if err != nil {
+ return nil, fmt.Errorf("error getting secret %s: %w", ref.Key, err)
+ }
+
+ return data.ToMap(), nil
+}
+
+func (p *Device42) Close(_ context.Context) error {
+ return nil
+}
+
+func init() {
+ esv1beta1.Register(&Device42{}, &esv1beta1.SecretStoreProvider{
+ Device42: &esv1beta1.Device42Provider{},
+ })
+}
diff --git a/pkg/provider/device42/device42_api.go b/pkg/provider/device42/device42_api.go
new file mode 100644
index 00000000000..659575a9e0e
--- /dev/null
+++ b/pkg/provider/device42/device42_api.go
@@ -0,0 +1,131 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package device42
+
+import (
+ "bytes"
+ "context"
+ "crypto/tls"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "net/http"
+ "strconv"
+ "time"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+)
+
+const (
+ DoRequestError = "error: do request: %w"
+ errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
+)
+
+type HTTPClient interface {
+ Do(*http.Request) (*http.Response, error)
+}
+
+type API struct {
+ client HTTPClient
+ baseURL string
+ hostPort string
+ password string
+ username string
+}
+
+type D42PasswordResponse struct {
+ Passwords []D42Password
+}
+
+type D42Password struct {
+ Password string `json:"password"`
+ ID int `json:"id"`
+}
+
+func NewAPI(baseURL, username, password, hostPort string) *API {
+ api := &API{
+ baseURL: baseURL,
+ hostPort: hostPort,
+ username: username,
+ password: password,
+ }
+ tr := &http.Transport{
+ TLSClientConfig: &tls.Config{MinVersion: tls.VersionTLS12},
+ }
+
+ api.client = &http.Client{Transport: tr}
+ return api
+}
+
+func (api *API) doAuthenticatedRequest(r *http.Request) (*http.Response, error) {
+ r.SetBasicAuth(api.username, api.password)
+ return api.client.Do(r)
+}
+
+func ReadAndUnmarshal(resp *http.Response, target any) error {
+ var buf bytes.Buffer
+ defer func() {
+ err := resp.Body.Close()
+ if err != nil {
+ return
+ }
+ }()
+ if resp.StatusCode < 200 || resp.StatusCode > 299 {
+ return fmt.Errorf("failed to authenticate with the given credentials: %d %s", resp.StatusCode, buf.String())
+ }
+ _, err := buf.ReadFrom(resp.Body)
+ if err != nil {
+ return err
+ }
+ return json.Unmarshal(buf.Bytes(), target)
+}
+
+func (api *API) GetSecret(secretID string) (D42Password, error) {
+ // https://api.device42.com/#!/Passwords/getPassword
+ endpointURL := fmt.Sprintf("https://%s:%s/api/1.0/passwords/?id=%s&plain_text=yes", api.baseURL, api.hostPort, secretID)
+ ctx, cancel := context.WithTimeout(context.Background(), time.Second*30)
+ defer cancel()
+ readSecretRequest, err := http.NewRequestWithContext(ctx, "GET", endpointURL, http.NoBody)
+ if err != nil {
+ return D42Password{}, fmt.Errorf("error: creating secrets request: %w", err)
+ }
+
+ respSecretRead, err := api.doAuthenticatedRequest(readSecretRequest) //nolint:bodyclose // linters bug
+ if err != nil {
+ return D42Password{}, fmt.Errorf(DoRequestError, err)
+ }
+
+ d42PasswordResponse := D42PasswordResponse{}
+ err = ReadAndUnmarshal(respSecretRead, &d42PasswordResponse)
+ if err != nil {
+ return D42Password{}, fmt.Errorf(errJSONSecretUnmarshal, err)
+ }
+ if len(d42PasswordResponse.Passwords) == 0 {
+ return D42Password{}, err
+ }
+ // There should only be one response
+ return d42PasswordResponse.Passwords[0], err
+}
+
+func (api *API) GetSecretMap(_ context.Context, _ esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
+ return nil, errors.New(errNotImplemented)
+}
+
+func (s D42Password) ToMap() map[string][]byte {
+ m := make(map[string][]byte)
+ m["password"] = []byte(s.Password)
+ m["id"] = []byte(strconv.Itoa(s.ID))
+ return m
+}
diff --git a/pkg/provider/device42/device42_api_test.go b/pkg/provider/device42/device42_api_test.go
new file mode 100644
index 00000000000..6248059c745
--- /dev/null
+++ b/pkg/provider/device42/device42_api_test.go
@@ -0,0 +1,127 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package device42
+
+import (
+ "bytes"
+ "encoding/json"
+ "net/http"
+ "reflect"
+ "testing"
+
+ fakedevice42 "github.com/external-secrets/external-secrets/pkg/provider/device42/fake"
+)
+
+const device42PasswordID = "12345"
+
+func d42PasswordResponse() D42PasswordResponse {
+ return D42PasswordResponse{Passwords: []D42Password{d42Password()}}
+}
+
+func d42Password() D42Password {
+ return D42Password{
+ Password: "test_Password",
+ ID: 12345,
+ }
+}
+
+func TestDevice42ApiGetSecret(t *testing.T) {
+ type fields struct {
+ funcStack []func(req *http.Request) (*http.Response, error)
+ }
+ type args struct {
+ secretID string
+ }
+ tests := []struct {
+ name string
+ fields fields
+ args args
+ want D42Password
+ wantErr bool
+ }{
+ {
+ name: "get secret",
+ fields: fields{
+ funcStack: []func(req *http.Request) (*http.Response, error){
+ createResponder(d42PasswordResponse(), true), //nolint:bodyclose
+ },
+ },
+ args: args{
+ secretID: device42PasswordID,
+ },
+ want: d42Password(),
+ wantErr: false,
+ },
+ {
+ name: "bad response on secret entry",
+ fields: fields{
+ funcStack: []func(req *http.Request) (*http.Response, error){
+ createResponder([]byte("bad response body"), false), //nolint:bodyclose // linters bug
+ },
+ },
+ args: args{
+ secretID: device42PasswordID,
+ },
+ want: D42Password{},
+ wantErr: true,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ api := &API{
+ client: &fakedevice42.MockClient{
+ FuncStack: tt.fields.funcStack,
+ },
+ baseURL: "localhost",
+ hostPort: "8714",
+ password: "test",
+ username: "test",
+ }
+ got, err := api.GetSecret(tt.args.secretID)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("Device42.GetSecret() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got, tt.want) {
+ t.Errorf("Device42.GetSecret() = %v, want %v", got, tt.want)
+ }
+ })
+ }
+}
+
+func createResponder(payload any, withMarshal bool) func(*http.Request) (*http.Response, error) {
+ return func(req *http.Request) (*http.Response, error) {
+ var payloadBytes []byte
+ if withMarshal {
+ payloadBytes, _ = json.Marshal(payload)
+ } else {
+ payloadBytes = payload.([]byte)
+ }
+ res := http.Response{
+ Status: "OK",
+ StatusCode: http.StatusOK,
+ Body: &closeableBuffer{bytes.NewReader(payloadBytes)},
+ }
+ return &res, nil
+ }
+}
+
+type closeableBuffer struct {
+ *bytes.Reader
+}
+
+func (cb *closeableBuffer) Close() error {
+ // Here you can add any cleanup code if needed
+ return nil
+}
diff --git a/pkg/provider/device42/fake/fake.go b/pkg/provider/device42/fake/fake.go
new file mode 100644
index 00000000000..ec7ce9d4739
--- /dev/null
+++ b/pkg/provider/device42/fake/fake.go
@@ -0,0 +1,31 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package fake
+
+import "net/http"
+
+// MockClient is the mock client.
+type MockClient struct {
+ index int
+ FuncStack []func(req *http.Request) (*http.Response, error)
+}
+
+// Do is the mock client's `Do` func.
+func (m *MockClient) Do(req *http.Request) (*http.Response, error) {
+ res, err := m.FuncStack[m.index](req)
+ m.index++
+
+ return res, err
+}
diff --git a/pkg/provider/doppler/client.go b/pkg/provider/doppler/client.go
index 5f09723b9af..4b9a405be3e 100644
--- a/pkg/provider/doppler/client.go
+++ b/pkg/provider/doppler/client.go
@@ -17,6 +17,7 @@ package doppler
import (
"context"
"encoding/json"
+ "errors"
"fmt"
"net/url"
"strings"
@@ -119,7 +120,7 @@ func (c *Client) DeleteSecret(_ context.Context, ref esv1beta1.PushSecretRemoteR
}
func (c *Client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf("not implemented")
+ return false, errors.New("not implemented")
}
func (c *Client) PushSecret(_ context.Context, secret *corev1.Secret, data esv1beta1.PushSecretData) error {
diff --git a/pkg/provider/doppler/doppler_test.go b/pkg/provider/doppler/doppler_test.go
index 23f96048a94..2b524ddf13d 100644
--- a/pkg/provider/doppler/doppler_test.go
+++ b/pkg/provider/doppler/doppler_test.go
@@ -16,7 +16,7 @@ package doppler
import (
"context"
- "fmt"
+ "errors"
"strings"
"testing"
@@ -191,7 +191,7 @@ func TestGetSecret(t *testing.T) {
pstc.request.Name = missingSecret
pstc.response = nil
pstc.expectError = missingSecretErr
- pstc.apiErr = fmt.Errorf("")
+ pstc.apiErr = errors.New("")
}
setInvalidSecret := func(pstc *dopplerTestCase) {
@@ -200,14 +200,14 @@ func TestGetSecret(t *testing.T) {
pstc.request.Name = invalidSecret
pstc.response = nil
pstc.expectError = missingSecretErr
- pstc.apiErr = fmt.Errorf("")
+ pstc.apiErr = errors.New("")
}
setClientError := func(pstc *dopplerTestCase) {
pstc.label = "invalid client error" //nolint:goconst
pstc.response = &client.SecretResponse{}
pstc.expectError = missingSecretErr
- pstc.apiErr = fmt.Errorf("")
+ pstc.apiErr = errors.New("")
}
testCases := []*dopplerTestCase{
@@ -254,7 +254,7 @@ func TestGetSecretMap(t *testing.T) {
pstc.label = "client error"
pstc.response = &client.SecretResponse{}
pstc.expectError = missingSecretErr
- pstc.apiErr = fmt.Errorf("")
+ pstc.apiErr = errors.New("")
}
testCases := []*dopplerTestCase{
@@ -300,14 +300,14 @@ func TestDeleteSecret(t *testing.T) {
pstc.request = makeValidDeleteRequest()
pstc.remoteRef.RemoteKey = invalidRemoteKey
pstc.expectError = missingDeleteErr
- pstc.apiErr = fmt.Errorf("")
+ pstc.apiErr = errors.New("")
}
setClientError := func(pstc *updateSecretCase) {
pstc.label = "invalid client error"
pstc.request = makeValidDeleteRequest()
pstc.expectError = missingDeleteErr
- pstc.apiErr = fmt.Errorf("")
+ pstc.apiErr = errors.New("")
}
testCases := []*updateSecretCase{
@@ -337,7 +337,7 @@ func TestPushSecret(t *testing.T) {
pstc.label = "push missing secret key"
pstc.secretData = makeSecretData(invalidSecret, *makeValidPushRemoteRef())
pstc.expectError = missingPushErr
- pstc.apiErr = fmt.Errorf("")
+ pstc.apiErr = errors.New("")
}
pushMissingRemoteSecret := func(pstc *updateSecretCase) {
@@ -349,13 +349,13 @@ func TestPushSecret(t *testing.T) {
},
)
pstc.expectError = missingPushErr
- pstc.apiErr = fmt.Errorf("")
+ pstc.apiErr = errors.New("")
}
setClientError := func(pstc *updateSecretCase) {
pstc.label = "invalid client error"
pstc.expectError = missingPushErr
- pstc.apiErr = fmt.Errorf("")
+ pstc.apiErr = errors.New("")
}
testCases := []*updateSecretCase{
@@ -418,12 +418,12 @@ func TestValidateStore(t *testing.T) {
{
label: "invalid store missing dopplerToken.name",
store: makeSecretStore(withAuth("", "", nil)),
- err: fmt.Errorf("invalid store: dopplerToken.name cannot be empty"),
+ err: errors.New("invalid store: dopplerToken.name cannot be empty"),
},
{
label: "invalid store namespace not allowed",
store: makeSecretStore(withAuth(secretName, "", &namespace)),
- err: fmt.Errorf("invalid store: namespace not allowed with namespaced SecretStore"),
+ err: errors.New("invalid store: namespace should either be empty or match the namespace of the SecretStore for a namespaced SecretStore"),
},
{
label: "valid provide optional dopplerToken.key",
diff --git a/pkg/provider/doppler/fake/fake.go b/pkg/provider/doppler/fake/fake.go
index 061024f13ab..2af17e8581f 100644
--- a/pkg/provider/doppler/fake/fake.go
+++ b/pkg/provider/doppler/fake/fake.go
@@ -15,7 +15,7 @@ limitations under the License.
package fake
import (
- "fmt"
+ "errors"
"net/url"
"github.com/google/go-cmp/cmp"
@@ -53,7 +53,7 @@ func (dc *DopplerClient) WithValue(request client.SecretRequest, response *clien
if dc != nil {
dc.getSecret = func(requestIn client.SecretRequest) (*client.SecretResponse, error) {
if !cmp.Equal(requestIn, request) {
- return nil, fmt.Errorf("unexpected test argument")
+ return nil, errors.New("unexpected test argument")
}
return response, err
}
@@ -64,7 +64,7 @@ func (dc *DopplerClient) WithUpdateValue(request client.UpdateSecretsRequest, er
if dc != nil {
dc.updateSecrets = func(requestIn client.UpdateSecretsRequest) error {
if !cmp.Equal(requestIn, request) {
- return fmt.Errorf("unexpected test argument")
+ return errors.New("unexpected test argument")
}
return err
}
diff --git a/pkg/provider/doppler/provider.go b/pkg/provider/doppler/provider.go
index 1bf21d4c13b..0b7fcb7e0b2 100644
--- a/pkg/provider/doppler/provider.go
+++ b/pkg/provider/doppler/provider.go
@@ -16,6 +16,7 @@ package doppler
import (
"context"
+ "errors"
"fmt"
"os"
"strconv"
@@ -55,7 +56,7 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore,
storeSpec := store.GetSpec()
if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.Doppler == nil {
- return nil, fmt.Errorf(errDopplerStore)
+ return nil, errors.New(errDopplerStore)
}
dopplerStoreSpec := storeSpec.Provider.Doppler
diff --git a/pkg/provider/fake/fake.go b/pkg/provider/fake/fake.go
index 3a8ae0c2a07..2d64c7bf2d2 100644
--- a/pkg/provider/fake/fake.go
+++ b/pkg/provider/fake/fake.go
@@ -17,6 +17,7 @@ package fake
import (
"context"
"encoding/json"
+ "errors"
"fmt"
"strings"
@@ -31,8 +32,8 @@ import (
)
var (
- errMissingStore = fmt.Errorf("missing store provider")
- errMissingFakeProvider = fmt.Errorf("missing store provider fake")
+ errMissingStore = errors.New("missing store provider")
+ errMissingFakeProvider = errors.New("missing store provider fake")
errMissingKeyField = "key must be set in data %v"
errMissingValueField = "at least one of value or valueMap must be set in data %v"
)
@@ -129,7 +130,7 @@ func (p *Provider) PushSecret(_ context.Context, secret *corev1.Secret, data esv
}
if currentData.Origin != FakeSetSecret {
- return fmt.Errorf("key already exists")
+ return errors.New("key already exists")
}
currentData.Value = string(value)
diff --git a/pkg/provider/fortanix/provider_test.go b/pkg/provider/fortanix/provider_test.go
index 963279babe7..32b74e173a9 100644
--- a/pkg/provider/fortanix/provider_test.go
+++ b/pkg/provider/fortanix/provider_test.go
@@ -15,100 +15,34 @@ package fortanix
import (
"context"
- "encoding/json"
"errors"
- "net/http"
- "net/http/httptest"
"testing"
+ "github.com/Azure/azure-sdk-for-go/sdk/azcore/to"
"github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/client-go/tools/clientcmd"
- clientcmdapi "k8s.io/client-go/tools/clientcmd/api"
- kubeclient "sigs.k8s.io/controller-runtime/pkg/client"
+ "k8s.io/apimachinery/pkg/runtime"
+ "sigs.k8s.io/controller-runtime/pkg/client/fake"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
v1 "github.com/external-secrets/external-secrets/apis/meta/v1"
)
-func pointer[T any](d T) *T {
- return &d
-}
-
-func respondJSON(w http.ResponseWriter, data any) {
- w.Header().Set("Content-Type", "application/json")
-
- json.NewEncoder(w).Encode(data)
-}
-
-func createMockKubernetesClient(t *testing.T) kubeclient.Client {
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- switch r.URL.Path {
- case "/api/v1":
- respondJSON(w, metav1.APIResourceList{
- APIResources: []metav1.APIResource{
- {
- Name: "secrets",
- Namespaced: true,
- Kind: "Secret",
- Verbs: metav1.Verbs{
- "get",
- },
- },
- },
- })
- case "/api/v1/namespaces/test/secrets/secret-name":
- respondJSON(w, corev1.Secret{
- ObjectMeta: metav1.ObjectMeta{
- Name: "secret-name",
- },
- Data: map[string][]byte{
- "apiKey": []byte("apiKey"),
- },
- })
- case "/api/v1/namespaces/test/secrets/missing-secret":
- w.WriteHeader(404)
- respondJSON(w, metav1.Status{
- Code: 404,
- })
- }
- }))
- t.Cleanup(server.Close)
-
- clientConfig := clientcmd.NewDefaultClientConfig(clientcmdapi.Config{
- Clusters: map[string]*clientcmdapi.Cluster{
- "test": {
- Server: server.URL,
- },
- },
- AuthInfos: map[string]*clientcmdapi.AuthInfo{
- "test": {
- Token: "token",
- },
- },
- Contexts: map[string]*clientcmdapi.Context{
- "test": {
- Cluster: "test",
- AuthInfo: "test",
- },
- },
- CurrentContext: "test",
- }, &clientcmd.ConfigOverrides{})
-
- restConfig, err := clientConfig.ClientConfig()
- assert.Nil(t, err)
- c, err := kubeclient.New(restConfig, kubeclient.Options{})
- assert.Nil(t, err)
-
- return c
-}
-
func TestNewClient(t *testing.T) {
t.Run("should create new client", func(t *testing.T) {
ctx := context.Background()
p := &Provider{}
- c := createMockKubernetesClient(t)
+ secret := &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "secret-name",
+ Namespace: "test",
+ },
+ Data: map[string][]byte{
+ "apiKey": []byte("apiKey"),
+ },
+ }
s := esv1beta1.SecretStore{
Spec: esv1beta1.SecretStoreSpec{
Provider: &esv1beta1.SecretStoreProvider{
@@ -123,8 +57,11 @@ func TestNewClient(t *testing.T) {
},
},
}
-
- _, err := p.NewClient(ctx, &s, c, "test")
+ scheme := runtime.NewScheme()
+ require.NoError(t, esv1beta1.AddToScheme(scheme))
+ require.NoError(t, corev1.AddToScheme(scheme))
+ fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(secret, &s).Build()
+ _, err := p.NewClient(ctx, &s, fakeClient, "test")
assert.Nil(t, err)
})
@@ -132,7 +69,6 @@ func TestNewClient(t *testing.T) {
t.Run("should fail to create new client if secret is missing", func(t *testing.T) {
ctx := context.Background()
p := &Provider{}
- c := createMockKubernetesClient(t)
s := esv1beta1.SecretStore{
Spec: esv1beta1.SecretStoreSpec{
Provider: &esv1beta1.SecretStoreProvider{
@@ -147,8 +83,11 @@ func TestNewClient(t *testing.T) {
},
},
}
-
- _, err := p.NewClient(ctx, &s, c, "test")
+ scheme := runtime.NewScheme()
+ require.NoError(t, esv1beta1.AddToScheme(scheme))
+ require.NoError(t, corev1.AddToScheme(scheme))
+ fakeClient := fake.NewClientBuilder().WithScheme(scheme).WithObjects(&s).Build()
+ _, err := p.NewClient(ctx, &s, fakeClient, "test")
assert.ErrorContains(t, err, "cannot resolve secret key ref")
})
@@ -195,11 +134,11 @@ func TestValidateStore(t *testing.T) {
SecretRef: &v1.SecretKeySelector{
Key: "key",
Name: "name",
- Namespace: pointer("namespace"),
+ Namespace: to.Ptr("namespace"),
},
},
},
- want: errors.New("namespace not allowed with namespaced SecretStore"),
+ want: errors.New("namespace should either be empty or match the namespace of the SecretStore for a namespaced SecretStore"),
},
}
for name, tc := range tests {
diff --git a/pkg/provider/gcp/secretmanager/auth.go b/pkg/provider/gcp/secretmanager/auth.go
index 5fd89f6a264..ab9648a45c1 100644
--- a/pkg/provider/gcp/secretmanager/auth.go
+++ b/pkg/provider/gcp/secretmanager/auth.go
@@ -16,6 +16,7 @@ package secretmanager
import (
"context"
+ "errors"
"fmt"
"golang.org/x/oauth2"
@@ -33,7 +34,7 @@ func NewTokenSource(ctx context.Context, auth esv1beta1.GCPSMAuth, projectID, st
}
wi, err := newWorkloadIdentity(ctx, projectID)
if err != nil {
- return nil, fmt.Errorf("unable to initialize workload identity")
+ return nil, errors.New("unable to initialize workload identity")
}
defer wi.Close()
isClusterKind := storeKind == esv1beta1.ClusterSecretStoreKind
diff --git a/pkg/provider/gcp/secretmanager/client.go b/pkg/provider/gcp/secretmanager/client.go
index b56511a4972..52f9566ca10 100644
--- a/pkg/provider/gcp/secretmanager/client.go
+++ b/pkg/provider/gcp/secretmanager/client.go
@@ -19,6 +19,8 @@ import (
"encoding/json"
"errors"
"fmt"
+ "maps"
+ "slices"
"strconv"
"strings"
@@ -41,6 +43,7 @@ import (
"github.com/external-secrets/external-secrets/pkg/metrics"
"github.com/external-secrets/external-secrets/pkg/provider/util/locks"
"github.com/external-secrets/external-secrets/pkg/utils"
+ "github.com/external-secrets/external-secrets/pkg/utils/metadata"
)
const (
@@ -49,8 +52,6 @@ const (
errGCPSMStore = "received invalid GCPSM SecretStore resource"
errUnableGetCredentials = "unable to get credentials: %w"
errClientClose = "unable to close SecretManager client: %w"
- errMissingStoreSpec = "invalid: missing store spec"
- errFetchSAKSecret = "could not fetch SecretAccessKey secret: %w"
errUnableProcessJSONCredentials = "failed to process the provided JSON credentials: %w"
errUnableCreateGCPSMClient = "failed to create GCP secretmanager client: %w"
errUninitalizedGCPProvider = "provider GCP is not initialized"
@@ -69,6 +70,7 @@ const (
managedByValue = "external-secrets"
providerName = "GCPSecretManager"
+ topicsKey = "topics"
)
type Client struct {
@@ -129,17 +131,41 @@ func parseError(err error) error {
return err
}
-func (c *Client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf("not implemented")
+func (c *Client) SecretExists(ctx context.Context, ref esv1beta1.PushSecretRemoteRef) (bool, error) {
+ secretName := fmt.Sprintf("projects/%s/secrets/%s", c.store.ProjectID, ref.GetRemoteKey())
+ gcpSecret, err := c.smClient.GetSecret(ctx, &secretmanagerpb.GetSecretRequest{
+ Name: secretName,
+ })
+ if err != nil {
+ if status.Code(err) == codes.NotFound {
+ return false, nil
+ }
+
+ return false, err
+ }
+
+ return gcpSecret != nil, nil
}
// PushSecret pushes a kubernetes secret key into gcp provider Secret.
func (c *Client) PushSecret(ctx context.Context, secret *corev1.Secret, pushSecretData esv1beta1.PushSecretData) error {
+ var (
+ payload []byte
+ err error
+ )
if pushSecretData.GetSecretKey() == "" {
- return fmt.Errorf("pushing the whole secret is not yet implemented")
+ // Must convert secret values to string, otherwise data will be sent as base64 to Vault
+ secretStringVal := make(map[string]string)
+ for k, v := range secret.Data {
+ secretStringVal[k] = string(v)
+ }
+ payload, err = utils.JSONMarshal(secretStringVal)
+ if err != nil {
+ return fmt.Errorf("failed to serialize secret content as JSON: %w", err)
+ }
+ } else {
+ payload = secret.Data[pushSecretData.GetSecretKey()]
}
-
- payload := secret.Data[pushSecretData.GetSecretKey()]
secretName := fmt.Sprintf("projects/%s/secrets/%s", c.store.ProjectID, pushSecretData.GetRemoteKey())
gcpSecret, err := c.smClient.GetSecret(ctx, &secretmanagerpb.GetSecretRequest{
Name: secretName,
@@ -151,19 +177,68 @@ func (c *Client) PushSecret(ctx context.Context, secret *corev1.Secret, pushSecr
return err
}
- gcpSecret, err = c.smClient.CreateSecret(ctx, &secretmanagerpb.CreateSecretRequest{
- Parent: fmt.Sprintf("projects/%s", c.store.ProjectID),
- SecretId: pushSecretData.GetRemoteKey(),
- Secret: &secretmanagerpb.Secret{
- Labels: map[string]string{
- managedByKey: managedByValue,
- },
- Replication: &secretmanagerpb.Replication{
- Replication: &secretmanagerpb.Replication_Automatic_{
- Automatic: &secretmanagerpb.Replication_Automatic{},
+ var replication = &secretmanagerpb.Replication{
+ Replication: &secretmanagerpb.Replication_Automatic_{
+ Automatic: &secretmanagerpb.Replication_Automatic{},
+ },
+ }
+
+ if c.store.Location != "" {
+ replica := &secretmanagerpb.Replication_UserManaged_Replica{
+ Location: c.store.Location,
+ }
+
+ if pushSecretData.GetMetadata() != nil {
+ var err error
+ meta, err := metadata.ParseMetadataParameters[PushSecretMetadataSpec](pushSecretData.GetMetadata())
+ if err != nil {
+ return fmt.Errorf("failed to parse PushSecret metadata: %w", err)
+ }
+ if meta != nil && meta.Spec.CMEKKeyName != "" {
+ replica.CustomerManagedEncryption = &secretmanagerpb.CustomerManagedEncryption{
+ KmsKeyName: meta.Spec.CMEKKeyName,
+ }
+ }
+ }
+
+ replication = &secretmanagerpb.Replication{
+ Replication: &secretmanagerpb.Replication_UserManaged_{
+ UserManaged: &secretmanagerpb.Replication_UserManaged{
+ Replicas: []*secretmanagerpb.Replication_UserManaged_Replica{
+ replica,
+ },
},
},
+ }
+ }
+
+ scrt := &secretmanagerpb.Secret{
+ Labels: map[string]string{
+ managedByKey: managedByValue,
},
+ Replication: replication,
+ }
+
+ topics, err := utils.FetchValueFromMetadata(topicsKey, pushSecretData.GetMetadata(), []any{})
+ if err != nil {
+ return fmt.Errorf("failed to fetch topics from metadata: %w", err)
+ }
+
+ for _, t := range topics {
+ name, ok := t.(string)
+ if !ok {
+ return fmt.Errorf("invalid topic type")
+ }
+
+ scrt.Topics = append(scrt.Topics, &secretmanagerpb.Topic{
+ Name: name,
+ })
+ }
+
+ gcpSecret, err = c.smClient.CreateSecret(ctx, &secretmanagerpb.CreateSecretRequest{
+ Parent: fmt.Sprintf("projects/%s", c.store.ProjectID),
+ SecretId: pushSecretData.GetRemoteKey(),
+ Secret: scrt,
})
metrics.ObserveAPICall(constants.ProviderGCPSM, constants.CallGCPSMCreateSecret, err)
if err != nil {
@@ -176,19 +251,43 @@ func (c *Client) PushSecret(ctx context.Context, secret *corev1.Secret, pushSecr
return err
}
- annotations, labels, err := builder.buildMetadata(gcpSecret.Annotations, gcpSecret.Labels)
+ annotations, labels, topics, err := builder.buildMetadata(gcpSecret.Annotations, gcpSecret.Labels, gcpSecret.Topics)
if err != nil {
return err
}
- if !mapEqual(gcpSecret.Annotations, annotations) || !mapEqual(gcpSecret.Labels, labels) {
+ // Comparing with a pointer based slice doesn't work so we are converting
+ // it to a string slice.
+ existingTopics := make([]string, 0, len(gcpSecret.Topics))
+ for _, t := range gcpSecret.Topics {
+ existingTopics = append(existingTopics, t.Name)
+ }
+
+ if !maps.Equal(gcpSecret.Annotations, annotations) || !maps.Equal(gcpSecret.Labels, labels) || !slices.Equal(existingTopics, topics) {
+ scrt := &secretmanagerpb.Secret{
+ Name: gcpSecret.Name,
+ Etag: gcpSecret.Etag,
+ Labels: labels,
+ Annotations: annotations,
+ Topics: gcpSecret.Topics,
+ }
+
+ if c.store.Location != "" {
+ scrt.Replication = &secretmanagerpb.Replication{
+ Replication: &secretmanagerpb.Replication_UserManaged_{
+ UserManaged: &secretmanagerpb.Replication_UserManaged{
+ Replicas: []*secretmanagerpb.Replication_UserManaged_Replica{
+ {
+ Location: c.store.Location,
+ },
+ },
+ },
+ },
+ }
+ }
+
_, err = c.smClient.UpdateSecret(ctx, &secretmanagerpb.UpdateSecretRequest{
- Secret: &secretmanagerpb.Secret{
- Name: gcpSecret.Name,
- Etag: gcpSecret.Etag,
- Labels: labels,
- Annotations: annotations,
- },
+ Secret: scrt,
UpdateMask: &field_mask.FieldMask{
Paths: []string{"labels", "annotations"},
},
@@ -369,7 +468,7 @@ func (c *Client) extractProjectIDNumber(secretFullName string) string {
// GetSecret returns a single secret from the provider.
func (c *Client) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
if utils.IsNil(c.smClient) || c.store.ProjectID == "" {
- return nil, fmt.Errorf(errUninitalizedGCPProvider)
+ return nil, errors.New(errUninitalizedGCPProvider)
}
if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
@@ -420,15 +519,7 @@ func (c *Client) getSecretMetadata(ctx context.Context, ref esv1beta1.ExternalSe
labels = "labels"
)
- extractMetadataKey := func(s string, p string) string {
- prefix := p + "."
- if !strings.HasPrefix(s, prefix) {
- return ""
- }
- return strings.TrimPrefix(s, prefix)
- }
-
- if annotation := extractMetadataKey(ref.Property, annotations); annotation != "" {
+ if annotation := c.extractMetadataKey(ref.Property, annotations); annotation != "" {
v, ok := secret.GetAnnotations()[annotation]
if !ok {
return nil, fmt.Errorf("annotation with key %s does not exist in secret %s", annotation, ref.Key)
@@ -437,7 +528,7 @@ func (c *Client) getSecretMetadata(ctx context.Context, ref esv1beta1.ExternalSe
return []byte(v), nil
}
- if label := extractMetadataKey(ref.Property, labels); label != "" {
+ if label := c.extractMetadataKey(ref.Property, labels); label != "" {
v, ok := secret.GetLabels()[label]
if !ok {
return nil, fmt.Errorf("label with key %s does not exist in secret %s", label, ref.Key)
@@ -479,10 +570,18 @@ func (c *Client) getSecretMetadata(ctx context.Context, ref esv1beta1.ExternalSe
return j, nil
}
+func (c *Client) extractMetadataKey(s, p string) string {
+ prefix := p + "."
+ if !strings.HasPrefix(s, prefix) {
+ return ""
+ }
+ return strings.TrimPrefix(s, prefix)
+}
+
// GetSecretMap returns multiple k/v pairs from the provider.
func (c *Client) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
if c.smClient == nil || c.store.ProjectID == "" {
- return nil, fmt.Errorf(errUninitalizedGCPProvider)
+ return nil, errors.New(errUninitalizedGCPProvider)
}
data, err := c.GetSecret(ctx, ref)
@@ -548,17 +647,3 @@ func getDataByProperty(data []byte, property string) gjson.Result {
}
return gjson.Get(payload, property)
}
-
-func mapEqual(m1, m2 map[string]string) bool {
- if len(m1) != len(m2) {
- return false
- }
-
- for k1, v1 := range m1 {
- if v2, ok := m2[k1]; !ok || v1 != v2 {
- return false
- }
- }
-
- return true
-}
diff --git a/pkg/provider/gcp/secretmanager/client_test.go b/pkg/provider/gcp/secretmanager/client_test.go
index e5b59bb6dd1..c7e7e1cf3f0 100644
--- a/pkg/provider/gcp/secretmanager/client_test.go
+++ b/pkg/provider/gcp/secretmanager/client_test.go
@@ -25,18 +25,30 @@ import (
"cloud.google.com/go/secretmanager/apiv1/secretmanagerpb"
"github.com/googleapis/gax-go/v2"
"github.com/googleapis/gax-go/v2/apierror"
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
pointer "k8s.io/utils/ptr"
+ "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
v1 "github.com/external-secrets/external-secrets/apis/meta/v1"
fakesm "github.com/external-secrets/external-secrets/pkg/provider/gcp/secretmanager/fake"
testingfake "github.com/external-secrets/external-secrets/pkg/provider/testing/fake"
)
+const (
+ errCallNotFoundAtIndex0 = "index 0 for call not found in the list of calls"
+ usEast1 = "us-east1"
+ errInvalidReplicationType = "req.Secret.Replication.Replication was not of type *secretmanagerpb.Replication_UserManaged_ but: %T"
+ testSecretName = "projects/foo/secret/bar"
+ managedBy = "managed-by"
+ externalSecrets = "external-secrets"
+)
+
type secretManagerTestCase struct {
mockClient *fakesm.MockSMClient
apiInput *secretmanagerpb.AccessSecretVersionRequest
@@ -46,7 +58,7 @@ type secretManagerTestCase struct {
apiErr error
expectError string
expectedSecret string
- // for testing secretmap
+ // for testing SecretMap
expectedData map[string][]byte
}
@@ -100,7 +112,7 @@ func makeValidSecretManagerTestCaseCustom(tweaks ...func(smtc *secretManagerTest
// This case can be shared by both GetSecret and GetSecretMap tests.
// bad case: set apiErr.
var setAPIErr = func(smtc *secretManagerTestCase) {
- smtc.apiErr = fmt.Errorf("oh no")
+ smtc.apiErr = errors.New("oh no")
smtc.expectError = "oh no"
}
@@ -198,7 +210,7 @@ func TestSecretManagerGetSecret(t *testing.T) {
}
}
-func TestGetSecret_MetadataPolicyFetch(t *testing.T) {
+func TestGetSecretMetadataPolicyFetch(t *testing.T) {
tests := []struct {
name string
ref esv1beta1.ExternalSecretDataRemoteRef
@@ -215,14 +227,14 @@ func TestGetSecret_MetadataPolicyFetch(t *testing.T) {
},
getSecretMockReturn: fakesm.SecretMockReturn{
Secret: &secretmanagerpb.Secret{
- Name: "projects/foo/secret/bar",
+ Name: testSecretName,
Annotations: map[string]string{
- "managed-by": "external-secrets",
+ managedBy: externalSecrets,
},
},
Err: nil,
},
- expectedSecret: "external-secrets",
+ expectedSecret: externalSecrets,
},
{
name: "label is specified",
@@ -233,14 +245,14 @@ func TestGetSecret_MetadataPolicyFetch(t *testing.T) {
},
getSecretMockReturn: fakesm.SecretMockReturn{
Secret: &secretmanagerpb.Secret{
- Name: "projects/foo/secret/bar",
+ Name: testSecretName,
Labels: map[string]string{
- "managed-by": "external-secrets",
+ managedBy: externalSecrets,
},
},
Err: nil,
},
- expectedSecret: "external-secrets",
+ expectedSecret: externalSecrets,
},
{
name: "annotations is specified",
@@ -251,7 +263,7 @@ func TestGetSecret_MetadataPolicyFetch(t *testing.T) {
},
getSecretMockReturn: fakesm.SecretMockReturn{
Secret: &secretmanagerpb.Secret{
- Name: "projects/foo/secret/bar",
+ Name: testSecretName,
Annotations: map[string]string{
"annotationKey1": "annotationValue1",
"annotationKey2": "annotationValue2",
@@ -274,7 +286,7 @@ func TestGetSecret_MetadataPolicyFetch(t *testing.T) {
},
getSecretMockReturn: fakesm.SecretMockReturn{
Secret: &secretmanagerpb.Secret{
- Name: "projects/foo/secret/bar",
+ Name: testSecretName,
Annotations: map[string]string{
"annotationKey1": "annotationValue1",
"annotationKey2": "annotationValue2",
@@ -296,7 +308,7 @@ func TestGetSecret_MetadataPolicyFetch(t *testing.T) {
},
getSecretMockReturn: fakesm.SecretMockReturn{
Secret: &secretmanagerpb.Secret{
- Name: "projects/foo/secret/bar",
+ Name: testSecretName,
Labels: map[string]string{
"label-key": "label-value",
},
@@ -317,9 +329,9 @@ func TestGetSecret_MetadataPolicyFetch(t *testing.T) {
},
getSecretMockReturn: fakesm.SecretMockReturn{
Secret: &secretmanagerpb.Secret{
- Name: "projects/foo/secret/bar",
+ Name: testSecretName,
Annotations: map[string]string{
- "managed-by": "external-secrets",
+ managedBy: externalSecrets,
},
},
Err: nil,
@@ -335,9 +347,9 @@ func TestGetSecret_MetadataPolicyFetch(t *testing.T) {
},
getSecretMockReturn: fakesm.SecretMockReturn{
Secret: &secretmanagerpb.Secret{
- Name: "projects/foo/secret/bar",
+ Name: testSecretName,
Labels: map[string]string{
- "managed-by": "external-secrets",
+ managedBy: externalSecrets,
},
},
Err: nil,
@@ -353,9 +365,9 @@ func TestGetSecret_MetadataPolicyFetch(t *testing.T) {
},
getSecretMockReturn: fakesm.SecretMockReturn{
Secret: &secretmanagerpb.Secret{
- Name: "projects/foo/secret/bar",
+ Name: testSecretName,
Labels: map[string]string{
- "managed-by": "external-secrets",
+ managedBy: externalSecrets,
},
},
Err: nil,
@@ -425,9 +437,9 @@ func TestDeleteSecret(t *testing.T) {
getSecretOutput: fakesm.SecretMockReturn{
Secret: &secretmanagerpb.Secret{
- Name: "projects/foo/secret/bar",
+ Name: testSecretName,
Labels: map[string]string{
- "managed-by": "external-secrets",
+ managedBy: externalSecrets,
},
},
Err: nil,
@@ -440,7 +452,7 @@ func TestDeleteSecret(t *testing.T) {
getSecretOutput: fakesm.SecretMockReturn{
Secret: &secretmanagerpb.Secret{
- Name: "projects/foo/secret/bar",
+ Name: testSecretName,
Labels: map[string]string{},
},
Err: nil,
@@ -517,7 +529,7 @@ func TestPushSecret(t *testing.T) {
canceledError := status.Error(codes.Canceled, "canceled")
canceledError, _ = apierror.FromError(canceledError)
- APIerror := fmt.Errorf("API Error")
+ APIerror := errors.New("API Error")
labelError := fmt.Errorf("secret %v is not managed by external secrets", remoteKey)
secret := secretmanagerpb.Secret{
@@ -528,7 +540,26 @@ func TestPushSecret(t *testing.T) {
},
},
Labels: map[string]string{
- "managed-by": "external-secrets",
+ managedBy: externalSecrets,
+ },
+ }
+ secretWithTopics := secretmanagerpb.Secret{
+ Name: "projects/default/secrets/baz",
+ Replication: &secretmanagerpb.Replication{
+ Replication: &secretmanagerpb.Replication_Automatic_{
+ Automatic: &secretmanagerpb.Replication_Automatic{},
+ },
+ },
+ Labels: map[string]string{
+ managedBy: externalSecrets,
+ },
+ Topics: []*secretmanagerpb.Topic{
+ {
+ Name: "topic1",
+ },
+ {
+ Name: "topic2",
+ },
},
}
wrongLabelSecret := secretmanagerpb.Secret{
@@ -539,7 +570,7 @@ func TestPushSecret(t *testing.T) {
},
},
Labels: map[string]string{
- "managed-by": "not-external-secrets",
+ managedBy: "not-external-secrets",
},
}
@@ -576,6 +607,7 @@ func TestPushSecret(t *testing.T) {
var secretVersion = secretmanagerpb.SecretVersion{}
type args struct {
+ store *esv1beta1.GCPSMProvider
mock *fakesm.MockSMClient
Metadata *apiextensionsv1.JSON
GetSecretMockReturn fakesm.SecretMockReturn
@@ -587,15 +619,18 @@ func TestPushSecret(t *testing.T) {
type want struct {
err error
+ req func(*fakesm.MockSMClient) error
}
tests := []struct {
- desc string
- args args
- want want
+ desc string
+ args args
+ want want
+ secret *corev1.Secret
}{
{
desc: "SetSecret successfully pushes a secret",
args: args{
+ store: &esv1beta1.GCPSMProvider{ProjectID: smtc.projectID},
mock: smtc.mockClient,
GetSecretMockReturn: fakesm.SecretMockReturn{Secret: &secret, Err: nil},
AccessSecretVersionMockReturn: fakesm.AccessSecretVersionMockReturn{Res: &res, Err: nil},
@@ -607,9 +642,17 @@ func TestPushSecret(t *testing.T) {
{
desc: "successfully pushes a secret with metadata",
args: args{
- mock: smtc.mockClient,
+ store: &esv1beta1.GCPSMProvider{ProjectID: smtc.projectID},
+ mock: smtc.mockClient,
Metadata: &apiextensionsv1.JSON{
- Raw: []byte(`{"annotations":{"annotation-key1":"annotation-value1"},"labels":{"label-key1":"label-value1"}}`),
+ Raw: []byte(`{
+ "apiVersion": "kubernetes.external-secrets.io/v1alpha1",
+ "kind": "PushSecretMetadata",
+ "spec": {
+ "annotations": {"annotation-key1":"annotation-value1"},
+ "labels": {"label-key1":"label-value1"}
+ }
+ }`),
},
GetSecretMockReturn: fakesm.SecretMockReturn{Secret: &secret, Err: nil},
UpdateSecretReturn: fakesm.SecretMockReturn{Secret: &secretmanagerpb.Secret{
@@ -620,7 +663,40 @@ func TestPushSecret(t *testing.T) {
},
},
Labels: map[string]string{
- "managed-by": "external-secrets",
+ managedBy: externalSecrets,
+ "label-key1": "label-value1",
+ },
+ Annotations: map[string]string{
+ "annotation-key1": "annotation-value1",
+ },
+ }, Err: nil},
+ AccessSecretVersionMockReturn: fakesm.AccessSecretVersionMockReturn{Res: &res, Err: nil},
+ AddSecretVersionMockReturn: fakesm.AddSecretVersionMockReturn{SecretVersion: &secretVersion, Err: nil}},
+ want: want{
+ err: nil,
+ },
+ },
+ {
+ desc: "successfully pushes a secret with defined region",
+ args: args{
+ store: &esv1beta1.GCPSMProvider{ProjectID: smtc.projectID, Location: usEast1},
+ mock: smtc.mockClient,
+ GetSecretMockReturn: fakesm.SecretMockReturn{Secret: nil, Err: notFoundError},
+ CreateSecretMockReturn: fakesm.SecretMockReturn{Secret: &secretmanagerpb.Secret{
+ Name: "projects/default/secrets/baz",
+ Replication: &secretmanagerpb.Replication{
+ Replication: &secretmanagerpb.Replication_UserManaged_{
+ UserManaged: &secretmanagerpb.Replication_UserManaged{
+ Replicas: []*secretmanagerpb.Replication_UserManaged_Replica{
+ {
+ Location: usEast1,
+ },
+ },
+ },
+ },
+ },
+ Labels: map[string]string{
+ managedBy: externalSecrets,
"label-key1": "label-value1",
},
Annotations: map[string]string{
@@ -631,23 +707,83 @@ func TestPushSecret(t *testing.T) {
AddSecretVersionMockReturn: fakesm.AddSecretVersionMockReturn{SecretVersion: &secretVersion, Err: nil}},
want: want{
err: nil,
+ req: func(m *fakesm.MockSMClient) error {
+ req, ok := m.CreateSecretCalledWithN[0]
+ if !ok {
+ return errors.New(errCallNotFoundAtIndex0)
+ }
+
+ user, ok := req.Secret.Replication.Replication.(*secretmanagerpb.Replication_UserManaged_)
+ if !ok {
+ return fmt.Errorf(errInvalidReplicationType, req.Secret.Replication.Replication)
+ }
+
+ if len(user.UserManaged.Replicas) < 1 {
+ return errors.New("req.Secret.Replication.Replication.Replicas was not empty")
+ }
+
+ if user.UserManaged.Replicas[0].Location != usEast1 {
+ return fmt.Errorf("req.Secret.Replication.Replicas[0].Location was not equal to us-east-1 but was %s", user.UserManaged.Replicas[0].Location)
+ }
+
+ return nil
+ },
},
},
{
- desc: "failed to push a secret with invalid metadata type",
+ desc: "SetSecret successfully pushes a secret with topics",
args: args{
- mock: smtc.mockClient,
Metadata: &apiextensionsv1.JSON{
- Raw: []byte(`{"tags":{"tag-key1":"tag-value1"}}`),
+ Raw: []byte(`{
+ "apiVersion": "kubernetes.external-secrets.io/v1alpha1",
+ "kind": "PushSecretMetadata",
+ "spec": {
+ "topics": ["topic1", "topic2"]
+ }
+ }`),
},
- GetSecretMockReturn: fakesm.SecretMockReturn{Secret: &secret, Err: nil}},
+ store: &esv1beta1.GCPSMProvider{ProjectID: smtc.projectID},
+ mock: &fakesm.MockSMClient{}, // the mock should NOT be shared between test cases
+ CreateSecretMockReturn: fakesm.SecretMockReturn{Secret: &secretWithTopics, Err: nil},
+ GetSecretMockReturn: fakesm.SecretMockReturn{Secret: nil, Err: notFoundError},
+ AccessSecretVersionMockReturn: fakesm.AccessSecretVersionMockReturn{Res: &res, Err: nil},
+ AddSecretVersionMockReturn: fakesm.AddSecretVersionMockReturn{SecretVersion: &secretVersion, Err: nil}},
want: want{
- err: fmt.Errorf("failed to decode PushSecret metadata"),
+ err: nil,
+ req: func(m *fakesm.MockSMClient) error {
+ scrt, ok := m.CreateSecretCalledWithN[0]
+ if !ok {
+ return errors.New(errCallNotFoundAtIndex0)
+ }
+
+ if scrt.Secret == nil {
+ return errors.New("index 0 for call was nil")
+ }
+
+ if len(scrt.Secret.Topics) != 2 {
+ return fmt.Errorf("secret topics count was not 2 but: %d", len(scrt.Secret.Topics))
+ }
+
+ if scrt.Secret.Topics[0].Name != "topic1" {
+ return fmt.Errorf("secret topic name for 1 was not topic1 but: %s", scrt.Secret.Topics[0].Name)
+ }
+
+ if scrt.Secret.Topics[1].Name != "topic2" {
+ return fmt.Errorf("secret topic name for 2 was not topic2 but: %s", scrt.Secret.Topics[1].Name)
+ }
+
+ if m.UpdateSecretCallN != 0 {
+ return fmt.Errorf("updateSecret called with %d", m.UpdateSecretCallN)
+ }
+
+ return nil
+ },
},
},
{
desc: "secret not pushed if AddSecretVersion errors",
args: args{
+ store: &esv1beta1.GCPSMProvider{ProjectID: smtc.projectID},
mock: smtc.mockClient,
GetSecretMockReturn: fakesm.SecretMockReturn{Secret: &secret, Err: nil},
AccessSecretVersionMockReturn: fakesm.AccessSecretVersionMockReturn{Res: &res, Err: nil},
@@ -660,6 +796,7 @@ func TestPushSecret(t *testing.T) {
{
desc: "secret not pushed if AccessSecretVersion errors",
args: args{
+ store: &esv1beta1.GCPSMProvider{ProjectID: smtc.projectID},
mock: smtc.mockClient,
GetSecretMockReturn: fakesm.SecretMockReturn{Secret: &secret, Err: nil},
AccessSecretVersionMockReturn: fakesm.AccessSecretVersionMockReturn{Res: nil, Err: APIerror},
@@ -671,6 +808,7 @@ func TestPushSecret(t *testing.T) {
{
desc: "secret not pushed if not managed-by external-secrets",
args: args{
+ store: &esv1beta1.GCPSMProvider{ProjectID: smtc.projectID},
mock: smtc.mockClient,
GetSecretMockReturn: fakesm.SecretMockReturn{Secret: &wrongLabelSecret, Err: nil},
},
@@ -681,6 +819,7 @@ func TestPushSecret(t *testing.T) {
{
desc: "don't push a secret with the same key and value",
args: args{
+ store: &esv1beta1.GCPSMProvider{ProjectID: smtc.projectID},
mock: smtc.mockClient,
AccessSecretVersionMockReturn: fakesm.AccessSecretVersionMockReturn{Res: &res2, Err: nil},
GetSecretMockReturn: fakesm.SecretMockReturn{Secret: &secret, Err: nil},
@@ -692,6 +831,7 @@ func TestPushSecret(t *testing.T) {
{
desc: "secret is created if one doesn't already exist",
args: args{
+ store: &esv1beta1.GCPSMProvider{ProjectID: smtc.projectID},
mock: smtc.mockClient,
GetSecretMockReturn: fakesm.SecretMockReturn{Secret: nil, Err: notFoundError},
AccessSecretVersionMockReturn: fakesm.AccessSecretVersionMockReturn{Res: nil, Err: notFoundError},
@@ -705,6 +845,7 @@ func TestPushSecret(t *testing.T) {
{
desc: "secret not created if CreateSecret returns not found error",
args: args{
+ store: &esv1beta1.GCPSMProvider{ProjectID: smtc.projectID},
mock: smtc.mockClient,
GetSecretMockReturn: fakesm.SecretMockReturn{Secret: nil, Err: notFoundError},
CreateSecretMockReturn: fakesm.SecretMockReturn{Secret: &secret, Err: notFoundError},
@@ -716,6 +857,7 @@ func TestPushSecret(t *testing.T) {
{
desc: "secret not created if CreateSecret returns error",
args: args{
+ store: &esv1beta1.GCPSMProvider{ProjectID: smtc.projectID},
mock: smtc.mockClient,
GetSecretMockReturn: fakesm.SecretMockReturn{Secret: nil, Err: canceledError},
},
@@ -726,6 +868,7 @@ func TestPushSecret(t *testing.T) {
{
desc: "access secret version for an existing secret returns error",
args: args{
+ store: &esv1beta1.GCPSMProvider{ProjectID: smtc.projectID},
mock: smtc.mockClient,
GetSecretMockReturn: fakesm.SecretMockReturn{Secret: &secret, Err: nil},
AccessSecretVersionMockReturn: fakesm.AccessSecretVersionMockReturn{Res: nil, Err: canceledError},
@@ -734,6 +877,19 @@ func TestPushSecret(t *testing.T) {
err: canceledError,
},
},
+ {
+ desc: "Whole secret is set with no existing GCPSM secret",
+ args: args{
+ store: &esv1beta1.GCPSMProvider{ProjectID: smtc.projectID},
+ mock: smtc.mockClient,
+ GetSecretMockReturn: fakesm.SecretMockReturn{Secret: &secret, Err: nil},
+ AccessSecretVersionMockReturn: fakesm.AccessSecretVersionMockReturn{Res: &res, Err: nil},
+ AddSecretVersionMockReturn: fakesm.AddSecretVersionMockReturn{SecretVersion: &secretVersion, Err: nil}},
+ want: want{
+ err: nil,
+ },
+ secret: &corev1.Secret{Data: map[string][]byte{"key1": []byte(`value1`), "key2": []byte(`value2`)}},
+ },
}
for _, tc := range tests {
t.Run(tc.desc, func(t *testing.T) {
@@ -745,11 +901,12 @@ func TestPushSecret(t *testing.T) {
c := Client{
smClient: tc.args.mock,
- store: &esv1beta1.GCPSMProvider{
- ProjectID: smtc.projectID,
- },
+ store: tc.args.store,
+ }
+ s := tc.secret
+ if s == nil {
+ s = &corev1.Secret{Data: map[string][]byte{secretKey: []byte("fake-value")}}
}
- s := &corev1.Secret{Data: map[string][]byte{secretKey: []byte("fake-value")}}
data := testingfake.PushSecretData{
SecretKey: secretKey,
Metadata: tc.args.Metadata,
@@ -771,11 +928,93 @@ func TestPushSecret(t *testing.T) {
if tc.want.err != nil {
t.Errorf("expected to receive an error but got nil")
}
+
+ if tc.want.req != nil {
+ if err := tc.want.req(tc.args.mock); err != nil {
+ t.Errorf("received an unexpected error while checking request: %v", err)
+ }
+ }
+ })
+ }
+}
+
+func TestSecretExists(t *testing.T) {
+ tests := []struct {
+ name string
+ ref esv1beta1.PushSecretRemoteRef
+ getSecretMockReturn fakesm.SecretMockReturn
+ expectedSecret bool
+ expectedErr func(t *testing.T, err error)
+ }{
+ {
+ name: "secret exists",
+ ref: v1alpha1.PushSecretRemoteRef{
+ RemoteKey: "bar",
+ },
+ getSecretMockReturn: fakesm.SecretMockReturn{
+ Secret: &secretmanagerpb.Secret{
+ Name: testSecretName,
+ },
+ Err: nil,
+ },
+ expectedSecret: true,
+ expectedErr: func(t *testing.T, err error) {
+ require.NoError(t, err)
+ },
+ },
+ {
+ name: "secret does not exists",
+ ref: v1alpha1.PushSecretRemoteRef{
+ RemoteKey: "bar",
+ },
+ getSecretMockReturn: fakesm.SecretMockReturn{
+ Err: nil,
+ },
+ expectedSecret: false,
+ expectedErr: func(t *testing.T, err error) {
+ require.NoError(t, err)
+ },
+ },
+ {
+ name: "unexpected error occurs",
+ ref: v1alpha1.PushSecretRemoteRef{
+ RemoteKey: "bar2",
+ },
+ getSecretMockReturn: fakesm.SecretMockReturn{
+ Secret: &secretmanagerpb.Secret{
+ Name: testSecretName,
+ },
+ Err: errors.New("some error"),
+ },
+ expectedSecret: false,
+ expectedErr: func(t *testing.T, err error) {
+ assert.ErrorContains(t, err, "some error")
+ },
+ },
+ }
+
+ for _, tc := range tests {
+ t.Run(tc.name, func(t *testing.T) {
+ smClient := fakesm.MockSMClient{}
+ smClient.NewGetSecretFn(tc.getSecretMockReturn)
+
+ client := Client{
+ smClient: &smClient,
+ store: &esv1beta1.GCPSMProvider{
+ ProjectID: "foo",
+ },
+ }
+ got, err := client.SecretExists(context.TODO(), tc.ref)
+ tc.expectedErr(t, err)
+
+ if got != tc.expectedSecret {
+ t.Fatalf("unexpected secret: expected %t, got %t", tc.expectedSecret, got)
+ }
})
}
}
-func TestPushSecret_Property(t *testing.T) {
+func TestPushSecretProperty(t *testing.T) {
secretKey := "secret-key"
defaultAddSecretVersionMockReturn := func(gotPayload, expectedPayload string) (*secretmanagerpb.SecretVersion, error) {
if gotPayload != expectedPayload {
@@ -965,7 +1204,7 @@ func TestPushSecret_Property(t *testing.T) {
}
if !strings.Contains(err.Error(), tc.expectedErr) {
- t.Fatalf("PushSecret returns unexpected error: %q is supposed to contain %q", err, tc.expectedErr)
+ t.Fatalf("PushSecret returns unexpected error: %q should have contained %s", err, tc.expectedErr)
}
return
diff --git a/pkg/provider/gcp/secretmanager/fake/fake.go b/pkg/provider/gcp/secretmanager/fake/fake.go
index 0d58a3eba6e..5d3e3ebd524 100644
--- a/pkg/provider/gcp/secretmanager/fake/fake.go
+++ b/pkg/provider/gcp/secretmanager/fake/fake.go
@@ -27,14 +27,17 @@ import (
)
type MockSMClient struct {
- accessSecretFn func(ctx context.Context, req *secretmanagerpb.AccessSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.AccessSecretVersionResponse, error)
- ListSecretsFn func(ctx context.Context, req *secretmanagerpb.ListSecretsRequest, opts ...gax.CallOption) *secretmanager.SecretIterator
- AddSecretFn func(ctx context.Context, req *secretmanagerpb.AddSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.SecretVersion, error)
- createSecretFn func(ctx context.Context, req *secretmanagerpb.CreateSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error)
- updateSecretFn func(ctx context.Context, req *secretmanagerpb.UpdateSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error)
- closeFn func() error
- GetSecretFn func(ctx context.Context, req *secretmanagerpb.GetSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error)
- DeleteSecretFn func(ctx context.Context, req *secretmanagerpb.DeleteSecretRequest, opts ...gax.CallOption) error
+ accessSecretFn func(ctx context.Context, req *secretmanagerpb.AccessSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.AccessSecretVersionResponse, error)
+ ListSecretsFn func(ctx context.Context, req *secretmanagerpb.ListSecretsRequest, opts ...gax.CallOption) *secretmanager.SecretIterator
+ AddSecretFn func(ctx context.Context, req *secretmanagerpb.AddSecretVersionRequest, opts ...gax.CallOption) (*secretmanagerpb.SecretVersion, error)
+ createSecretFn func(ctx context.Context, req *secretmanagerpb.CreateSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error)
+ CreateSecretCalledWithN map[int]*secretmanagerpb.CreateSecretRequest
+ createSecretCallN int
+ updateSecretFn func(ctx context.Context, req *secretmanagerpb.UpdateSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error)
+ UpdateSecretCallN int
+ closeFn func() error
+ GetSecretFn func(ctx context.Context, req *secretmanagerpb.GetSecretRequest, opts ...gax.CallOption) (*secretmanagerpb.Secret, error)
+ DeleteSecretFn func(ctx context.Context, req *secretmanagerpb.DeleteSecretRequest, opts ...gax.CallOption) error
}
type AccessSecretVersionMockReturn struct {
@@ -98,6 +101,12 @@ func (mc *MockSMClient) NewAddSecretVersionFn(mock AddSecretVersionMockReturn) {
}
func (mc *MockSMClient) CreateSecret(ctx context.Context, req *secretmanagerpb.CreateSecretRequest, _ ...gax.CallOption) (*secretmanagerpb.Secret, error) {
+ if mc.CreateSecretCalledWithN == nil {
+ mc.CreateSecretCalledWithN = make(map[int]*secretmanagerpb.CreateSecretRequest)
+ }
+ mc.CreateSecretCalledWithN[mc.createSecretCallN] = req
+ mc.createSecretCallN++
+
return mc.createSecretFn(ctx, req)
}
@@ -175,6 +184,7 @@ func (mc *MockSMClient) AccessSecretVersionWithError(err error) {
}
func (mc *MockSMClient) UpdateSecret(ctx context.Context, req *secretmanagerpb.UpdateSecretRequest, _ ...gax.CallOption) (*secretmanagerpb.Secret, error) {
+ mc.UpdateSecretCallN++
return mc.updateSecretFn(ctx, req)
}
@@ -190,7 +200,7 @@ func (mc *MockSMClient) WithValue(_ context.Context, req *secretmanagerpb.Access
// type secretmanagerpb.AccessSecretVersionRequest contains unexported fields
// use cmpopts.IgnoreUnexported to ignore all the unexported fields in the cmp.
if !cmp.Equal(paramReq, req, cmpopts.IgnoreUnexported(secretmanagerpb.AccessSecretVersionRequest{})) {
- return nil, fmt.Errorf("unexpected test argument")
+ return nil, errors.New("unexpected test argument")
}
return val, err
}
diff --git a/pkg/provider/gcp/secretmanager/provider.go b/pkg/provider/gcp/secretmanager/provider.go
index 5996d87c9f7..44a594b0770 100644
--- a/pkg/provider/gcp/secretmanager/provider.go
+++ b/pkg/provider/gcp/secretmanager/provider.go
@@ -16,6 +16,7 @@ package secretmanager
import (
"context"
+ "errors"
"fmt"
"sync"
@@ -60,7 +61,7 @@ func (p *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
storeSpec := store.GetSpec()
if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.GCPSM == nil {
- return nil, fmt.Errorf(errGCPSMStore)
+ return nil, errors.New(errGCPSMStore)
}
gcpStore := storeSpec.Provider.GCPSM
@@ -113,18 +114,18 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore,
func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnings, error) {
if store == nil {
- return nil, fmt.Errorf(errInvalidStore)
+ return nil, errors.New(errInvalidStore)
}
spc := store.GetSpec()
if spc == nil {
- return nil, fmt.Errorf(errInvalidStoreSpec)
+ return nil, errors.New(errInvalidStoreSpec)
}
if spc.Provider == nil {
- return nil, fmt.Errorf(errInvalidStoreProv)
+ return nil, errors.New(errInvalidStoreProv)
}
g := spc.Provider.GCPSM
if p == nil {
- return nil, fmt.Errorf(errInvalidGCPProv)
+ return nil, errors.New(errInvalidGCPProv)
}
if g.Auth.SecretRef != nil {
if err := utils.ValidateReferentSecretSelector(store, g.Auth.SecretRef.SecretAccessKey); err != nil {
@@ -145,7 +146,7 @@ func clusterProjectID(spec *esv1beta1.SecretStoreSpec) (string, error) {
} else if spec.Provider.GCPSM.ProjectID != "" {
return spec.Provider.GCPSM.ProjectID, nil
} else {
- return "", fmt.Errorf(errNoProjectID)
+ return "", errors.New(errNoProjectID)
}
}
diff --git a/pkg/provider/gcp/secretmanager/push_secret.go b/pkg/provider/gcp/secretmanager/push_secret.go
index 6c116e9cbdf..f1560f1f6f2 100644
--- a/pkg/provider/gcp/secretmanager/push_secret.go
+++ b/pkg/provider/gcp/secretmanager/push_secret.go
@@ -16,18 +16,30 @@ package secretmanager
import (
"bytes"
- "encoding/json"
"errors"
"fmt"
+ "maps"
+ "cloud.google.com/go/secretmanager/apiv1/secretmanagerpb"
"github.com/tidwall/sjson"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ "github.com/external-secrets/external-secrets/pkg/utils/metadata"
)
-type Metadata struct {
- Annotations map[string]string `json:"annotations"`
- Labels map[string]string `json:"labels"`
+type PushSecretMetadataMergePolicy string
+
+const (
+ PushSecretMetadataMergePolicyReplace PushSecretMetadataMergePolicy = "Replace"
+ PushSecretMetadataMergePolicyMerge PushSecretMetadataMergePolicy = "Merge"
+)
+
+type PushSecretMetadataSpec struct {
+ Annotations map[string]string `json:"annotations,omitempty"`
+ Labels map[string]string `json:"labels,omitempty"`
+ Topics []string `json:"topics,omitempty"`
+ MergePolicy PushSecretMetadataMergePolicy `json:"mergePolicy,omitempty"`
+ CMEKKeyName string `json:"cmekKeyName,omitempty"`
}
func newPushSecretBuilder(payload []byte, data esv1beta1.PushSecretData) (pushSecretBuilder, error) {
@@ -49,7 +61,7 @@ func newPushSecretBuilder(payload []byte, data esv1beta1.PushSecretData) (pushSe
}
type pushSecretBuilder interface {
- buildMetadata(annotations, labels map[string]string) (map[string]string, map[string]string, error)
+ buildMetadata(annotations, labels map[string]string, topics []*secretmanagerpb.Topic) (map[string]string, map[string]string, []string, error)
needUpdate(original []byte) bool
buildData(original []byte) ([]byte, error)
}
@@ -59,29 +71,34 @@ type psBuilder struct {
pushSecretData esv1beta1.PushSecretData
}
-func (b *psBuilder) buildMetadata(_, labels map[string]string) (map[string]string, map[string]string, error) {
+func (b *psBuilder) buildMetadata(_, labels map[string]string, _ []*secretmanagerpb.Topic) (map[string]string, map[string]string, []string, error) {
if manager, ok := labels[managedByKey]; !ok || manager != managedByValue {
- return nil, nil, fmt.Errorf("secret %v is not managed by external secrets", b.pushSecretData.GetRemoteKey())
+ return nil, nil, nil, fmt.Errorf("secret %v is not managed by external secrets", b.pushSecretData.GetRemoteKey())
}
- var metadata Metadata
+ var meta *metadata.PushSecretMetadata[PushSecretMetadataSpec]
if b.pushSecretData.GetMetadata() != nil {
- decoder := json.NewDecoder(bytes.NewReader(b.pushSecretData.GetMetadata().Raw))
- // Want to return an error if unknown fields exist
- decoder.DisallowUnknownFields()
-
- if err := decoder.Decode(&metadata); err != nil {
- return nil, nil, fmt.Errorf("failed to decode PushSecret metadata: %w", err)
+ var err error
+ meta, err = metadata.ParseMetadataParameters[PushSecretMetadataSpec](b.pushSecretData.GetMetadata())
+ if err != nil {
+ return nil, nil, nil, fmt.Errorf("failed to parse PushSecret metadata: %w", err)
}
}
+ var spec PushSecretMetadataSpec
+ if meta != nil {
+ spec = meta.Spec
+ }
+
newLabels := map[string]string{}
- if metadata.Labels != nil {
- newLabels = metadata.Labels
+ maps.Copy(newLabels, spec.Labels)
+ if spec.MergePolicy == PushSecretMetadataMergePolicyMerge {
+ // Keep labels from the existing GCP Secret Manager Secret
+ maps.Copy(newLabels, labels)
}
newLabels[managedByKey] = managedByValue
- return metadata.Annotations, newLabels, nil
+ return spec.Annotations, newLabels, spec.Topics, nil
}
func (b *psBuilder) needUpdate(original []byte) bool {
@@ -101,7 +118,7 @@ type propertyPSBuilder struct {
pushSecretData esv1beta1.PushSecretData
}
-func (b *propertyPSBuilder) buildMetadata(annotations, labels map[string]string) (map[string]string, map[string]string, error) {
+func (b *propertyPSBuilder) buildMetadata(annotations, labels map[string]string, topics []*secretmanagerpb.Topic) (map[string]string, map[string]string, []string, error) {
newAnnotations := map[string]string{}
newLabels := map[string]string{}
if annotations != nil {
@@ -112,7 +129,13 @@ func (b *propertyPSBuilder) buildMetadata(annotations, labels map[string]string)
}
newLabels[managedByKey] = managedByValue
- return newAnnotations, newLabels, nil
+
+ result := make([]string, 0, len(topics))
+ for _, t := range topics {
+ result = append(result, t.Name)
+ }
+
+ return newAnnotations, newLabels, result, nil
}
func (b *propertyPSBuilder) needUpdate(original []byte) bool {
diff --git a/pkg/provider/gcp/secretmanager/push_secret_test.go b/pkg/provider/gcp/secretmanager/push_secret_test.go
new file mode 100644
index 00000000000..12e3eee65c2
--- /dev/null
+++ b/pkg/provider/gcp/secretmanager/push_secret_test.go
@@ -0,0 +1,144 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package secretmanager
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+
+ testingfake "github.com/external-secrets/external-secrets/pkg/provider/testing/fake"
+)
+
+func TestBuildMetadata(t *testing.T) {
+ tests := []struct {
+ name string
+ labels map[string]string
+ metadata *apiextensionsv1.JSON
+ expectedError bool
+ expectedLabels map[string]string
+ expectedAnnotations map[string]string
+ expectedTopics []string
+ }{
+ {
+ name: "secret not managed by external secrets",
+ labels: map[string]string{
+ "someKey": "someValue",
+ },
+ expectedError: true,
+ },
+ {
+ name: "metadata with default MergePolicy of Replace",
+ labels: map[string]string{
+ managedByKey: managedByValue,
+ "someOtherKey": "someOtherValue",
+ },
+ metadata: &apiextensionsv1.JSON{
+ Raw: []byte(`{
+ "apiVersion": "kubernetes.external-secrets.io/v1alpha1",
+ "kind": "PushSecretMetadata",
+ "spec": {
+ "annotations": {"key1":"value1"},
+ "labels": {"key2":"value2"}
+ }
+ }`),
+ },
+ expectedError: false,
+ expectedLabels: map[string]string{
+ managedByKey: managedByValue,
+ "key2": "value2",
+ },
+ expectedAnnotations: map[string]string{
+ "key1": "value1",
+ },
+ expectedTopics: nil,
+ },
+ {
+ name: "metadata with merge policy",
+ labels: map[string]string{
+ managedByKey: managedByValue,
+ "existingKey": "existingValue",
+ },
+ metadata: &apiextensionsv1.JSON{
+ Raw: []byte(`{
+ "apiVersion": "kubernetes.external-secrets.io/v1alpha1",
+ "kind": "PushSecretMetadata",
+ "spec": {
+ "annotations": {"key1":"value1"},
+ "labels": {"key2":"value2"},
+ "mergePolicy": "Merge"
+ }
+ }`),
+ },
+ expectedError: false,
+ expectedLabels: map[string]string{
+ managedByKey: managedByValue,
+ "existingKey": "existingValue",
+ "key2": "value2",
+ },
+ expectedAnnotations: map[string]string{
+ "key1": "value1",
+ },
+ expectedTopics: nil,
+ },
+ {
+ name: "metadata with CMEK key name",
+ labels: map[string]string{
+ managedByKey: managedByValue,
+ },
+ metadata: &apiextensionsv1.JSON{
+ Raw: []byte(`{
+ "apiVersion": "kubernetes.external-secrets.io/v1alpha1",
+ "kind": "PushSecretMetadata",
+ "spec": {
+ "annotations": {"key1":"value1"},
+ "labels": {"key2":"value2"},
+ "cmekKeyName": "projects/my-project/locations/us-east1/keyRings/my-keyring/cryptoKeys/my-key"
+ }
+ }`),
+ },
+ expectedError: false,
+ expectedLabels: map[string]string{
+ managedByKey: managedByValue,
+ "key2": "value2",
+ },
+ expectedAnnotations: map[string]string{
+ "key1": "value1",
+ },
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ psData := testingfake.PushSecretData{
+ Metadata: tt.metadata,
+ }
+ builder := &psBuilder{
+ pushSecretData: psData,
+ }
+
+ annotations, labels, topics, err := builder.buildMetadata(nil, tt.labels, nil)
+ if tt.expectedError {
+ assert.Error(t, err)
+ } else {
+ assert.NoError(t, err)
+ assert.Equal(t, tt.expectedLabels, labels)
+ assert.Equal(t, tt.expectedAnnotations, annotations)
+ assert.Equal(t, tt.expectedTopics, topics)
+ }
+ })
+ }
+}
diff --git a/pkg/provider/gitlab/fake/fake.go b/pkg/provider/gitlab/fake/fake.go
index 8a07b64e078..22c6b0ebc80 100644
--- a/pkg/provider/gitlab/fake/fake.go
+++ b/pkg/provider/gitlab/fake/fake.go
@@ -17,7 +17,7 @@ package fake
import (
"net/http"
- "github.com/xanzy/go-gitlab"
+ gitlab "gitlab.com/gitlab-org/api/client-go"
)
type APIResponse[O any] struct {
@@ -135,7 +135,7 @@ type GitlabMockGroupVariablesClient struct {
listVariables func(gid any, options ...gitlab.RequestOptionFunc) ([]*gitlab.GroupVariable, *gitlab.Response, error)
}
-func (mc *GitlabMockGroupVariablesClient) GetVariable(gid any, key string, _ ...gitlab.RequestOptionFunc) (*gitlab.GroupVariable, *gitlab.Response, error) {
+func (mc *GitlabMockGroupVariablesClient) GetVariable(gid any, key string, _ *gitlab.GetGroupVariableOptions, _ ...gitlab.RequestOptionFunc) (*gitlab.GroupVariable, *gitlab.Response, error) {
return mc.getVariable(gid, key, nil)
}
diff --git a/pkg/provider/gitlab/gitlab.go b/pkg/provider/gitlab/gitlab.go
index f5fd1e00b43..667dacc64c9 100644
--- a/pkg/provider/gitlab/gitlab.go
+++ b/pkg/provider/gitlab/gitlab.go
@@ -17,6 +17,7 @@ package gitlab
import (
"context"
"encoding/json"
+ "errors"
"fmt"
"net/http"
"sort"
@@ -24,7 +25,7 @@ import (
"strings"
"github.com/tidwall/gjson"
- "github.com/xanzy/go-gitlab"
+ gitlab "gitlab.com/gitlab-org/api/client-go"
corev1 "k8s.io/api/core/v1"
ctrl "sigs.k8s.io/controller-runtime"
@@ -37,19 +38,16 @@ import (
)
const (
- errGitlabCredSecretName = "credentials are empty"
- errInvalidClusterStoreMissingSAKNamespace = "invalid clusterStore missing SAK namespace"
- errFetchSAKSecret = "couldn't find secret on cluster: %w"
- errList = "could not verify whether the gitlabClient is valid: %w"
- errProjectAuth = "gitlabClient is not allowed to get secrets for project id [%s]"
- errGroupAuth = "gitlabClient is not allowed to get secrets for group id [%s]"
- errUninitializedGitlabProvider = "provider gitlab is not initialized"
- errNameNotDefined = "'find.name' is mandatory"
- errEnvironmentIsConstricted = "'find.tags' is constrained by 'environment_scope' of the store"
- errTagsOnlyEnvironmentSupported = "'find.tags' only supports 'environment_scope'"
- errPathNotImplemented = "'find.path' is not implemented in the GitLab provider"
- errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
- errNotImplemented = "not implemented"
+ errList = "could not verify whether the gitlabClient is valid: %w"
+ errProjectAuth = "gitlabClient is not allowed to get secrets for project id [%s]"
+ errGroupAuth = "gitlabClient is not allowed to get secrets for group id [%s]"
+ errUninitializedGitlabProvider = "provider gitlab is not initialized"
+ errNameNotDefined = "'find.name' is mandatory"
+ errEnvironmentIsConstricted = "'find.tags' is constrained by 'environment_scope' of the store"
+ errTagsOnlyEnvironmentSupported = "'find.tags' only supports 'environment_scope'"
+ errPathNotImplemented = "'find.path' is not implemented in the GitLab provider"
+ errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
+ errNotImplemented = "not implemented"
)
// https://github.com/external-secrets/external-secrets/issues/644
@@ -66,7 +64,7 @@ type ProjectVariablesClient interface {
}
type GroupVariablesClient interface {
- GetVariable(gid any, key string, options ...gitlab.RequestOptionFunc) (*gitlab.GroupVariable, *gitlab.Response, error)
+ GetVariable(gid any, key string, opts *gitlab.GetGroupVariableOptions, options ...gitlab.RequestOptionFunc) (*gitlab.GroupVariable, *gitlab.Response, error)
ListVariables(gid any, opt *gitlab.ListGroupVariablesOptions, options ...gitlab.RequestOptionFunc) ([]*gitlab.GroupVariable, *gitlab.Response, error)
}
@@ -89,21 +87,21 @@ func (g *gitlabBase) getAuth(ctx context.Context) (string, error) {
}
func (g *gitlabBase) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
- return fmt.Errorf(errNotImplemented)
+ return errors.New(errNotImplemented)
}
func (g *gitlabBase) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf(errNotImplemented)
+ return false, errors.New(errNotImplemented)
}
func (g *gitlabBase) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
- return fmt.Errorf(errNotImplemented)
+ return errors.New(errNotImplemented)
}
// GetAllSecrets syncs all gitlab project and group variables into a single Kubernetes Secret.
func (g *gitlabBase) GetAllSecrets(_ context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
if utils.IsNil(g.projectVariablesClient) {
- return nil, fmt.Errorf(errUninitializedGitlabProvider)
+ return nil, errors.New(errUninitializedGitlabProvider)
}
var effectiveEnvironment = g.store.Environment
if ref.Tags != nil {
@@ -112,15 +110,15 @@ func (g *gitlabBase) GetAllSecrets(_ context.Context, ref esv1beta1.ExternalSecr
return nil, err
}
if !isEmptyOrWildcard(effectiveEnvironment) && !isEmptyOrWildcard(environment) {
- return nil, fmt.Errorf(errEnvironmentIsConstricted)
+ return nil, errors.New(errEnvironmentIsConstricted)
}
effectiveEnvironment = environment
}
if ref.Path != nil {
- return nil, fmt.Errorf(errPathNotImplemented)
+ return nil, errors.New(errPathNotImplemented)
}
if ref.Name == nil {
- return nil, fmt.Errorf(errNameNotDefined)
+ return nil, errors.New(errNameNotDefined)
}
var matcher *find.Matcher
@@ -137,36 +135,27 @@ func (g *gitlabBase) GetAllSecrets(_ context.Context, ref esv1beta1.ExternalSecr
return nil, err
}
- var gopts = &gitlab.ListGroupVariablesOptions{PerPage: 100}
- secretData := make(map[string][]byte)
- for _, groupID := range g.store.GroupIDs {
- for groupPage := 1; ; groupPage++ {
- gopts.Page = groupPage
- groupVars, response, err := g.groupVariablesClient.ListVariables(groupID, gopts)
- metrics.ObserveAPICall(constants.ProviderGitLab, constants.CallGitLabGroupListVariables, err)
- if err != nil {
- return nil, err
- }
- for _, data := range groupVars {
- matching, key, isWildcard := matchesFilter(effectiveEnvironment, data.EnvironmentScope, data.Key, matcher)
- if !matching && !isWildcard {
- continue
- }
- secretData[key] = []byte(data.Value)
- }
- if response.CurrentPage >= response.TotalPages {
- break
- }
- }
+ secretData, err := g.fetchSecretData(effectiveEnvironment, matcher)
+ if err != nil {
+ return nil, err
+ }
+
+ // _Note_: fetchProjectVariables alters secret data map
+ if err := g.fetchProjectVariables(effectiveEnvironment, matcher, secretData); err != nil {
+ return nil, err
}
+ return secretData, nil
+}
+
+func (g *gitlabBase) fetchProjectVariables(effectiveEnvironment string, matcher *find.Matcher, secretData map[string][]byte) error {
var popts = &gitlab.ListProjectVariablesOptions{PerPage: 100}
for projectPage := 1; ; projectPage++ {
popts.Page = projectPage
projectData, response, err := g.projectVariablesClient.ListVariables(g.store.ProjectID, popts)
metrics.ObserveAPICall(constants.ProviderGitLab, constants.CallGitLabProjectListVariables, err)
if err != nil {
- return nil, err
+ return err
}
for _, data := range projectData {
@@ -186,14 +175,64 @@ func (g *gitlabBase) GetAllSecrets(_ context.Context, ref esv1beta1.ExternalSecr
}
}
+ return nil
+}
+
+func (g *gitlabBase) fetchSecretData(effectiveEnvironment string, matcher *find.Matcher) (map[string][]byte, error) {
+ var gopts = &gitlab.ListGroupVariablesOptions{PerPage: 100}
+ secretData := make(map[string][]byte)
+ for _, groupID := range g.store.GroupIDs {
+ if err := g.setVariablesForGroupID(effectiveEnvironment, matcher, gopts, groupID, secretData); err != nil {
+ return nil, err
+ }
+ }
+
return secretData, nil
}
+func (g *gitlabBase) setVariablesForGroupID(
+ effectiveEnvironment string,
+ matcher *find.Matcher,
+ gopts *gitlab.ListGroupVariablesOptions,
+ groupID string,
+ secretData map[string][]byte,
+) error {
+ for groupPage := 1; ; groupPage++ {
+ gopts.Page = groupPage
+ groupVars, response, err := g.groupVariablesClient.ListVariables(groupID, gopts)
+ metrics.ObserveAPICall(constants.ProviderGitLab, constants.CallGitLabGroupListVariables, err)
+ if err != nil {
+ return err
+ }
+ g.setGroupValues(effectiveEnvironment, matcher, groupVars, secretData)
+
+ if response.CurrentPage >= response.TotalPages {
+ break
+ }
+ }
+ return nil
+}
+
+func (g *gitlabBase) setGroupValues(
+ effectiveEnvironment string,
+ matcher *find.Matcher,
+ groupVars []*gitlab.GroupVariable,
+ secretData map[string][]byte,
+) {
+ for _, data := range groupVars {
+ matching, key, isWildcard := matchesFilter(effectiveEnvironment, data.EnvironmentScope, data.Key, matcher)
+ if !matching && !isWildcard {
+ continue
+ }
+ secretData[key] = []byte(data.Value)
+ }
+}
+
func ExtractTag(tags map[string]string) (string, error) {
var environmentScope string
for tag, value := range tags {
if tag != "environment_scope" {
- return "", fmt.Errorf(errTagsOnlyEnvironmentSupported)
+ return "", errors.New(errTagsOnlyEnvironmentSupported)
}
environmentScope = value
}
@@ -202,7 +241,7 @@ func ExtractTag(tags map[string]string) (string, error) {
func (g *gitlabBase) GetSecret(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
if utils.IsNil(g.projectVariablesClient) || utils.IsNil(g.groupVariablesClient) {
- return nil, fmt.Errorf(errUninitializedGitlabProvider)
+ return nil, errors.New(errUninitializedGitlabProvider)
}
// Need to replace hyphens with underscores to work with GitLab API
@@ -221,15 +260,9 @@ func (g *gitlabBase) GetSecret(_ context.Context, ref esv1beta1.ExternalSecretDa
vopts = &gitlab.GetProjectVariableOptions{Filter: &gitlab.VariableFilter{EnvironmentScope: g.store.Environment}}
}
- data, resp, err := g.projectVariablesClient.GetVariable(g.store.ProjectID, ref.Key, vopts)
- metrics.ObserveAPICall(constants.ProviderGitLab, constants.CallGitLabProjectVariableGet, err)
- if !isEmptyOrWildcard(g.store.Environment) && resp.StatusCode == http.StatusNotFound {
- vopts.Filter.EnvironmentScope = "*"
- data, resp, err = g.projectVariablesClient.GetVariable(g.store.ProjectID, ref.Key, vopts)
- metrics.ObserveAPICall(constants.ProviderGitLab, constants.CallGitLabProjectVariableGet, err)
- }
-
- if resp.StatusCode >= 400 && resp.StatusCode != http.StatusNotFound && err != nil {
+ // _Note_: getVariables potentially alters vopts environment variable.
+ data, resp, err := g.getVariables(ref, vopts)
+ if err != nil {
return nil, err
}
diff --git a/pkg/provider/gitlab/gitlab_test.go b/pkg/provider/gitlab/gitlab_test.go
index 2848078880d..4334a90f1ae 100644
--- a/pkg/provider/gitlab/gitlab_test.go
+++ b/pkg/provider/gitlab/gitlab_test.go
@@ -17,6 +17,7 @@ package gitlab
import (
"context"
"encoding/json"
+ "errors"
"fmt"
"net/http"
"reflect"
@@ -25,8 +26,8 @@ import (
"github.com/google/uuid"
tassert "github.com/stretchr/testify/assert"
- "github.com/xanzy/go-gitlab"
"github.com/yandex-cloud/go-sdk/iamkey"
+ gitlab "gitlab.com/gitlab-org/api/client-go"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
k8sclient "sigs.k8s.io/controller-runtime/pkg/client"
@@ -256,14 +257,14 @@ func prepareMockGroupVarClient(smtc *secretManagerTestCase) {
// This case can be shared by both GetSecret and GetSecretMap tests.
// bad case: set apiErr.
var setAPIErr = func(smtc *secretManagerTestCase) {
- smtc.apiErr = fmt.Errorf("oh no")
+ smtc.apiErr = errors.New("oh no")
smtc.expectError = "oh no"
smtc.projectAPIResponse.Response.StatusCode = http.StatusInternalServerError
smtc.expectedValidationResult = esv1beta1.ValidationResultError
}
var setListAPIErr = func(smtc *secretManagerTestCase) {
- err := fmt.Errorf("oh no")
+ err := errors.New("oh no")
smtc.apiErr = err
smtc.expectError = fmt.Errorf(errList, err).Error()
smtc.expectedValidationResult = esv1beta1.ValidationResultError
@@ -845,23 +846,23 @@ func TestValidateStore(t *testing.T) {
testCases := []ValidateStoreTestCase{
{
store: makeSecretStore("", environment),
- err: fmt.Errorf("projectID and groupIDs must not both be empty"),
+ err: errors.New("projectID and groupIDs must not both be empty"),
},
{
store: makeSecretStore(project, environment, withGroups([]string{"group1"}, true)),
- err: fmt.Errorf("defining groupIDs and inheritFromGroups = true is not allowed"),
+ err: errors.New("defining groupIDs and inheritFromGroups = true is not allowed"),
},
{
store: makeSecretStore(project, environment, withAccessToken("", userkey, nil)),
- err: fmt.Errorf("accessToken.name cannot be empty"),
+ err: errors.New("accessToken.name cannot be empty"),
},
{
store: makeSecretStore(project, environment, withAccessToken(username, "", nil)),
- err: fmt.Errorf("accessToken.key cannot be empty"),
+ err: errors.New("accessToken.key cannot be empty"),
},
{
store: makeSecretStore(project, environment, withAccessToken("userName", "userKey", &namespace)),
- err: fmt.Errorf("namespace not allowed with namespaced SecretStore"),
+ err: errors.New("namespace should either be empty or match the namespace of the SecretStore for a namespaced SecretStore"),
},
{
store: makeSecretStore(project, environment, withAccessToken("userName", "userKey", nil)),
diff --git a/pkg/provider/gitlab/provider.go b/pkg/provider/gitlab/provider.go
index 1922d3a5347..3cd09590847 100644
--- a/pkg/provider/gitlab/provider.go
+++ b/pkg/provider/gitlab/provider.go
@@ -16,13 +16,17 @@ package gitlab
import (
"context"
+ "errors"
"fmt"
+ "net/http"
- "github.com/xanzy/go-gitlab"
+ gitlab "gitlab.com/gitlab-org/api/client-go"
kclient "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ "github.com/external-secrets/external-secrets/pkg/constants"
+ "github.com/external-secrets/external-secrets/pkg/metrics"
"github.com/external-secrets/external-secrets/pkg/utils"
)
@@ -50,7 +54,7 @@ func (g *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
func (g *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
storeSpec := store.GetSpec()
if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.Gitlab == nil {
- return nil, fmt.Errorf("no store type or wrong store type")
+ return nil, errors.New("no store type or wrong store type")
}
storeSpecGitlab := storeSpec.Provider.Gitlab
@@ -96,6 +100,25 @@ func (g *gitlabBase) getClient(ctx context.Context, provider *esv1beta1.GitlabPr
return client, nil
}
+func (g *gitlabBase) getVariables(ref esv1beta1.ExternalSecretDataRemoteRef, vopts *gitlab.GetProjectVariableOptions) (*gitlab.ProjectVariable, *gitlab.Response, error) {
+ data, resp, err := g.projectVariablesClient.GetVariable(g.store.ProjectID, ref.Key, vopts)
+ metrics.ObserveAPICall(constants.ProviderGitLab, constants.CallGitLabProjectVariableGet, err)
+ if err != nil {
+ if resp != nil && resp.StatusCode == http.StatusNotFound && !isEmptyOrWildcard(g.store.Environment) {
+ vopts.Filter.EnvironmentScope = "*"
+ data, resp, err = g.projectVariablesClient.GetVariable(g.store.ProjectID, ref.Key, vopts)
+ metrics.ObserveAPICall(constants.ProviderGitLab, constants.CallGitLabProjectVariableGet, err)
+ if err != nil || resp == nil {
+ return nil, nil, fmt.Errorf("error getting variable %s from GitLab: %w", ref.Key, err)
+ }
+ } else {
+ return nil, nil, err
+ }
+ }
+
+ return data, resp, nil
+}
+
func (g *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnings, error) {
storeSpec := store.GetSpec()
gitlabSpec := storeSpec.Provider.Gitlab
@@ -106,19 +129,19 @@ func (g *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnin
}
if gitlabSpec.ProjectID == "" && len(gitlabSpec.GroupIDs) == 0 {
- return nil, fmt.Errorf("projectID and groupIDs must not both be empty")
+ return nil, errors.New("projectID and groupIDs must not both be empty")
}
if gitlabSpec.InheritFromGroups && len(gitlabSpec.GroupIDs) > 0 {
- return nil, fmt.Errorf("defining groupIDs and inheritFromGroups = true is not allowed")
+ return nil, errors.New("defining groupIDs and inheritFromGroups = true is not allowed")
}
if accessToken.Key == "" {
- return nil, fmt.Errorf("accessToken.key cannot be empty")
+ return nil, errors.New("accessToken.key cannot be empty")
}
if accessToken.Name == "" {
- return nil, fmt.Errorf("accessToken.name cannot be empty")
+ return nil, errors.New("accessToken.name cannot be empty")
}
return nil, nil
diff --git a/pkg/provider/ibm/provider.go b/pkg/provider/ibm/provider.go
index fe662ffca19..6c5738d96e8 100644
--- a/pkg/provider/ibm/provider.go
+++ b/pkg/provider/ibm/provider.go
@@ -17,6 +17,7 @@ package ibm
import (
"context"
"encoding/json"
+ "errors"
"fmt"
"os"
"strings"
@@ -38,10 +39,6 @@ import (
)
const (
- SecretsManagerEndpointEnv = "IBM_SECRETSMANAGER_ENDPOINT"
- STSEndpointEnv = "IBM_STS_ENDPOINT"
- SSMEndpointEnv = "IBM_SSM_ENDPOINT"
-
certificateConst = "certificate"
intermediateConst = "intermediate"
privateKeyConst = "private_key"
@@ -53,14 +50,13 @@ const (
payloadConst = "payload"
smAPIKeyConst = "api_key"
- errIBMClient = "cannot setup new ibm client: %w"
- errIBMCredSecretName = "invalid IBM SecretStore resource: missing IBM APIKey"
- errUninitalizedIBMProvider = "provider IBM is not initialized"
- errFetchSAKSecret = "could not fetch SecretAccessKey secret: %w"
- errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
- errJSONSecretMarshal = "unable to marshal secret: %w"
- errExtractingSecret = "unable to extract the fetched secret %s of type %s while performing %s"
- errNotImplemented = "not implemented"
+ errIBMClient = "cannot setup new ibm client: %w"
+ errUninitializedIBMProvider = "provider IBM is not initialized"
+ errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
+ errJSONSecretMarshal = "unable to marshal secret: %w"
+ errExtractingSecret = "unable to extract the fetched secret %s of type %s while performing %s"
+ errNotImplemented = "not implemented"
+ errKeyDoesNotExist = "key %s does not exist in secret %s"
)
var contextTimeout = time.Minute * 2
@@ -98,27 +94,27 @@ func (c *client) setAuth(ctx context.Context) error {
}
func (ibm *providerIBM) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
- return fmt.Errorf(errNotImplemented)
+ return errors.New(errNotImplemented)
}
func (ibm *providerIBM) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf(errNotImplemented)
+ return false, errors.New(errNotImplemented)
}
-// Not Implemented PushSecret.
+// PushSecret not implemented.
func (ibm *providerIBM) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
- return fmt.Errorf(errNotImplemented)
+ return errors.New(errNotImplemented)
}
-// Empty GetAllSecrets.
+// GetAllSecrets empty.
func (ibm *providerIBM) GetAllSecrets(_ context.Context, _ esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
// TO be implemented
- return nil, fmt.Errorf(errNotImplemented)
+ return nil, errors.New(errNotImplemented)
}
func (ibm *providerIBM) GetSecret(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
if utils.IsNil(ibm.IBMClient) {
- return nil, fmt.Errorf(errUninitalizedIBMProvider)
+ return nil, errors.New(errUninitializedIBMProvider)
}
var secretGroupName string
@@ -143,7 +139,7 @@ func (ibm *providerIBM) GetSecret(_ context.Context, ref esv1beta1.ExternalSecre
case sm.Secret_SecretType_UsernamePassword:
if ref.Property == "" {
- return nil, fmt.Errorf("remoteRef.property required for secret type username_password")
+ return nil, errors.New("remoteRef.property required for secret type username_password")
}
return getUsernamePasswordSecret(ibm, &secretName, ref, secretGroupName)
@@ -158,7 +154,7 @@ func (ibm *providerIBM) GetSecret(_ context.Context, ref esv1beta1.ExternalSecre
case sm.Secret_SecretType_ImportedCert:
if ref.Property == "" {
- return nil, fmt.Errorf("remoteRef.property required for secret type imported_cert")
+ return nil, errors.New("remoteRef.property required for secret type imported_cert")
}
return getImportCertSecret(ibm, &secretName, ref, secretGroupName)
@@ -166,7 +162,7 @@ func (ibm *providerIBM) GetSecret(_ context.Context, ref esv1beta1.ExternalSecre
case sm.Secret_SecretType_PublicCert:
if ref.Property == "" {
- return nil, fmt.Errorf("remoteRef.property required for secret type public_cert")
+ return nil, errors.New("remoteRef.property required for secret type public_cert")
}
return getPublicCertSecret(ibm, &secretName, ref, secretGroupName)
@@ -174,7 +170,7 @@ func (ibm *providerIBM) GetSecret(_ context.Context, ref esv1beta1.ExternalSecre
case sm.Secret_SecretType_PrivateCert:
if ref.Property == "" {
- return nil, fmt.Errorf("remoteRef.property required for secret type private_cert")
+ return nil, errors.New("remoteRef.property required for secret type private_cert")
}
return getPrivateCertSecret(ibm, &secretName, ref, secretGroupName)
@@ -208,7 +204,7 @@ func getArbitrarySecret(ibm *providerIBM, secretName *string, secretGroupName st
if val, ok := secMap[payloadConst]; ok {
return []byte(val.(string)), nil
}
- return nil, fmt.Errorf("key %s does not exist in secret %s", payloadConst, *secretName)
+ return nil, fmt.Errorf(errKeyDoesNotExist, payloadConst, *secretName)
}
func getImportCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
@@ -229,7 +225,7 @@ func getImportCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.Ext
fmt.Printf("warn: %s is empty for secret %s\n", privateKeyConst, *secretName)
return []byte(""), nil
}
- return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
+ return nil, fmt.Errorf(errKeyDoesNotExist, ref.Property, ref.Key)
}
func getPublicCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
@@ -244,7 +240,7 @@ func getPublicCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.Ext
if val, ok := secMap[ref.Property]; ok {
return []byte(val.(string)), nil
}
- return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
+ return nil, fmt.Errorf(errKeyDoesNotExist, ref.Property, ref.Key)
}
func getPrivateCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
@@ -259,7 +255,7 @@ func getPrivateCertSecret(ibm *providerIBM, secretName *string, ref esv1beta1.Ex
if val, ok := secMap[ref.Property]; ok {
return []byte(val.(string)), nil
}
- return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
+ return nil, fmt.Errorf(errKeyDoesNotExist, ref.Property, ref.Key)
}
func getIamCredentialsSecret(ibm *providerIBM, secretName *string, secretGroupName string) ([]byte, error) {
@@ -274,7 +270,7 @@ func getIamCredentialsSecret(ibm *providerIBM, secretName *string, secretGroupNa
if val, ok := secMap[smAPIKeyConst]; ok {
return []byte(val.(string)), nil
}
- return nil, fmt.Errorf("key %s does not exist in secret %s", smAPIKeyConst, *secretName)
+ return nil, fmt.Errorf(errKeyDoesNotExist, smAPIKeyConst, *secretName)
}
func getServiceCredentialsSecret(ibm *providerIBM, secretName *string, secretGroupName string) ([]byte, error) {
@@ -293,7 +289,7 @@ func getServiceCredentialsSecret(ibm *providerIBM, secretName *string, secretGro
}
return mval, nil
}
- return nil, fmt.Errorf("key %s does not exist in secret %s", credentialsConst, *secretName)
+ return nil, fmt.Errorf(errKeyDoesNotExist, credentialsConst, *secretName)
}
func getUsernamePasswordSecret(ibm *providerIBM, secretName *string, ref esv1beta1.ExternalSecretDataRemoteRef, secretGroupName string) ([]byte, error) {
@@ -308,7 +304,7 @@ func getUsernamePasswordSecret(ibm *providerIBM, secretName *string, ref esv1bet
if val, ok := secMap[ref.Property]; ok {
return []byte(val.(string)), nil
}
- return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
+ return nil, fmt.Errorf(errKeyDoesNotExist, ref.Property, ref.Key)
}
// Returns a secret of type kv and supports json path.
@@ -347,7 +343,7 @@ func getKVSecret(ref esv1beta1.ExternalSecretDataRemoteRef, secret *sm.KVSecret)
// try to get value for this path
val := gjson.Get(payloadJSON, ref.Property)
if !val.Exists() {
- return nil, fmt.Errorf("key %s does not exist in secret %s", ref.Property, ref.Key)
+ return nil, fmt.Errorf(errKeyDoesNotExist, ref.Property, ref.Key)
}
return []byte(val.String()), nil
}
@@ -361,7 +357,7 @@ func getSecretData(ibm *providerIBM, secretName *string, secretType, secretGroup
// secret name has been provided instead of id
if secretGroupName == "" {
// secret group name is not provided
- return nil, fmt.Errorf("failed to fetch the secret, secret group name is missing")
+ return nil, errors.New("failed to fetch the secret, secret group name is missing")
}
// secret group name is provided along with secret name,
@@ -398,7 +394,7 @@ func getSecretData(ibm *providerIBM, secretName *string, secretType, secretGroup
func (ibm *providerIBM) GetSecretMap(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
if utils.IsNil(ibm.IBMClient) {
- return nil, fmt.Errorf(errUninitalizedIBMProvider)
+ return nil, errors.New(errUninitializedIBMProvider)
}
var secretGroupName string
secretType := sm.Secret_SecretType_Arbitrary
@@ -433,7 +429,7 @@ func (ibm *providerIBM) GetSecretMap(_ context.Context, ref esv1beta1.ExternalSe
checkNilFn := func(propertyList []string) error {
for _, prop := range propertyList {
if _, ok := secMap[prop]; !ok {
- return fmt.Errorf("key %s does not exist in secret %s", prop, secretName)
+ return fmt.Errorf(errKeyDoesNotExist, prop, secretName)
}
}
return nil
@@ -545,7 +541,7 @@ func (ibm *providerIBM) ValidateStore(store esv1beta1.GenericStore) (admission.W
storeSpec := store.GetSpec()
ibmSpec := storeSpec.Provider.IBM
if ibmSpec.ServiceURL == nil {
- return nil, fmt.Errorf("serviceURL is required")
+ return nil, errors.New("serviceURL is required")
}
containerRef := ibmSpec.Auth.ContainerAuth
@@ -557,15 +553,15 @@ func (ibm *providerIBM) ValidateStore(store esv1beta1.GenericStore) (admission.W
if missingContainerRef == missingSecretRef {
// since both are equal, if one is missing assume both are missing
if missingContainerRef {
- return nil, fmt.Errorf("missing auth method")
+ return nil, errors.New("missing auth method")
}
- return nil, fmt.Errorf("too many auth methods defined")
+ return nil, errors.New("too many auth methods defined")
}
if !missingContainerRef {
// catch undefined container auth profile
if containerRef.Profile == "" {
- return nil, fmt.Errorf("container auth profile cannot be empty")
+ return nil, errors.New("container auth profile cannot be empty")
}
// proceed with container auth
@@ -585,10 +581,10 @@ func (ibm *providerIBM) ValidateStore(store esv1beta1.GenericStore) (admission.W
return nil, err
}
if secretKeyRef.Name == "" {
- return nil, fmt.Errorf("secretAPIKey.name cannot be empty")
+ return nil, errors.New("secretAPIKey.name cannot be empty")
}
if secretKeyRef.Key == "" {
- return nil, fmt.Errorf("secretAPIKey.key cannot be empty")
+ return nil, errors.New("secretAPIKey.key cannot be empty")
}
return nil, nil
diff --git a/pkg/provider/ibm/provider_test.go b/pkg/provider/ibm/provider_test.go
index 7e72ebda8f0..d4334a2219c 100644
--- a/pkg/provider/ibm/provider_test.go
+++ b/pkg/provider/ibm/provider_test.go
@@ -17,6 +17,7 @@ package ibm
import (
"context"
"encoding/json"
+ "errors"
"fmt"
"reflect"
"strconv"
@@ -67,7 +68,7 @@ func makeValidSecretManagerTestCase() *secretManagerTestCase {
ref: makeValidRef(),
apiOutput: makeValidAPIOutput(),
getByNameInput: makeValidGetByNameInput(),
- getByNameOutput: makeValidGetByNameOutput(),
+ getByNameOutput: makeValidAPIOutput(),
getByNameError: nil,
serviceURL: nil,
apiErr: nil,
@@ -114,16 +115,6 @@ func makeValidGetByNameInput() *sm.GetSecretByNameTypeOptions {
return &sm.GetSecretByNameTypeOptions{}
}
-func makeValidGetByNameOutput() sm.SecretIntf {
- secret := &sm.Secret{
- SecretType: utilpointer.To(sm.Secret_SecretType_Arbitrary),
- Name: utilpointer.To("testyname"),
- ID: utilpointer.To(secretUUID),
- }
- var i sm.SecretIntf = secret
- return i
-}
-
func makeValidSecretManagerTestCaseCustom(tweaks ...func(smtc *secretManagerTestCase)) *secretManagerTestCase {
smtc := makeValidSecretManagerTestCase()
for _, fn := range tweaks {
@@ -144,13 +135,13 @@ func makeValidSecretManagerTestCaseCustom(tweaks ...func(smtc *secretManagerTest
// This case can be shared by both GetSecret and GetSecretMap tests.
// bad case: set apiErr.
var setAPIErr = func(smtc *secretManagerTestCase) {
- smtc.apiErr = fmt.Errorf("oh no")
+ smtc.apiErr = errors.New("oh no")
smtc.expectError = "oh no"
}
var setNilMockClient = func(smtc *secretManagerTestCase) {
smtc.mockClient = nil
- smtc.expectError = errUninitalizedIBMProvider
+ smtc.expectError = errUninitializedIBMProvider
}
// simple tests for Validate Store.
@@ -165,7 +156,7 @@ func TestValidateStore(t *testing.T) {
}
_, err := p.ValidateStore(store)
if err == nil {
- t.Errorf(errExpectedErr)
+ t.Error(errExpectedErr)
} else if err.Error() != "serviceURL is required" {
t.Errorf("service URL test failed")
}
@@ -173,7 +164,7 @@ func TestValidateStore(t *testing.T) {
store.Spec.Provider.IBM.ServiceURL = &url
_, err = p.ValidateStore(store)
if err == nil {
- t.Errorf(errExpectedErr)
+ t.Error(errExpectedErr)
} else if err.Error() != "missing auth method" {
t.Errorf("KeySelector test failed: expected missing auth method, got %v", err)
}
@@ -187,8 +178,8 @@ func TestValidateStore(t *testing.T) {
}
_, err = p.ValidateStore(store)
if err == nil {
- t.Errorf(errExpectedErr)
- } else if err.Error() != "namespace not allowed with namespaced SecretStore" {
+ t.Error(errExpectedErr)
+ } else if err.Error() != "namespace should either be empty or match the namespace of the SecretStore for a namespaced SecretStore" {
t.Errorf("KeySelector test failed: expected namespace not allowed, got %v", err)
}
diff --git a/pkg/provider/infisical/api/api.go b/pkg/provider/infisical/api/api.go
new file mode 100644
index 00000000000..f1586f451df
--- /dev/null
+++ b/pkg/provider/infisical/api/api.go
@@ -0,0 +1,304 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impliec.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package api
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "strconv"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ "github.com/external-secrets/external-secrets/pkg/metrics"
+ "github.com/external-secrets/external-secrets/pkg/provider/infisical/constants"
+)
+
+type InfisicalClient struct {
+ BaseURL *url.URL
+ client *http.Client
+ token string
+}
+
+type InfisicalApis interface {
+ MachineIdentityLoginViaUniversalAuth(data MachineIdentityUniversalAuthLoginRequest) (*MachineIdentityDetailsResponse, error)
+ GetSecretsV3(data GetSecretsV3Request) (map[string]string, error)
+ GetSecretByKeyV3(data GetSecretByKeyV3Request) (string, error)
+ RevokeAccessToken() error
+}
+
+const (
+ machineIdentityLoginViaUniversalAuth = "MachineIdentityLoginViaUniversalAuth"
+ getSecretsV3 = "GetSecretsV3"
+ getSecretByKeyV3 = "GetSecretByKeyV3"
+ revokeAccessToken = "RevokeAccessToken"
+)
+
+const UserAgentName = "k8-external-secrets-operator"
+
+var errJSONUnmarshal = errors.New("unable to unmarshal API response")
+var errNoAccessToken = errors.New("unexpected error: no access token available to revoke")
+var errAccessTokenAlreadyRetrieved = errors.New("unexpected error: access token was already retrieved")
+
+type InfisicalAPIError struct {
+ StatusCode int
+ Err any
+ Message any
+ Details any
+}
+
+func (e *InfisicalAPIError) Error() string {
+ if e.Details != nil {
+ detailsJSON, _ := json.Marshal(e.Details)
+ return fmt.Sprintf("API error (%d): error=%v message=%v, details=%s", e.StatusCode, e.Err, e.Message, string(detailsJSON))
+ } else {
+ return fmt.Sprintf("API error (%d): error=%v message=%v", e.StatusCode, e.Err, e.Message)
+ }
+}
+
+// checkError checks for an error on the http response and generates an appropriate error if one is
+// found.
+func checkError(resp *http.Response) error {
+ if resp.StatusCode >= 200 && resp.StatusCode < 400 {
+ return nil
+ }
+
+ var buf bytes.Buffer
+ _, err := buf.ReadFrom(resp.Body)
+ if err != nil {
+ return fmt.Errorf("API error (%d) and failed to read response body: %w", resp.StatusCode, err)
+ }
+
+ // Attempt to unmarshal the response body into an InfisicalAPIErrorResponse.
+ var errRes InfisicalAPIErrorResponse
+ err = json.Unmarshal(buf.Bytes(), &errRes)
+ // Non-200 errors that cannot be unmarshaled must be handled, as errors could come from outside of
+ // Infisical.
+ if err != nil {
+ return fmt.Errorf("API error (%d), could not unmarshal error response: %w", resp.StatusCode, err)
+ } else if errRes.StatusCode == 0 {
+ // When the InfisicalResponse has a zero-value status code, then the
+ // response was either malformed or not from Infisical. Instead, just return
+ // the error string from the response.
+ return fmt.Errorf("API error (%d): %s", resp.StatusCode, buf.String())
+ } else {
+ return &InfisicalAPIError{
+ StatusCode: resp.StatusCode,
+ Message: errRes.Message,
+ Err: errRes.Error,
+ Details: errRes.Details,
+ }
+ }
+}
+
+func NewAPIClient(baseURL string, client *http.Client) (*InfisicalClient, error) {
+ baseParsedURL, err := url.Parse(baseURL)
+ if err != nil {
+ return nil, err
+ }
+
+ api := &InfisicalClient{
+ BaseURL: baseParsedURL,
+ client: client,
+ }
+
+ return api, nil
+}
+
+func (a *InfisicalClient) SetTokenViaMachineIdentity(clientID, clientSecret string) error {
+ if a.token != "" {
+ return errAccessTokenAlreadyRetrieved
+ }
+
+ var loginResponse MachineIdentityDetailsResponse
+ err := a.do(
+ "api/v1/auth/universal-auth/login",
+ http.MethodPost,
+ map[string]string{},
+ MachineIdentityUniversalAuthLoginRequest{
+ ClientID: clientID,
+ ClientSecret: clientSecret,
+ },
+ &loginResponse,
+ )
+ metrics.ObserveAPICall(constants.ProviderName, machineIdentityLoginViaUniversalAuth, err)
+
+ if err != nil {
+ return err
+ }
+
+ a.token = loginResponse.AccessToken
+ return nil
+}
+
+func (a *InfisicalClient) RevokeAccessToken() error {
+ if a.token == "" {
+ return errNoAccessToken
+ }
+
+ var revokeResponse RevokeMachineIdentityAccessTokenResponse
+ err := a.do(
+ "api/v1/auth/token/revoke",
+ http.MethodPost,
+ map[string]string{},
+ RevokeMachineIdentityAccessTokenRequest{AccessToken: a.token},
+ &revokeResponse,
+ )
+ metrics.ObserveAPICall(constants.ProviderName, revokeAccessToken, err)
+
+ if err != nil {
+ return err
+ }
+
+ a.token = ""
+ return nil
+}
+
+func (a *InfisicalClient) resolveEndpoint(path string) string {
+ return a.BaseURL.ResolveReference(&url.URL{Path: path}).String()
+}
+
+func (a *InfisicalClient) addHeaders(r *http.Request) {
+ if a.token != "" {
+ r.Header.Add("Authorization", "Bearer "+a.token)
+ }
+ r.Header.Add("User-Agent", UserAgentName)
+ r.Header.Add("Content-Type", "application/json")
+}
+
+// do is a generic function that makes an API call to the Infisical API, and handle the response
+// (including if an API error is returned).
+func (a *InfisicalClient) do(endpoint, method string, params map[string]string, body, response any) error {
+ endpointURL := a.resolveEndpoint(endpoint)
+
+ bodyReader, err := MarshalReqBody(body)
+ if err != nil {
+ return err
+ }
+
+ r, err := http.NewRequest(method, endpointURL, bodyReader)
+ if err != nil {
+ return err
+ }
+
+ a.addHeaders(r)
+
+ q := r.URL.Query()
+ for key, value := range params {
+ q.Add(key, value)
+ }
+ r.URL.RawQuery = q.Encode()
+
+ resp, err := a.client.Do(r)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ if err := checkError(resp); err != nil {
+ return err
+ }
+
+ bodyBytes, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return err
+ }
+
+ err = json.Unmarshal(bodyBytes, response)
+ if err != nil {
+ // Importantly, we do not include the response in the actual error to avoid
+ // leaking anything sensitive.
+ return errJSONUnmarshal
+ }
+
+ return nil
+}
+
+func (a *InfisicalClient) GetSecretsV3(data GetSecretsV3Request) (map[string]string, error) {
+ params := map[string]string{
+ "workspaceSlug": data.ProjectSlug,
+ "environment": data.EnvironmentSlug,
+ "secretPath": data.SecretPath,
+ "include_imports": "true",
+ "expandSecretReferences": "true",
+ "recursive": strconv.FormatBool(data.Recursive),
+ }
+
+ res := GetSecretsV3Response{}
+ err := a.do(
+ "api/v3/secrets/raw",
+ http.MethodGet,
+ params,
+ http.NoBody,
+ &res,
+ )
+ metrics.ObserveAPICall(constants.ProviderName, getSecretsV3, err)
+ if err != nil {
+ return nil, err
+ }
+
+ secrets := make(map[string]string)
+ for _, s := range res.ImportedSecrets {
+ for _, el := range s.Secrets {
+ secrets[el.SecretKey] = el.SecretValue
+ }
+ }
+ for _, el := range res.Secrets {
+ secrets[el.SecretKey] = el.SecretValue
+ }
+
+ return secrets, nil
+}
+
+func (a *InfisicalClient) GetSecretByKeyV3(data GetSecretByKeyV3Request) (string, error) {
+ params := map[string]string{
+ "workspaceSlug": data.ProjectSlug,
+ "environment": data.EnvironmentSlug,
+ "secretPath": data.SecretPath,
+ "include_imports": "true",
+ }
+
+ endpointURL := fmt.Sprintf("api/v3/secrets/raw/%s", data.SecretKey)
+
+ res := GetSecretByKeyV3Response{}
+ err := a.do(
+ endpointURL,
+ http.MethodGet,
+ params,
+ http.NoBody,
+ &res,
+ )
+ metrics.ObserveAPICall(constants.ProviderName, getSecretByKeyV3, err)
+ if err != nil {
+ var apiErr *InfisicalAPIError
+ if errors.As(err, &apiErr) && apiErr.StatusCode == 404 {
+ return "", esv1beta1.NoSecretError{}
+ }
+ return "", err
+ }
+
+ return res.Secret.SecretValue, nil
+}
+
+func MarshalReqBody(data any) (*bytes.Reader, error) {
+ body, err := json.Marshal(data)
+ if err != nil {
+ return nil, err
+ }
+ return bytes.NewReader(body), nil
+}
diff --git a/pkg/provider/infisical/api/api_fake.go b/pkg/provider/infisical/api/api_fake.go
new file mode 100644
index 00000000000..0eefc405505
--- /dev/null
+++ b/pkg/provider/infisical/api/api_fake.go
@@ -0,0 +1,47 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package api
+
+import (
+ "encoding/json"
+ "net/http"
+ "net/http/httptest"
+)
+
+func newMockServer(status int, data any) *httptest.Server {
+ body, err := json.Marshal(data)
+ if err != nil {
+ panic(err)
+ }
+
+ return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.WriteHeader(status)
+ _, err := w.Write(body)
+ if err != nil {
+ panic(err)
+ }
+ }))
+}
+
+// NewMockClient creates an InfisicalClient with a mocked HTTP client that has a
+// fixed response.
+func NewMockClient(status int, data any) (*InfisicalClient, func()) {
+ server := newMockServer(status, data)
+ client, err := NewAPIClient(server.URL, server.Client())
+ if err != nil {
+ panic(err)
+ }
+ return client, server.Close
+}
diff --git a/pkg/provider/infisical/api/api_models.go b/pkg/provider/infisical/api/api_models.go
new file mode 100644
index 00000000000..55cbe26f894
--- /dev/null
+++ b/pkg/provider/infisical/api/api_models.go
@@ -0,0 +1,90 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impliec.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package api
+
+type MachineIdentityUniversalAuthRefreshRequest struct {
+ AccessToken string `json:"accessToken"`
+}
+
+type MachineIdentityDetailsResponse struct {
+ AccessToken string `json:"accessToken"`
+ ExpiresIn int `json:"expiresIn"`
+ AccessTokenMaxTTL int `json:"accessTokenMaxTTL"`
+ TokenType string `json:"tokenType"`
+}
+
+type MachineIdentityUniversalAuthLoginRequest struct {
+ ClientID string `json:"clientId"`
+ ClientSecret string `json:"clientSecret"`
+}
+
+type RevokeMachineIdentityAccessTokenRequest struct {
+ AccessToken string `json:"accessToken"`
+}
+
+type RevokeMachineIdentityAccessTokenResponse struct {
+ Message string `json:"message"`
+}
+
+type GetSecretByKeyV3Request struct {
+ EnvironmentSlug string `json:"environment"`
+ ProjectSlug string `json:"workspaceSlug"`
+ SecretPath string `json:"secretPath"`
+ SecretKey string `json:"secretKey"`
+}
+
+type GetSecretByKeyV3Response struct {
+ Secret SecretsV3 `json:"secret"`
+}
+
+type GetSecretsV3Request struct {
+ EnvironmentSlug string `json:"environment"`
+ ProjectSlug string `json:"workspaceSlug"`
+ Recursive bool `json:"recursive"`
+ SecretPath string `json:"secretPath"`
+}
+
+type GetSecretsV3Response struct {
+ Secrets []SecretsV3 `json:"secrets"`
+ ImportedSecrets []ImportedSecretV3 `json:"imports,omitempty"`
+ Modified bool `json:"modified,omitempty"`
+ ETag string `json:"ETag,omitempty"`
+}
+
+type SecretsV3 struct {
+ ID string `json:"id"`
+ Workspace string `json:"workspace"`
+ Environment string `json:"environment"`
+ Version int `json:"version"`
+ Type string `json:"string"`
+ SecretKey string `json:"secretKey"`
+ SecretValue string `json:"secretValue"`
+ SecretComment string `json:"secretComment"`
+}
+
+type ImportedSecretV3 struct {
+ Environment string `json:"environment"`
+ FolderID string `json:"folderId"`
+ SecretPath string `json:"secretPath"`
+ Secrets []SecretsV3 `json:"secrets"`
+}
+
+type InfisicalAPIErrorResponse struct {
+ StatusCode int `json:"statusCode"`
+ Message string `json:"message"`
+ Error string `json:"error"`
+ // According to Infisical's API docs, `details` are only returned for 403 errors.
+ Details any `json:"details,omitempty"`
+}
diff --git a/pkg/provider/infisical/api/api_test.go b/pkg/provider/infisical/api/api_test.go
new file mode 100644
index 00000000000..8547b549d6c
--- /dev/null
+++ b/pkg/provider/infisical/api/api_test.go
@@ -0,0 +1,317 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package api
+
+import (
+ "errors"
+ "reflect"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+)
+
+const (
+ fakeClientID = "client-id"
+ fakeClientSecret = "client-secret"
+ fakeToken = "token"
+ fakeProjectSlug = "first-project"
+ fakeEnvironmentSlug = "dev"
+)
+
+func TestAPIClientDo(t *testing.T) {
+ apiURL := "foo"
+ httpMethod := "bar"
+
+ testCases := []struct {
+ Name string
+ MockStatusCode int
+ MockResponse any
+ ExpectedResponse any
+ ExpectedError error
+ }{
+ {
+ Name: "Success",
+ MockStatusCode: 200,
+ MockResponse: MachineIdentityDetailsResponse{
+ AccessToken: "foobar",
+ },
+ ExpectedResponse: MachineIdentityDetailsResponse{
+ AccessToken: "foobar",
+ },
+ ExpectedError: nil,
+ },
+ {
+ Name: "Error when response cannot be unmarshalled",
+ MockStatusCode: 500,
+ MockResponse: []byte("not-json"),
+ ExpectedError: errors.New("API error (500), could not unmarshal error response: json: cannot unmarshal string into Go value of type api.InfisicalAPIErrorResponse"),
+ },
+ {
+ Name: "Error when non-Infisical error response received",
+ MockStatusCode: 500,
+ MockResponse: map[string]string{"foo": "bar"},
+ ExpectedError: errors.New("API error (500): {\"foo\":\"bar\"}"),
+ },
+ {
+ Name: "Do: Error when non-200 response received",
+ MockStatusCode: 401,
+ MockResponse: InfisicalAPIErrorResponse{
+ StatusCode: 401,
+ Error: "Unauthorized",
+ },
+ ExpectedError: &InfisicalAPIError{StatusCode: 401, Err: "Unauthorized", Message: ""},
+ },
+ {
+ Name: "Error when arbitrary details are returned",
+ MockStatusCode: 401,
+ MockResponse: InfisicalAPIErrorResponse{
+ StatusCode: 401,
+ Error: "Unauthorized",
+ Details: map[string]string{"foo": "details"},
+ },
+ ExpectedError: &InfisicalAPIError{StatusCode: 401, Err: "Unauthorized", Message: "", Details: map[string]string{"foo": "details"}},
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.Name, func(t *testing.T) {
+ apiClient, closeFunc := NewMockClient(tc.MockStatusCode, tc.MockResponse)
+ defer closeFunc()
+
+ // Automatically pluck out the expected response type using reflection to create a new empty value for unmarshalling.
+ var actualResponse any
+ if tc.ExpectedResponse != nil {
+ actualResponse = reflect.New(reflect.TypeOf(tc.ExpectedResponse)).Interface()
+ }
+
+ err := apiClient.do(apiURL, httpMethod, nil, nil, actualResponse)
+ if tc.ExpectedError != nil {
+ assert.Error(t, err)
+ assert.Equal(t, tc.ExpectedError.Error(), err.Error())
+ } else {
+ assert.NoError(t, err)
+ assert.Equal(t, tc.ExpectedResponse, reflect.ValueOf(actualResponse).Elem().Interface())
+ }
+ })
+ }
+}
+
+// TestAPIClientDoInvalidResponse tests the case where the response is a 200 but does not unmarshal
+// correctly.
+func TestAPIClientDoInvalidResponse(t *testing.T) {
+ apiClient, closeFunc := NewMockClient(200, []byte("not-json"))
+ defer closeFunc()
+
+ err := apiClient.do("foo", "bar", nil, nil, nil)
+ assert.ErrorIs(t, err, errJSONUnmarshal)
+}
+
+func TestSetTokenViaMachineIdentity(t *testing.T) {
+ t.Run("Success", func(t *testing.T) {
+ apiClient, closeFunc := NewMockClient(200, MachineIdentityDetailsResponse{
+ AccessToken: "foobar",
+ })
+ defer closeFunc()
+
+ err := apiClient.SetTokenViaMachineIdentity(fakeClientID, fakeClientSecret)
+ assert.NoError(t, err)
+ assert.Equal(t, apiClient.token, "foobar")
+ })
+
+ t.Run("SetTokenViaMachineIdentity: Error when non-200 response received", func(t *testing.T) {
+ apiClient, closeFunc := NewMockClient(401, InfisicalAPIErrorResponse{
+ StatusCode: 401,
+ Error: "Unauthorized",
+ })
+ defer closeFunc()
+
+ err := apiClient.SetTokenViaMachineIdentity(fakeClientID, fakeClientSecret)
+ assert.Error(t, err)
+ var apiErr *InfisicalAPIError
+ assert.True(t, errors.As(err, &apiErr))
+ assert.Equal(t, 401, apiErr.StatusCode)
+ assert.Equal(t, "Unauthorized", apiErr.Err)
+ })
+
+ t.Run("Error when token already set", func(t *testing.T) {
+ apiClient, closeFunc := NewMockClient(401, nil)
+ defer closeFunc()
+
+ apiClient.token = fakeToken
+
+ err := apiClient.SetTokenViaMachineIdentity(fakeClientID, fakeClientSecret)
+ assert.ErrorIs(t, err, errAccessTokenAlreadyRetrieved)
+ })
+}
+
+func TestRevokeAccessToken(t *testing.T) {
+ t.Run("Success", func(t *testing.T) {
+ apiClient, closeFunc := NewMockClient(200, RevokeMachineIdentityAccessTokenResponse{
+ Message: "Success",
+ })
+ defer closeFunc()
+
+ apiClient.token = fakeToken
+
+ err := apiClient.RevokeAccessToken()
+ assert.NoError(t, err)
+ // Verify that the access token was unset.
+ assert.Equal(t, apiClient.token, "")
+ })
+
+ t.Run("RevokeAccessToken: Error when non-200 response received", func(t *testing.T) {
+ apiClient, closeFunc := NewMockClient(401, InfisicalAPIErrorResponse{
+ StatusCode: 401,
+ Error: "Unauthorized",
+ })
+ defer closeFunc()
+
+ apiClient.token = fakeToken
+
+ err := apiClient.RevokeAccessToken()
+ assert.Error(t, err)
+ var apiErr *InfisicalAPIError
+ assert.True(t, errors.As(err, &apiErr))
+ assert.Equal(t, 401, apiErr.StatusCode)
+ assert.Equal(t, "Unauthorized", apiErr.Err)
+ })
+
+ t.Run("Error when no access token is set", func(t *testing.T) {
+ apiClient, closeFunc := NewMockClient(401, nil)
+ defer closeFunc()
+
+ err := apiClient.RevokeAccessToken()
+ assert.ErrorIs(t, err, errNoAccessToken)
+ })
+}
+
+func TestGetSecretsV3(t *testing.T) {
+ t.Run("Works with secrets", func(t *testing.T) {
+ apiClient, closeFunc := NewMockClient(200, GetSecretsV3Response{
+ Secrets: []SecretsV3{
+ {SecretKey: "foo", SecretValue: "bar"},
+ },
+ })
+ defer closeFunc()
+
+ secrets, err := apiClient.GetSecretsV3(GetSecretsV3Request{
+ ProjectSlug: fakeProjectSlug,
+ EnvironmentSlug: fakeEnvironmentSlug,
+ SecretPath: "/",
+ Recursive: true,
+ })
+ assert.NoError(t, err)
+ assert.Equal(t, secrets, map[string]string{"foo": "bar"})
+ })
+
+ t.Run("Works with imported secrets", func(t *testing.T) {
+ apiClient, closeFunc := NewMockClient(200, GetSecretsV3Response{
+ ImportedSecrets: []ImportedSecretV3{{
+ Secrets: []SecretsV3{{SecretKey: "foo", SecretValue: "bar"}},
+ }},
+ })
+ defer closeFunc()
+
+ secrets, err := apiClient.GetSecretsV3(GetSecretsV3Request{
+ ProjectSlug: fakeProjectSlug,
+ EnvironmentSlug: fakeEnvironmentSlug,
+ SecretPath: "/",
+ Recursive: true,
+ })
+ assert.NoError(t, err)
+ assert.Equal(t, secrets, map[string]string{"foo": "bar"})
+ })
+
+ t.Run("GetSecretsV3: Error when non-200 response received", func(t *testing.T) {
+ apiClient, closeFunc := NewMockClient(401, InfisicalAPIErrorResponse{
+ StatusCode: 401,
+ Error: "Unauthorized",
+ })
+ defer closeFunc()
+
+ _, err := apiClient.GetSecretsV3(GetSecretsV3Request{
+ ProjectSlug: fakeProjectSlug,
+ EnvironmentSlug: fakeEnvironmentSlug,
+ SecretPath: "/",
+ Recursive: true,
+ })
+ assert.Error(t, err)
+ var apiErr *InfisicalAPIError
+ assert.True(t, errors.As(err, &apiErr))
+ assert.Equal(t, 401, apiErr.StatusCode)
+ assert.Equal(t, "Unauthorized", apiErr.Err)
+ })
+}
+func TestGetSecretByKeyV3(t *testing.T) {
+ t.Run("Works", func(t *testing.T) {
+ apiClient, closeFunc := NewMockClient(200, GetSecretByKeyV3Response{
+ Secret: SecretsV3{
+ SecretKey: "foo",
+ SecretValue: "bar",
+ },
+ })
+ defer closeFunc()
+
+ secret, err := apiClient.GetSecretByKeyV3(GetSecretByKeyV3Request{
+ ProjectSlug: fakeProjectSlug,
+ EnvironmentSlug: fakeEnvironmentSlug,
+ SecretPath: "/",
+ SecretKey: "foo",
+ })
+ assert.NoError(t, err)
+ assert.Equal(t, "bar", secret)
+ })
+
+ t.Run("Error when secret is not found", func(t *testing.T) {
+ apiClient, closeFunc := NewMockClient(404, InfisicalAPIErrorResponse{
+ StatusCode: 404,
+ Error: "Not Found",
+ })
+ defer closeFunc()
+
+ _, err := apiClient.GetSecretByKeyV3(GetSecretByKeyV3Request{
+ ProjectSlug: fakeProjectSlug,
+ EnvironmentSlug: fakeEnvironmentSlug,
+ SecretPath: "/",
+ SecretKey: "foo",
+ })
+ assert.Error(t, err)
+ // Importantly, we return the standard error for no secrets found.
+ assert.ErrorIs(t, err, esv1beta1.NoSecretError{})
+ })
+
+ // Test case where the request is unauthorized
+ t.Run("ErrorHandlingUnauthorized", func(t *testing.T) {
+ apiClient, closeFunc := NewMockClient(401, InfisicalAPIErrorResponse{
+ StatusCode: 401,
+ Error: "Unauthorized",
+ })
+ defer closeFunc()
+
+ _, err := apiClient.GetSecretByKeyV3(GetSecretByKeyV3Request{
+ ProjectSlug: fakeProjectSlug,
+ EnvironmentSlug: fakeEnvironmentSlug,
+ SecretPath: "/",
+ SecretKey: "foo",
+ })
+ assert.Error(t, err)
+ var apiErr *InfisicalAPIError
+ assert.True(t, errors.As(err, &apiErr))
+ assert.Equal(t, 401, apiErr.StatusCode)
+ assert.Equal(t, "Unauthorized", apiErr.Err)
+ })
+}
diff --git a/pkg/provider/infisical/client.go b/pkg/provider/infisical/client.go
new file mode 100644
index 00000000000..a11891da76e
--- /dev/null
+++ b/pkg/provider/infisical/client.go
@@ -0,0 +1,172 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impliec.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package infisical
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "strings"
+
+ "github.com/tidwall/gjson"
+ corev1 "k8s.io/api/core/v1"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ "github.com/external-secrets/external-secrets/pkg/find"
+ "github.com/external-secrets/external-secrets/pkg/provider/infisical/api"
+)
+
+var (
+ errNotImplemented = errors.New("not implemented")
+ errPropertyNotFound = "property %s does not exist in secret %s"
+ errTagsNotImplemented = errors.New("find by tags not supported")
+)
+
+func getPropertyValue(jsonData, propertyName, keyName string) ([]byte, error) {
+ result := gjson.Get(jsonData, propertyName)
+ if !result.Exists() {
+ return nil, fmt.Errorf(errPropertyNotFound, propertyName, keyName)
+ }
+ return []byte(result.Str), nil
+}
+
+// if GetSecret returns an error with type NoSecretError.
+// then the secret entry will be deleted depending on the deletionPolicy.
+func (p *Provider) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
+ secret, err := p.apiClient.GetSecretByKeyV3(api.GetSecretByKeyV3Request{
+ EnvironmentSlug: p.apiScope.EnvironmentSlug,
+ ProjectSlug: p.apiScope.ProjectSlug,
+ SecretKey: ref.Key,
+ SecretPath: p.apiScope.SecretPath,
+ })
+
+ if err != nil {
+ return nil, err
+ }
+
+ if ref.Property != "" {
+ propertyValue, err := getPropertyValue(secret, ref.Property, ref.Key)
+ if err != nil {
+ return nil, err
+ }
+
+ return propertyValue, nil
+ }
+
+ return []byte(secret), nil
+}
+
+// GetSecretMap returns multiple k/v pairs from the provider.
+func (p *Provider) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
+ secret, err := p.GetSecret(ctx, ref)
+ if err != nil {
+ return nil, err
+ }
+
+ kv := make(map[string]json.RawMessage)
+ err = json.Unmarshal(secret, &kv)
+ if err != nil {
+ return nil, fmt.Errorf("unable to unmarshal secret %s: %w", ref.Key, err)
+ }
+ secretData := make(map[string][]byte)
+ for k, v := range kv {
+ var strVal string
+ err = json.Unmarshal(v, &strVal)
+ if err == nil {
+ secretData[k] = []byte(strVal)
+ } else {
+ secretData[k] = v
+ }
+ }
+ return secretData, nil
+}
+
+// GetAllSecrets returns multiple k/v pairs from the provider.
+func (p *Provider) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
+ if ref.Tags != nil {
+ return nil, errTagsNotImplemented
+ }
+
+ secrets, err := p.apiClient.GetSecretsV3(api.GetSecretsV3Request{
+ EnvironmentSlug: p.apiScope.EnvironmentSlug,
+ ProjectSlug: p.apiScope.ProjectSlug,
+ SecretPath: p.apiScope.SecretPath,
+ Recursive: p.apiScope.Recursive,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ secretMap := make(map[string][]byte)
+ for key, value := range secrets {
+ secretMap[key] = []byte(value)
+ }
+ if ref.Name == nil && ref.Path == nil {
+ return secretMap, nil
+ }
+
+ var matcher *find.Matcher
+ if ref.Name != nil {
+ m, err := find.New(*ref.Name)
+ if err != nil {
+ return nil, err
+ }
+ matcher = m
+ }
+
+ selected := map[string][]byte{}
+ for key, value := range secrets {
+ if (matcher != nil && !matcher.MatchName(key)) || (ref.Path != nil && !strings.HasPrefix(key, *ref.Path)) {
+ continue
+ }
+ selected[key] = []byte(value)
+ }
+ return selected, nil
+}
+
+// Validate checks if the client is configured correctly.
+// and is able to retrieve secrets from the provider.
+// If the validation result is unknown it will be ignored.
+func (p *Provider) Validate() (esv1beta1.ValidationResult, error) {
+ // try to fetch the secrets to ensure provided credentials has access to read secrets
+ _, err := p.apiClient.GetSecretsV3(api.GetSecretsV3Request{
+ EnvironmentSlug: p.apiScope.EnvironmentSlug,
+ ProjectSlug: p.apiScope.ProjectSlug,
+ Recursive: p.apiScope.Recursive,
+ SecretPath: p.apiScope.SecretPath,
+ })
+
+ if err != nil {
+ return esv1beta1.ValidationResultError, fmt.Errorf("cannot read secrets with provided project scope project:%s environment:%s secret-path:%s recursive:%t, %w", p.apiScope.ProjectSlug, p.apiScope.EnvironmentSlug, p.apiScope.SecretPath, p.apiScope.Recursive, err)
+ }
+
+ return esv1beta1.ValidationResultReady, nil
+}
+
+// PushSecret will write a single secret into the provider.
+func (p *Provider) PushSecret(ctx context.Context, secret *corev1.Secret, data esv1beta1.PushSecretData) error {
+ return errNotImplemented
+}
+
+// DeleteSecret will delete the secret from a provider.
+func (p *Provider) DeleteSecret(ctx context.Context, remoteRef esv1beta1.PushSecretRemoteRef) error {
+ return errNotImplemented
+}
+
+// SecretExists checks if a secret is already present in the provider at the given location.
+func (p *Provider) SecretExists(ctx context.Context, remoteRef esv1beta1.PushSecretRemoteRef) (bool, error) {
+ return false, errNotImplemented
+}
diff --git a/pkg/provider/infisical/constants/constants.go b/pkg/provider/infisical/constants/constants.go
new file mode 100644
index 00000000000..987fbe370e1
--- /dev/null
+++ b/pkg/provider/infisical/constants/constants.go
@@ -0,0 +1,19 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impliec.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package constants
+
+const (
+ UniversalAuth = "universal-auth"
+ ProviderName = "infisical"
+)
diff --git a/pkg/provider/infisical/provider.go b/pkg/provider/infisical/provider.go
new file mode 100644
index 00000000000..fbb9b798550
--- /dev/null
+++ b/pkg/provider/infisical/provider.go
@@ -0,0 +1,165 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implieclient.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package infisical
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "net/http"
+ "time"
+
+ ctrl "sigs.k8s.io/controller-runtime"
+ kclient "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+ "github.com/external-secrets/external-secrets/pkg/provider/infisical/api"
+ "github.com/external-secrets/external-secrets/pkg/provider/infisical/constants"
+ "github.com/external-secrets/external-secrets/pkg/utils"
+ "github.com/external-secrets/external-secrets/pkg/utils/resolvers"
+)
+
+var (
+ Logger = ctrl.Log.WithName("provider").WithName(constants.ProviderName)
+)
+
+type Provider struct {
+ apiClient *api.InfisicalClient
+ apiScope *InfisicalClientScope
+}
+
+type InfisicalClientScope struct {
+ EnvironmentSlug string
+ ProjectSlug string
+ Recursive bool
+ SecretPath string
+}
+
+// https://github.com/external-secrets/external-secrets/issues/644
+var _ esv1beta1.SecretsClient = &Provider{}
+var _ esv1beta1.Provider = &Provider{}
+
+func init() {
+ esv1beta1.Register(&Provider{}, &esv1beta1.SecretStoreProvider{
+ Infisical: &esv1beta1.InfisicalProvider{},
+ })
+}
+
+func (p *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
+ return esv1beta1.SecretStoreReadOnly
+}
+
+func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
+ storeSpec := store.GetSpec()
+
+ if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.Infisical == nil {
+ return nil, errors.New("invalid infisical store")
+ }
+
+ infisicalSpec := storeSpec.Provider.Infisical
+
+ apiClient, err := api.NewAPIClient(infisicalSpec.HostAPI, &http.Client{
+ Timeout: time.Second * 15,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ if infisicalSpec.Auth.UniversalAuthCredentials != nil {
+ universalAuthCredentials := infisicalSpec.Auth.UniversalAuthCredentials
+ clientID, err := GetStoreSecretData(ctx, store, kube, namespace, universalAuthCredentials.ClientID)
+ if err != nil {
+ return nil, err
+ }
+
+ clientSecret, err := GetStoreSecretData(ctx, store, kube, namespace, universalAuthCredentials.ClientSecret)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := apiClient.SetTokenViaMachineIdentity(clientID, clientSecret); err != nil {
+ return nil, fmt.Errorf("failed to authenticate via universal auth %w", err)
+ }
+
+ return &Provider{
+ apiClient: apiClient,
+ apiScope: &InfisicalClientScope{
+ EnvironmentSlug: infisicalSpec.SecretsScope.EnvironmentSlug,
+ ProjectSlug: infisicalSpec.SecretsScope.ProjectSlug,
+ Recursive: infisicalSpec.SecretsScope.Recursive,
+ SecretPath: infisicalSpec.SecretsScope.SecretsPath,
+ },
+ }, nil
+ }
+
+ return &Provider{}, errors.New("authentication method not found")
+}
+
+func (p *Provider) Close(ctx context.Context) error {
+ if err := p.apiClient.RevokeAccessToken(); err != nil {
+ return err
+ }
+ return nil
+}
+
+func GetStoreSecretData(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string, secret esmeta.SecretKeySelector) (string, error) {
+ secretRef := esmeta.SecretKeySelector{
+ Name: secret.Name,
+ Key: secret.Key,
+ }
+ if secret.Namespace != nil {
+ secretRef.Namespace = secret.Namespace
+ }
+
+ secretData, err := resolvers.SecretKeyRef(ctx, kube, store.GetObjectKind().GroupVersionKind().Kind, namespace, &secretRef)
+ if err != nil {
+ return "", err
+ }
+ return secretData, nil
+}
+
+func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnings, error) {
+ storeSpec := store.GetSpec()
+ infisicalStoreSpec := storeSpec.Provider.Infisical
+ if infisicalStoreSpec == nil {
+ return nil, errors.New("invalid infisical store")
+ }
+
+ if infisicalStoreSpec.SecretsScope.EnvironmentSlug == "" || infisicalStoreSpec.SecretsScope.ProjectSlug == "" {
+ return nil, errors.New("secretsScope.projectSlug and secretsScope.environmentSlug cannot be empty")
+ }
+
+ if infisicalStoreSpec.Auth.UniversalAuthCredentials != nil {
+ uaCredential := infisicalStoreSpec.Auth.UniversalAuthCredentials
+ // to validate reference authentication
+ err := utils.ValidateReferentSecretSelector(store, uaCredential.ClientID)
+ if err != nil {
+ return nil, err
+ }
+
+ err = utils.ValidateReferentSecretSelector(store, uaCredential.ClientSecret)
+ if err != nil {
+ return nil, err
+ }
+
+ if uaCredential.ClientID.Key == "" || uaCredential.ClientSecret.Key == "" {
+ return nil, errors.New("universalAuthCredentials.clientId and universalAuthCredentials.clientSecret cannot be empty")
+ }
+ }
+
+ return nil, nil
+}
diff --git a/pkg/provider/infisical/provider_test.go b/pkg/provider/infisical/provider_test.go
new file mode 100644
index 00000000000..6f68206c684
--- /dev/null
+++ b/pkg/provider/infisical/provider_test.go
@@ -0,0 +1,241 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or impliec.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package infisical
+
+import (
+ "context"
+ "errors"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/stretchr/testify/require"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ esv1meta "github.com/external-secrets/external-secrets/apis/meta/v1"
+ "github.com/external-secrets/external-secrets/pkg/provider/infisical/api"
+)
+
+type storeModifier func(*esv1beta1.SecretStore) *esv1beta1.SecretStore
+
+var apiScope = InfisicalClientScope{
+ SecretPath: "/",
+ ProjectSlug: "first-project",
+ EnvironmentSlug: "dev",
+}
+
+type TestCases struct {
+ Name string
+ MockStatusCode int
+ MockResponse any
+ Property string
+ Error error
+ Output any
+}
+
+func TestGetSecret(t *testing.T) {
+ key := "foo"
+
+ testCases := []TestCases{
+ {
+ Name: "Get_valid_key",
+ MockStatusCode: 200,
+ MockResponse: api.GetSecretByKeyV3Response{
+ Secret: api.SecretsV3{
+ SecretKey: key,
+ SecretValue: "bar",
+ },
+ },
+ Output: []byte("bar"),
+ },
+ {
+ Name: "Get_property_key",
+ MockStatusCode: 200,
+ MockResponse: api.GetSecretByKeyV3Response{
+ Secret: api.SecretsV3{
+ SecretKey: key,
+ SecretValue: `{"bar": "value"}`,
+ },
+ },
+ Property: "bar",
+ Output: []byte("value"),
+ },
+ {
+ Name: "Key_not_found",
+ MockStatusCode: 404,
+ MockResponse: api.InfisicalAPIError{
+ StatusCode: 404,
+ Err: "Not Found",
+ Message: "Secret not found",
+ },
+ Error: esv1beta1.NoSecretError{},
+ Output: "",
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.Name, func(t *testing.T) {
+ apiClient, closeFunc := api.NewMockClient(tc.MockStatusCode, tc.MockResponse)
+ defer closeFunc()
+ p := &Provider{
+ apiClient: apiClient,
+ apiScope: &apiScope,
+ }
+
+ output, err := p.GetSecret(context.Background(), esv1beta1.ExternalSecretDataRemoteRef{
+ Key: key,
+ Property: tc.Property,
+ })
+
+ if tc.Error == nil {
+ assert.NoError(t, err)
+ assert.Equal(t, tc.Output, output)
+ } else {
+ assert.ErrorAs(t, err, &tc.Error)
+ }
+ })
+ }
+}
+
+func TestGetSecretMap(t *testing.T) {
+ key := "foo"
+ testCases := []TestCases{
+ {
+ Name: "Get_valid_key_map",
+ MockStatusCode: 200,
+ MockResponse: api.GetSecretByKeyV3Response{
+ Secret: api.SecretsV3{
+ SecretKey: key,
+ SecretValue: `{"bar": "value"}`,
+ },
+ },
+ Output: map[string][]byte{
+ "bar": []byte("value"),
+ },
+ },
+ {
+ Name: "Get_invalid_map",
+ MockStatusCode: 200,
+ MockResponse: []byte(``),
+ Error: errors.New("unable to unmarshal secret foo"),
+ },
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.Name, func(t *testing.T) {
+ apiClient, closeFunc := api.NewMockClient(tc.MockStatusCode, tc.MockResponse)
+ defer closeFunc()
+ p := &Provider{
+ apiClient: apiClient,
+ apiScope: &apiScope,
+ }
+ output, err := p.GetSecretMap(context.Background(), esv1beta1.ExternalSecretDataRemoteRef{
+ Key: key,
+ })
+ if tc.Error == nil {
+ assert.NoError(t, err)
+ assert.Equal(t, tc.Output, output)
+ } else {
+ assert.ErrorAs(t, err, &tc.Error)
+ }
+ })
+ }
+}
+
+func makeSecretStore(projectSlug, environment, secretPath string, fn ...storeModifier) *esv1beta1.SecretStore {
+ store := &esv1beta1.SecretStore{
+ Spec: esv1beta1.SecretStoreSpec{
+ Provider: &esv1beta1.SecretStoreProvider{
+ Infisical: &esv1beta1.InfisicalProvider{
+ Auth: esv1beta1.InfisicalAuth{
+ UniversalAuthCredentials: &esv1beta1.UniversalAuthCredentials{},
+ },
+ SecretsScope: esv1beta1.MachineIdentityScopeInWorkspace{
+ SecretsPath: secretPath,
+ EnvironmentSlug: environment,
+ ProjectSlug: projectSlug,
+ },
+ },
+ },
+ },
+ }
+ for _, f := range fn {
+ store = f(store)
+ }
+ return store
+}
+
+func withClientID(name, key string, namespace *string) storeModifier {
+ return func(store *esv1beta1.SecretStore) *esv1beta1.SecretStore {
+ store.Spec.Provider.Infisical.Auth.UniversalAuthCredentials.ClientID = esv1meta.SecretKeySelector{
+ Name: name,
+ Key: key,
+ Namespace: namespace,
+ }
+ return store
+ }
+}
+
+func withClientSecret(name, key string, namespace *string) storeModifier {
+ return func(store *esv1beta1.SecretStore) *esv1beta1.SecretStore {
+ store.Spec.Provider.Infisical.Auth.UniversalAuthCredentials.ClientSecret = esv1meta.SecretKeySelector{
+ Name: name,
+ Key: key,
+ Namespace: namespace,
+ }
+ return store
+ }
+}
+
+type ValidateStoreTestCase struct {
+ store *esv1beta1.SecretStore
+ assertError func(t *testing.T, err error)
+}
+
+func TestValidateStore(t *testing.T) {
+ const randomID = "some-random-id"
+ const authType = "universal-auth"
+ var authCredMissingErr = errors.New("universalAuthCredentials.clientId and universalAuthCredentials.clientSecret cannot be empty")
+ var authScopeMissingErr = errors.New("secretsScope.projectSlug and secretsScope.environmentSlug cannot be empty")
+
+ testCases := []ValidateStoreTestCase{
+ {
+ store: makeSecretStore("", "", ""),
+ assertError: func(t *testing.T, err error) {
+ require.ErrorAs(t, err, &authScopeMissingErr)
+ },
+ },
+ {
+ store: makeSecretStore(apiScope.ProjectSlug, apiScope.EnvironmentSlug, apiScope.SecretPath, withClientID(authType, randomID, nil)),
+ assertError: func(t *testing.T, err error) {
+ require.ErrorAs(t, err, &authCredMissingErr)
+ },
+ },
+ {
+ store: makeSecretStore(apiScope.ProjectSlug, apiScope.EnvironmentSlug, apiScope.SecretPath, withClientSecret(authType, randomID, nil)),
+ assertError: func(t *testing.T, err error) {
+ require.ErrorAs(t, err, &authCredMissingErr)
+ },
+ },
+ {
+ store: makeSecretStore(apiScope.ProjectSlug, apiScope.EnvironmentSlug, apiScope.SecretPath, withClientID(authType, randomID, nil), withClientSecret(authType, randomID, nil)),
+ assertError: func(t *testing.T, err error) { require.NoError(t, err) },
+ },
+ }
+ p := Provider{}
+ for _, tc := range testCases {
+ _, err := p.ValidateStore(tc.store)
+ tc.assertError(t, err)
+ }
+}
diff --git a/pkg/provider/keepersecurity/client.go b/pkg/provider/keepersecurity/client.go
index 518855537e3..12ae9e80b85 100644
--- a/pkg/provider/keepersecurity/client.go
+++ b/pkg/provider/keepersecurity/client.go
@@ -19,11 +19,11 @@ import (
"encoding/json"
"errors"
"fmt"
+ "maps"
"regexp"
"strings"
ksm "github.com/keeper-security/secrets-manager-go/core"
- "golang.org/x/exp/maps"
corev1 "k8s.io/api/core/v1"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
@@ -46,7 +46,7 @@ const (
errInvalidJSONSecret = "invalid Secret. Secret %s can not be converted to JSON. %w"
errInvalidRegex = "find.name.regex. Invalid Regular expresion %s. %w"
errInvalidRemoteRefKey = "match.remoteRef.remoteKey. Invalid format. Format should match secretName/key got %s"
- errInvalidSecretType = "ESO can only push/delete %s record types. Secret %s is type %s"
+ errInvalidSecretType = "ESO can only push/delete records of type %s. Secret %s is type %s"
errFieldNotFound = "secret %s does not contain any custom field with label %s"
externalSecretType = "externalSecrets"
@@ -66,6 +66,7 @@ type Client struct {
type SecurityClient interface {
GetSecrets(filter []string) ([]*ksm.Record, error)
GetSecretByTitle(recordTitle string) (*ksm.Record, error)
+ GetSecretsByTitle(recordTitle string) (records []*ksm.Record, err error)
CreateSecretWithRecordData(recUID, folderUID string, recordData *ksm.RecordCreate) (string, error)
DeleteSecrets(recrecordUids []string) (map[string]string, error)
Save(record *ksm.Record) error
@@ -127,10 +128,10 @@ func (c *Client) GetSecretMap(_ context.Context, ref esv1beta1.ExternalSecretDat
func (c *Client) GetAllSecrets(_ context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
if ref.Tags != nil {
- return nil, fmt.Errorf(errTagsNotImplemented)
+ return nil, errors.New(errTagsNotImplemented)
}
if ref.Path != nil {
- return nil, fmt.Errorf(errPathNotImplemented)
+ return nil, errors.New(errPathNotImplemented)
}
secretData := make(map[string][]byte)
records, err := c.findSecrets()
@@ -164,7 +165,7 @@ func (c *Client) Close(_ context.Context) error {
func (c *Client) PushSecret(_ context.Context, secret *corev1.Secret, data esv1beta1.PushSecretData) error {
if data.GetSecretKey() == "" {
- return fmt.Errorf("pushing the whole secret is not yet implemented")
+ return errors.New("pushing the whole secret is not yet implemented")
}
value := secret.Data[data.GetSecretKey()]
@@ -172,24 +173,22 @@ func (c *Client) PushSecret(_ context.Context, secret *corev1.Secret, data esv1b
if err != nil {
return err
}
+
record, err := c.findSecretByName(parts[0])
if err != nil {
- _, err = c.createSecret(parts[0], parts[1], value)
- if err != nil {
- return err
- }
+ return err
}
+
if record != nil {
- if record.Type() != externalSecretType {
+ if record.Type() == externalSecretType {
+ return c.updateSecret(record, parts[1], value)
+ } else {
return fmt.Errorf(errInvalidSecretType, externalSecretType, record.Title(), record.Type())
}
- err = c.updateSecret(record, parts[1], value)
- if err != nil {
- return err
- }
+ } else {
+ _, err = c.createSecret(parts[0], parts[1], value)
+ return err
}
-
- return nil
}
func (c *Client) DeleteSecret(_ context.Context, remoteRef esv1beta1.PushSecretRemoteRef) error {
@@ -200,20 +199,19 @@ func (c *Client) DeleteSecret(_ context.Context, remoteRef esv1beta1.PushSecretR
secret, err := c.findSecretByName(parts[0])
if err != nil {
return err
+ } else if secret == nil {
+ return nil // not found == already deleted (success)
}
+
if secret.Type() != externalSecretType {
return fmt.Errorf(errInvalidSecretType, externalSecretType, secret.Title(), secret.Type())
}
_, err = c.ksmClient.DeleteSecrets([]string{secret.Uid})
- if err != nil {
- return nil
- }
-
- return nil
+ return err
}
func (c *Client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf("not implemented")
+ return false, errors.New("not implemented")
}
func (c *Client) buildSecretNameAndKey(remoteRef esv1beta1.PushSecretRemoteRef) ([]string, error) {
@@ -325,12 +323,32 @@ func (c *Client) findSecretByID(id string) (*ksm.Record, error) {
}
func (c *Client) findSecretByName(name string) (*ksm.Record, error) {
- record, err := c.ksmClient.GetSecretByTitle(name)
+ records, err := c.ksmClient.GetSecretsByTitle(name)
if err != nil {
return nil, err
}
- return record, nil
+ // filter in-place, preserve only records of type externalSecretType
+ n := 0
+ for _, record := range records {
+ if record.Type() == externalSecretType {
+ records[n] = record
+ n++
+ }
+ }
+ records = records[:n]
+
+ // record not found is not an error - handled differently:
+ // PushSecret will create new record instead
+ // DeleteSecret will consider record already deleted (no error)
+ if len(records) == 0 {
+ return nil, nil
+ } else if len(records) == 1 {
+ return records[0], nil
+ }
+
+ // len(records) > 1
+ return nil, fmt.Errorf(errKeeperSecuritySecretNotUnique, name)
}
func (s *Secret) validate() error {
diff --git a/pkg/provider/keepersecurity/client_test.go b/pkg/provider/keepersecurity/client_test.go
index b58c5c8127e..3784213df42 100644
--- a/pkg/provider/keepersecurity/client_test.go
+++ b/pkg/provider/keepersecurity/client_test.go
@@ -70,8 +70,8 @@ func TestClientDeleteSecret(t *testing.T) {
record0: record0,
}, nil
},
- GetSecretByTitleFn: func(recordTitle string) (*ksm.Record, error) {
- return generateRecords()[0], nil
+ GetSecretsByTitleFn: func(recordTitle string) (records []*ksm.Record, err error) {
+ return generateRecords()[:1], nil
},
},
folderID: folderID,
@@ -85,11 +85,16 @@ func TestClientDeleteSecret(t *testing.T) {
wantErr: false,
},
{
- name: "Delete invalid secret type",
+ name: "Delete secret with multiple matches by Name",
fields: fields{
ksmClient: &fake.MockKeeperClient{
- GetSecretByTitleFn: func(recordTitle string) (*ksm.Record, error) {
- return generateRecords()[1], nil
+ DeleteSecretsFn: func(recrecordUids []string) (map[string]string, error) {
+ return map[string]string{
+ record0: record0,
+ }, nil
+ },
+ GetSecretsByTitleFn: func(recordTitle string) (records []*ksm.Record, err error) {
+ return []*ksm.Record{generateRecords()[0], generateRecords()[0]}, nil
},
},
folderID: folderID,
@@ -106,7 +111,7 @@ func TestClientDeleteSecret(t *testing.T) {
name: "Delete non existing secret",
fields: fields{
ksmClient: &fake.MockKeeperClient{
- GetSecretByTitleFn: func(recordTitle string) (*ksm.Record, error) {
+ GetSecretsByTitleFn: func(recordTitle string) (records []*ksm.Record, err error) {
return nil, errors.New("failed")
},
},
@@ -303,6 +308,24 @@ func TestClientGetSecret(t *testing.T) {
want: []byte(outputRecord0),
wantErr: false,
},
+ {
+ name: "Get secret with multiple matches by ID",
+ fields: fields{
+ ksmClient: &fake.MockKeeperClient{
+ GetSecretsFn: func(filter []string) ([]*ksm.Record, error) {
+ return []*ksm.Record{generateRecords()[0], generateRecords()[0]}, nil
+ },
+ },
+ folderID: folderID,
+ },
+ args: args{
+ ctx: context.Background(),
+ ref: v1beta1.ExternalSecretDataRemoteRef{
+ Key: record0,
+ },
+ },
+ wantErr: true,
+ },
{
name: "Get non existing secret",
fields: fields{
@@ -511,8 +534,8 @@ func TestClientPushSecret(t *testing.T) {
name: "Push new valid secret",
fields: fields{
ksmClient: &fake.MockKeeperClient{
- GetSecretByTitleFn: func(recordTitle string) (*ksm.Record, error) {
- return nil, errors.New("NotFound")
+ GetSecretsByTitleFn: func(recordTitle string) (records []*ksm.Record, err error) {
+ return generateRecords()[0:0], nil
},
CreateSecretWithRecordDataFn: func(recUID, folderUid string, recordData *ksm.RecordCreate) (string, error) {
return "record5", nil
@@ -533,8 +556,8 @@ func TestClientPushSecret(t *testing.T) {
name: "Push existing valid secret",
fields: fields{
ksmClient: &fake.MockKeeperClient{
- GetSecretByTitleFn: func(recordTitle string) (*ksm.Record, error) {
- return generateRecords()[0], nil
+ GetSecretsByTitleFn: func(recordTitle string) (records []*ksm.Record, err error) {
+ return generateRecords()[0:1], nil
},
SaveFn: func(record *ksm.Record) error {
return nil
@@ -552,14 +575,11 @@ func TestClientPushSecret(t *testing.T) {
wantErr: false,
},
{
- name: "Push existing invalid secret",
+ name: "Unable to push new valid secret with multiple matches by Name",
fields: fields{
ksmClient: &fake.MockKeeperClient{
- GetSecretByTitleFn: func(recordTitle string) (*ksm.Record, error) {
- return generateRecords()[1], nil
- },
- SaveFn: func(record *ksm.Record) error {
- return nil
+ GetSecretsByTitleFn: func(recordTitle string) (records []*ksm.Record, err error) {
+ return []*ksm.Record{generateRecords()[0], generateRecords()[0]}, nil
},
},
folderID: folderID,
@@ -569,7 +589,7 @@ func TestClientPushSecret(t *testing.T) {
SecretKey: secretKey,
RemoteKey: validExistingRecord,
},
- value: []byte("foo2"),
+ value: []byte("foo"),
},
wantErr: true,
},
@@ -577,7 +597,7 @@ func TestClientPushSecret(t *testing.T) {
name: "Unable to push new valid secret",
fields: fields{
ksmClient: &fake.MockKeeperClient{
- GetSecretByTitleFn: func(recordTitle string) (*ksm.Record, error) {
+ GetSecretsByTitleFn: func(recordTitle string) (records []*ksm.Record, err error) {
return nil, errors.New("NotFound")
},
CreateSecretWithRecordDataFn: func(recUID, folderUID string, recordData *ksm.RecordCreate) (string, error) {
@@ -602,6 +622,9 @@ func TestClientPushSecret(t *testing.T) {
GetSecretByTitleFn: func(recordTitle string) (*ksm.Record, error) {
return generateRecords()[0], nil
},
+ GetSecretsByTitleFn: func(recordTitle string) (records []*ksm.Record, err error) {
+ return generateRecords()[0:1], nil
+ },
SaveFn: func(record *ksm.Record) error {
return errors.New("Unable to save")
},
diff --git a/pkg/provider/keepersecurity/fake/fake.go b/pkg/provider/keepersecurity/fake/fake.go
index 76654005b67..551b989533c 100644
--- a/pkg/provider/keepersecurity/fake/fake.go
+++ b/pkg/provider/keepersecurity/fake/fake.go
@@ -19,6 +19,7 @@ import ksm "github.com/keeper-security/secrets-manager-go/core"
type MockKeeperClient struct {
GetSecretsFn func([]string) ([]*ksm.Record, error)
GetSecretByTitleFn func(recordTitle string) (*ksm.Record, error)
+ GetSecretsByTitleFn func(recordTitle string) (records []*ksm.Record, err error)
CreateSecretWithRecordDataFn func(recUID, folderUID string, recordData *ksm.RecordCreate) (string, error)
DeleteSecretsFn func(recrecordUids []string) (map[string]string, error)
SaveFn func(record *ksm.Record) error
@@ -47,6 +48,10 @@ func (mc *MockKeeperClient) GetSecretByTitle(recordTitle string) (*ksm.Record, e
return mc.GetSecretByTitleFn(recordTitle)
}
+func (mc *MockKeeperClient) GetSecretsByTitle(recordTitle string) (records []*ksm.Record, err error) {
+ return mc.GetSecretsByTitleFn(recordTitle)
+}
+
func (mc *MockKeeperClient) CreateSecretWithRecordData(recUID, folderUID string, recordData *ksm.RecordCreate) (string, error) {
return mc.CreateSecretWithRecordDataFn(recUID, folderUID, recordData)
}
diff --git a/pkg/provider/keepersecurity/provider.go b/pkg/provider/keepersecurity/provider.go
index 4b4140de35f..271c6a81981 100644
--- a/pkg/provider/keepersecurity/provider.go
+++ b/pkg/provider/keepersecurity/provider.go
@@ -16,6 +16,7 @@ package keepersecurity
import (
"context"
+ "errors"
"fmt"
ksm "github.com/keeper-security/secrets-manager-go/core"
@@ -29,16 +30,13 @@ import (
)
const (
- errKeeperSecurityUnableToCreateConfig = "unable to create valid KeeperSecurity config: %w"
- errKeeperSecurityStore = "received invalid KeeperSecurity SecretStore resource: %s"
- errKeeperSecurityNilSpec = "nil spec"
- errKeeperSecurityNilSpecProvider = "nil spec.provider"
- errKeeperSecurityNilSpecProviderKeeperSecurity = "nil spec.provider.keepersecurity"
- errKeeperSecurityStoreMissingAuth = "missing: spec.provider.keepersecurity.auth"
- errKeeperSecurityStoreMissingFolderID = "missing: spec.provider.keepersecurity.folderID"
- errInvalidClusterStoreMissingK8sSecretNamespace = "invalid ClusterSecretStore: missing KeeperSecurity k8s Auth Secret Namespace"
- errFetchK8sSecret = "could not fetch k8s Secret: %w"
- errMissingK8sSecretKey = "missing Secret key: %s"
+ errKeeperSecurityUnableToCreateConfig = "unable to create valid KeeperSecurity config: %w"
+ errKeeperSecurityStore = "received invalid KeeperSecurity SecretStore resource: %s"
+ errKeeperSecurityNilSpec = "nil spec"
+ errKeeperSecurityNilSpecProvider = "nil spec.provider"
+ errKeeperSecurityNilSpecProviderKeeperSecurity = "nil spec.provider.keepersecurity"
+ errKeeperSecurityStoreMissingAuth = "missing: spec.provider.keepersecurity.auth"
+ errKeeperSecurityStoreMissingFolderID = "missing: spec.provider.keepersecurity.folderID"
)
// Provider implements the necessary NewClient() and ValidateStore() funcs.
@@ -90,23 +88,23 @@ func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnin
}
spc := store.GetSpec()
if spc == nil {
- return nil, fmt.Errorf(errKeeperSecurityNilSpec)
+ return nil, errors.New(errKeeperSecurityNilSpec)
}
if spc.Provider == nil {
- return nil, fmt.Errorf(errKeeperSecurityNilSpecProvider)
+ return nil, errors.New(errKeeperSecurityNilSpecProvider)
}
if spc.Provider.KeeperSecurity == nil {
- return nil, fmt.Errorf(errKeeperSecurityNilSpecProviderKeeperSecurity)
+ return nil, errors.New(errKeeperSecurityNilSpecProviderKeeperSecurity)
}
// check mandatory fields
config := spc.Provider.KeeperSecurity
if err := utils.ValidateSecretSelector(store, config.Auth); err != nil {
- return nil, fmt.Errorf(errKeeperSecurityStoreMissingAuth)
+ return nil, errors.New(errKeeperSecurityStoreMissingAuth)
}
if config.FolderID == "" {
- return nil, fmt.Errorf(errKeeperSecurityStoreMissingFolderID)
+ return nil, errors.New(errKeeperSecurityStoreMissingFolderID)
}
return nil, nil
diff --git a/pkg/provider/kubernetes/auth.go b/pkg/provider/kubernetes/auth.go
index a4118de67bb..8d7eeaf663b 100644
--- a/pkg/provider/kubernetes/auth.go
+++ b/pkg/provider/kubernetes/auth.go
@@ -16,98 +16,99 @@ package kubernetes
import (
"context"
+ "errors"
"fmt"
authenticationv1 "k8s.io/api/authentication/v1"
- corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
- "k8s.io/apimachinery/pkg/types"
+ "k8s.io/client-go/rest"
+ "k8s.io/client-go/tools/clientcmd"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+ "github.com/external-secrets/external-secrets/pkg/utils"
"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
)
const (
- errInvalidClusterStoreMissingNamespace = "missing namespace"
- errFetchCredentials = "could not fetch credentials: %w"
- errMissingCredentials = "missing credentials: \"%s\""
- errEmptyKey = "key %s found but empty"
- errUnableCreateToken = "cannot create service account token: %q"
+ errUnableCreateToken = "cannot create service account token: %q"
)
-func (c *Client) setAuth(ctx context.Context) error {
- err := c.setCA(ctx)
- if err != nil {
- return err
- }
- if c.store.Auth.Token != nil {
- c.BearerToken, err = c.fetchSecretKey(ctx, c.store.Auth.Token.BearerToken)
+func (c *Client) getAuth(ctx context.Context) (*rest.Config, error) {
+ if c.store.AuthRef != nil {
+ cfg, err := c.fetchSecretKey(ctx, *c.store.AuthRef)
if err != nil {
- return fmt.Errorf("could not fetch Auth.Token.BearerToken: %w", err)
+ return nil, err
}
- return nil
+
+ return clientcmd.RESTConfigFromKubeConfig(cfg)
}
- if c.store.Auth.ServiceAccount != nil {
- c.BearerToken, err = c.serviceAccountToken(ctx, c.store.Auth.ServiceAccount)
- if err != nil {
- return fmt.Errorf("could not fetch Auth.ServiceAccount: %w", err)
- }
- return nil
+
+ if c.store.Server.URL == "" {
+ return nil, errors.New("no server URL provided")
}
- if c.store.Auth.Cert != nil {
- return c.setClientCert(ctx)
+
+ cfg := &rest.Config{
+ Host: c.store.Server.URL,
}
- return fmt.Errorf("no credentials provided")
-}
-func (c *Client) setCA(ctx context.Context) error {
- if c.store.Server.CABundle != nil {
- c.CA = c.store.Server.CABundle
- return nil
+ ca, err := utils.FetchCACertFromSource(ctx, utils.CreateCertOpts{
+ CABundle: c.store.Server.CABundle,
+ CAProvider: c.store.Server.CAProvider,
+ StoreKind: c.storeKind,
+ Namespace: c.namespace,
+ Client: c.ctrlClient,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ cfg.TLSClientConfig = rest.TLSClientConfig{
+ Insecure: false,
+ CAData: ca,
}
- if c.store.Server.CAProvider != nil {
- var ca []byte
- var err error
- switch c.store.Server.CAProvider.Type {
- case esv1beta1.CAProviderTypeConfigMap:
- keySelector := esmeta.SecretKeySelector{
- Name: c.store.Server.CAProvider.Name,
- Namespace: c.store.Server.CAProvider.Namespace,
- Key: c.store.Server.CAProvider.Key,
- }
- ca, err = c.fetchConfigMapKey(ctx, keySelector)
- if err != nil {
- return fmt.Errorf("unable to fetch Server.CAProvider ConfigMap: %w", err)
- }
- case esv1beta1.CAProviderTypeSecret:
- keySelector := esmeta.SecretKeySelector{
- Name: c.store.Server.CAProvider.Name,
- Namespace: c.store.Server.CAProvider.Namespace,
- Key: c.store.Server.CAProvider.Key,
- }
- ca, err = c.fetchSecretKey(ctx, keySelector)
- if err != nil {
- return fmt.Errorf("unable to fetch Server.CAProvider Secret: %w", err)
- }
+
+ switch {
+ case c.store.Auth.Token != nil:
+ token, err := c.fetchSecretKey(ctx, c.store.Auth.Token.BearerToken)
+ if err != nil {
+ return nil, fmt.Errorf("could not fetch Auth.Token.BearerToken: %w", err)
}
- c.CA = ca
- return nil
+
+ cfg.BearerToken = string(token)
+ case c.store.Auth.ServiceAccount != nil:
+ token, err := c.serviceAccountToken(ctx, c.store.Auth.ServiceAccount)
+ if err != nil {
+ return nil, fmt.Errorf("could not fetch Auth.ServiceAccount: %w", err)
+ }
+
+ cfg.BearerToken = string(token)
+ case c.store.Auth.Cert != nil:
+ key, cert, err := c.getClientKeyAndCert(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("could not fetch client key and cert: %w", err)
+ }
+
+ cfg.TLSClientConfig.KeyData = key
+ cfg.TLSClientConfig.CertData = cert
+ default:
+ return nil, errors.New("no auth provider given")
}
- return fmt.Errorf("no Certificate Authority provided")
+
+ return cfg, nil
}
-func (c *Client) setClientCert(ctx context.Context) error {
+func (c *Client) getClientKeyAndCert(ctx context.Context) ([]byte, []byte, error) {
var err error
- c.Certificate, err = c.fetchSecretKey(ctx, c.store.Auth.Cert.ClientCert)
+ cert, err := c.fetchSecretKey(ctx, c.store.Auth.Cert.ClientCert)
if err != nil {
- return fmt.Errorf("unable to fetch client certificate: %w", err)
+ return nil, nil, fmt.Errorf("unable to fetch client certificate: %w", err)
}
- c.Key, err = c.fetchSecretKey(ctx, c.store.Auth.Cert.ClientKey)
+ key, err := c.fetchSecretKey(ctx, c.store.Auth.Cert.ClientKey)
if err != nil {
- return fmt.Errorf("unable to fetch client key: %w", err)
+ return nil, nil, fmt.Errorf("unable to fetch client key: %w", err)
}
- return nil
+ return key, cert, nil
}
func (c *Client) serviceAccountToken(ctx context.Context, serviceAccountRef *esmeta.ServiceAccountSelector) ([]byte, error) {
@@ -142,30 +143,3 @@ func (c *Client) fetchSecretKey(ctx context.Context, ref esmeta.SecretKeySelecto
}
return []byte(secret), nil
}
-
-func (c *Client) fetchConfigMapKey(ctx context.Context, key esmeta.SecretKeySelector) ([]byte, error) {
- configMap := &corev1.ConfigMap{}
- objectKey := types.NamespacedName{
- Name: key.Name,
- Namespace: c.namespace,
- }
- // only ClusterStore is allowed to set namespace (and then it's required)
- if c.storeKind == esv1beta1.ClusterSecretStoreKind {
- if key.Namespace == nil {
- return nil, fmt.Errorf(errInvalidClusterStoreMissingNamespace)
- }
- objectKey.Namespace = *key.Namespace
- }
- err := c.ctrlClient.Get(ctx, objectKey, configMap)
- if err != nil {
- return nil, fmt.Errorf(errFetchCredentials, err)
- }
- val, ok := configMap.Data[key.Key]
- if !ok {
- return nil, fmt.Errorf(errMissingCredentials, key.Key)
- }
- if val == "" {
- return nil, fmt.Errorf(errEmptyKey, key.Key)
- }
- return []byte(val), nil
-}
diff --git a/pkg/provider/kubernetes/auth_test.go b/pkg/provider/kubernetes/auth_test.go
index 9784c1372a0..23ed2beeaf7 100644
--- a/pkg/provider/kubernetes/auth_test.go
+++ b/pkg/provider/kubernetes/auth_test.go
@@ -18,10 +18,11 @@ import (
"context"
"testing"
- "github.com/google/go-cmp/cmp"
+ "github.com/stretchr/testify/assert"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
+ "k8s.io/client-go/rest"
pointer "k8s.io/utils/ptr"
kclient "sigs.k8s.io/controller-runtime/pkg/client"
fclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
@@ -31,6 +32,44 @@ import (
utilfake "github.com/external-secrets/external-secrets/pkg/provider/util/fake"
)
+const (
+ caCert = `-----BEGIN CERTIFICATE-----
+MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw
+CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp
+Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2
+MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
+bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG
+ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS
+7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp
+0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS
+B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49
+BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ
+LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4
+DXZDjC5Ty3zfDBeWUA==
+-----END CERTIFICATE-----
+`
+ authTestKubeConfig = `apiVersion: v1
+clusters:
+- cluster:
+ server: https://api.my-domain.tld
+ certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNHVENDQVorZ0F3SUJBZ0lRQ2VDVFphejMyY2k1UGh3TEJDb3U4ekFLQmdncWhrak9QUVFEQXpCT01Rc3cKQ1FZRFZRUUdFd0pWVXpFWE1CVUdBMVVFQ2hNT1JHbG5hVU5sY25Rc0lFbHVZeTR4SmpBa0JnTlZCQU1USFVScApaMmxEWlhKMElGUk1VeUJGUTBNZ1VETTROQ0JTYjI5MElFYzFNQjRYRFRJeE1ERXhOVEF3TURBd01Gb1hEVFEyCk1ERXhOREl6TlRrMU9Wb3dUakVMTUFrR0ExVUVCaE1DVlZNeEZ6QVZCZ05WQkFvVERrUnBaMmxEWlhKMExDQkoKYm1NdU1TWXdKQVlEVlFRREV4MUVhV2RwUTJWeWRDQlVURk1nUlVORElGQXpPRFFnVW05dmRDQkhOVEIyTUJBRwpCeXFHU000OUFnRUdCU3VCQkFBaUEySUFCTUZFb2M4UmwxQ2EzaU9DTlFmTjBNc1luZEx4ZjNjMVR6dmRsSEpTCjdjSTcrT3o2ZTJ0WUlPeVpyc244YUxOMXVkc0o3TWdUOVU3R0NoMW1NRXk3SDBjS1BHRVFRaWw4cFFnTzRDTHAKMHpWb3pwdGpuNFMxbVUxWW9JNzFWT2VWeWFOQ01FQXdIUVlEVlIwT0JCWUVGTUZSUlZCWnF6N25MRnI2SUNJUwpCNENJZkJGcU1BNEdBMVVkRHdFQi93UUVBd0lCaGpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUFvR0NDcUdTTTQ5CkJBTURBMmdBTUdVQ01RQ0phbzFINSt6OGJsVUQyV2RzSms2RHh2M0oreXNUdkxkNmpMUmwwbWxwWXhOak95WlEKTGdHaGVRYVJuVWkvd3I0Q01FZkRGWHV4b0pHWlNaT29QSHpvUmdhTExQSXhBSlNkWXNpSnZSbUVGT21sK3dHNApEWFpEakM1VHkzemZEQmVXVUE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
+ name: mycluster
+contexts:
+- context:
+ cluster: mycluster
+ user: myuser
+ name: mycontext
+current-context: mycontext
+kind: Config
+preferences: {}
+users:
+- name: myuser
+ user:
+ token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE3MTkzOTY4OTksImV4cCI6MTc1MDkzMjg4NywiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.xXrfIl0akhfjWU_BDl7Ad54SXje0YlJdnugzwh96VmM
+`
+ serverURL = "https://my.test.tld"
+)
+
func TestSetAuth(t *testing.T) {
type fields struct {
kube kclient.Client
@@ -39,16 +78,11 @@ func TestSetAuth(t *testing.T) {
namespace string
storeKind string
}
- type want struct {
- Certificate []byte
- Key []byte
- CA []byte
- BearerToken []byte
- }
+ type want = rest.Config
tests := []struct {
name string
fields fields
- want want
+ want *want
wantErr bool
}{
{
@@ -58,7 +92,7 @@ func TestSetAuth(t *testing.T) {
Server: esv1beta1.KubernetesServer{},
},
},
- want: want{},
+ want: nil,
wantErr: true,
},
{
@@ -66,13 +100,11 @@ func TestSetAuth(t *testing.T) {
fields: fields{
store: &esv1beta1.KubernetesProvider{
Server: esv1beta1.KubernetesServer{
- CABundle: []byte("1234"),
+ CABundle: []byte(caCert),
},
},
},
- want: want{
- CA: []byte("1234"),
- },
+ want: nil,
wantErr: true,
},
{
@@ -85,29 +117,52 @@ func TestSetAuth(t *testing.T) {
Namespace: "default",
},
Data: map[string][]byte{
- "cert": []byte("1234"),
+ "cert": []byte(caCert),
+ "token": []byte("mytoken"),
},
}).Build(),
store: &esv1beta1.KubernetesProvider{
Server: esv1beta1.KubernetesServer{
+ URL: serverURL,
CAProvider: &esv1beta1.CAProvider{
Type: esv1beta1.CAProviderTypeSecret,
Name: "foobar",
Key: "cert",
},
},
+ Auth: esv1beta1.KubernetesAuth{
+ Token: &esv1beta1.TokenAuth{
+ BearerToken: v1.SecretKeySelector{
+ Name: "foobar",
+ Namespace: pointer.To("shouldnotberelevant"),
+ Key: "token",
+ },
+ },
+ },
},
},
- want: want{
- CA: []byte("1234"),
+ want: &want{
+ Host: serverURL,
+ BearerToken: "mytoken",
+ TLSClientConfig: rest.TLSClientConfig{
+ CAData: []byte(caCert),
+ },
},
- wantErr: true,
+ wantErr: false,
},
{
name: "should fetch ca from ConfigMap",
fields: fields{
namespace: "default",
- kube: fclient.NewClientBuilder().WithObjects(&corev1.ConfigMap{
+ kube: fclient.NewClientBuilder().WithObjects(&corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foobar",
+ Namespace: "default",
+ },
+ Data: map[string][]byte{
+ "token": []byte("mytoken"),
+ },
+ }, &corev1.ConfigMap{
ObjectMeta: metav1.ObjectMeta{
Name: "foobar",
Namespace: "default",
@@ -118,18 +173,32 @@ func TestSetAuth(t *testing.T) {
}).Build(),
store: &esv1beta1.KubernetesProvider{
Server: esv1beta1.KubernetesServer{
+ URL: serverURL,
CAProvider: &esv1beta1.CAProvider{
Type: esv1beta1.CAProviderTypeConfigMap,
Name: "foobar",
Key: "cert",
},
},
+ Auth: esv1beta1.KubernetesAuth{
+ Token: &esv1beta1.TokenAuth{
+ BearerToken: v1.SecretKeySelector{
+ Name: "foobar",
+ Namespace: pointer.To("shouldnotberelevant"),
+ Key: "token",
+ },
+ },
+ },
},
},
- want: want{
- CA: []byte("1234"),
+ want: &want{
+ Host: serverURL,
+ BearerToken: "mytoken",
+ TLSClientConfig: rest.TLSClientConfig{
+ CAData: []byte("1234"),
+ },
},
- wantErr: true,
+ wantErr: false,
},
{
name: "should set token from secret",
@@ -146,7 +215,8 @@ func TestSetAuth(t *testing.T) {
}).Build(),
store: &esv1beta1.KubernetesProvider{
Server: esv1beta1.KubernetesServer{
- CABundle: []byte("1234"),
+ URL: serverURL,
+ CABundle: []byte(caCert),
},
Auth: esv1beta1.KubernetesAuth{
Token: &esv1beta1.TokenAuth{
@@ -159,9 +229,12 @@ func TestSetAuth(t *testing.T) {
},
},
},
- want: want{
- CA: []byte("1234"),
- BearerToken: []byte("mytoken"),
+ want: &want{
+ Host: serverURL,
+ BearerToken: "mytoken",
+ TLSClientConfig: rest.TLSClientConfig{
+ CAData: []byte(caCert),
+ },
},
wantErr: false,
},
@@ -181,7 +254,8 @@ func TestSetAuth(t *testing.T) {
}).Build(),
store: &esv1beta1.KubernetesProvider{
Server: esv1beta1.KubernetesServer{
- CABundle: []byte("1234"),
+ URL: serverURL,
+ CABundle: []byte(caCert),
},
Auth: esv1beta1.KubernetesAuth{
Cert: &esv1beta1.CertAuth{
@@ -197,10 +271,13 @@ func TestSetAuth(t *testing.T) {
},
},
},
- want: want{
- CA: []byte("1234"),
- Certificate: []byte("my-cert"),
- Key: []byte("my-key"),
+ want: &want{
+ Host: serverURL,
+ TLSClientConfig: rest.TLSClientConfig{
+ CAData: []byte(caCert),
+ CertData: []byte("my-cert"),
+ KeyData: []byte("my-key"),
+ },
},
wantErr: false,
},
@@ -217,7 +294,8 @@ func TestSetAuth(t *testing.T) {
kubeclientset: utilfake.NewCreateTokenMock().WithToken("my-sa-token"),
store: &esv1beta1.KubernetesProvider{
Server: esv1beta1.KubernetesServer{
- CABundle: []byte("1234"),
+ URL: serverURL,
+ CABundle: []byte(caCert),
},
Auth: esv1beta1.KubernetesAuth{
ServiceAccount: &v1.ServiceAccountSelector{
@@ -227,9 +305,68 @@ func TestSetAuth(t *testing.T) {
},
},
},
- want: want{
- CA: []byte("1234"),
- BearerToken: []byte("my-sa-token"),
+ want: &want{
+ Host: serverURL,
+ BearerToken: "my-sa-token",
+ TLSClientConfig: rest.TLSClientConfig{
+ CAData: []byte(caCert),
+ },
+ },
+ wantErr: false,
+ },
+ {
+ name: "should fail with missing URL",
+ fields: fields{
+ namespace: "default",
+ kube: fclient.NewClientBuilder().WithObjects(&corev1.ServiceAccount{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "my-sa",
+ Namespace: "default",
+ },
+ }).Build(),
+ kubeclientset: utilfake.NewCreateTokenMock().WithToken("my-sa-token"),
+ store: &esv1beta1.KubernetesProvider{
+ Server: esv1beta1.KubernetesServer{
+ CABundle: []byte(caCert),
+ },
+ Auth: esv1beta1.KubernetesAuth{
+ ServiceAccount: &v1.ServiceAccountSelector{
+ Name: "my-sa",
+ Namespace: pointer.To("shouldnotberelevant"),
+ },
+ },
+ },
+ },
+ want: nil,
+ wantErr: true,
+ },
+ {
+ name: "should read config from secret",
+ fields: fields{
+ namespace: "default",
+ kube: fclient.NewClientBuilder().WithObjects(&corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foobar",
+ Namespace: "default",
+ },
+ Data: map[string][]byte{
+ "config": []byte(authTestKubeConfig),
+ },
+ }).Build(),
+ store: &esv1beta1.KubernetesProvider{
+ AuthRef: &v1.SecretKeySelector{
+ Name: "foobar",
+ Namespace: pointer.To("default"),
+ Key: "config",
+ },
+ },
+ },
+ want: &want{
+ Host: "https://api.my-domain.tld",
+ BearerToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE3MTkzOTY4OTksImV4cCI6MTc1MDkzMjg4NywiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.xXrfIl0akhfjWU_BDl7Ad54SXje0YlJdnugzwh96VmM",
+ TLSClientConfig: rest.TLSClientConfig{
+ CAData: []byte(caCert),
+ },
},
wantErr: false,
},
@@ -243,18 +380,11 @@ func TestSetAuth(t *testing.T) {
namespace: tt.fields.namespace,
storeKind: tt.fields.storeKind,
}
- if err := k.setAuth(context.Background()); (err != nil) != tt.wantErr {
+ cfg, err := k.getAuth(context.Background())
+ if (err != nil) != tt.wantErr {
t.Errorf("BaseClient.setAuth() error = %v, wantErr %v", err, tt.wantErr)
}
- w := want{
- Certificate: k.Certificate,
- Key: k.Key,
- CA: k.CA,
- BearerToken: k.BearerToken,
- }
- if !cmp.Equal(w, tt.want) {
- t.Errorf("unexpected value: expected %#v, got %#v", tt.want, w)
- }
+ assert.Equal(t, tt.want, cfg)
})
}
}
diff --git a/pkg/provider/kubernetes/client.go b/pkg/provider/kubernetes/client.go
index 662d506a2e2..d8b8afcb6de 100644
--- a/pkg/provider/kubernetes/client.go
+++ b/pkg/provider/kubernetes/client.go
@@ -15,16 +15,16 @@ limitations under the License.
package kubernetes
import (
- "bytes"
"context"
"encoding/base64"
"encoding/json"
+ "errors"
"fmt"
- "reflect"
"strings"
"github.com/tidwall/gjson"
v1 "k8s.io/api/core/v1"
+ "k8s.io/apimachinery/pkg/api/equality"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
@@ -34,6 +34,7 @@ import (
"github.com/external-secrets/external-secrets/pkg/find"
"github.com/external-secrets/external-secrets/pkg/metrics"
"github.com/external-secrets/external-secrets/pkg/utils"
+ "github.com/external-secrets/external-secrets/pkg/utils/metadata"
)
const (
@@ -77,7 +78,7 @@ func (c *Client) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretData
func (c *Client) DeleteSecret(ctx context.Context, remoteRef esv1beta1.PushSecretRemoteRef) error {
if remoteRef.GetProperty() == "" {
- return fmt.Errorf("requires property in RemoteRef to delete secret value")
+ return errors.New("requires property in RemoteRef to delete secret value")
}
extSecret, getErr := c.userSecretClient.Get(ctx, remoteRef.GetRemoteKey(), metav1.GetOptions{})
@@ -101,57 +102,114 @@ func (c *Client) DeleteSecret(ctx context.Context, remoteRef esv1beta1.PushSecre
}
func (c *Client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf("not implemented")
+ return false, errors.New("not implemented")
}
func (c *Client) PushSecret(ctx context.Context, secret *v1.Secret, data esv1beta1.PushSecretData) error {
if data.GetProperty() == "" && data.GetSecretKey() != "" {
- return fmt.Errorf("requires property in RemoteRef to push secret value if secret key is defined")
+ return errors.New("requires property in RemoteRef to push secret value if secret key is defined")
+ }
+ remoteSecret := &v1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: c.store.RemoteNamespace,
+ Name: data.GetRemoteKey(),
+ },
}
- extSecret, getErr := c.userSecretClient.Get(ctx, data.GetRemoteKey(), metav1.GetOptions{})
- metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesGetSecret, getErr)
- if getErr != nil {
- // create if it not exists
- if apierrors.IsNotFound(getErr) {
- typ := v1.SecretTypeOpaque
- if secret.Type != "" {
- typ = secret.Type
- }
+ return c.createOrUpdate(ctx, remoteSecret, func() error {
+ return c.mergePushSecretData(data, remoteSecret, secret)
+ })
+}
- return c.createSecret(ctx, secret, typ, data)
- }
- return getErr
+func (c *Client) mergePushSecretData(remoteRef esv1beta1.PushSecretData, remoteSecret, localSecret *v1.Secret) error {
+ // apply secret type
+ secretType := v1.SecretTypeOpaque
+ if localSecret.Type != "" {
+ secretType = localSecret.Type
}
+ remoteSecret.Type = secretType
- // the whole secret was pushed to the provider
- if data.GetSecretKey() == "" {
- if data.GetProperty() != "" {
- value, err := c.marshalData(secret)
- if err != nil {
- return err
- }
+ // merge secret data with existing secret data
+ if remoteSecret.Data == nil {
+ remoteSecret.Data = make(map[string][]byte)
+ }
- if v, ok := extSecret.Data[data.GetProperty()]; ok && bytes.Equal(v, value) {
- return nil
- }
+ pushMeta, err := metadata.ParseMetadataParameters[PushSecretMetadataSpec](remoteRef.GetMetadata())
+ if err != nil {
+ return fmt.Errorf("unable to parse metadata parameters: %w", err)
+ }
+
+ // merge metadata based on the policy
+ var targetLabels, targetAnnotations map[string]string
+ sourceLabels, sourceAnnotations, err := mergeSourceMetadata(localSecret, pushMeta)
+ if err != nil {
+ return fmt.Errorf("failed to merge source metadata: %w", err)
+ }
+ targetLabels, targetAnnotations, err = mergeTargetMetadata(remoteSecret, pushMeta, sourceLabels, sourceAnnotations)
+ if err != nil {
+ return fmt.Errorf("failed to merge target metadata: %w", err)
+ }
+ remoteSecret.ObjectMeta.Labels = targetLabels
+ remoteSecret.ObjectMeta.Annotations = targetAnnotations
- return c.updateProperty(ctx, extSecret, data, value)
+ // case 1: push the whole secret
+ if remoteRef.GetProperty() == "" {
+ for k, v := range localSecret.Data {
+ remoteSecret.Data[k] = v
}
+ return nil
+ }
- if reflect.DeepEqual(extSecret.Data, secret.Data) {
- return nil
+ // cases 2a + 2b: push into a property.
+ // if secret key is empty, we will marshal the whole secret and put it into
+ // the property defined in the remoteRef.
+ if remoteRef.GetSecretKey() == "" {
+ value, err := c.marshalData(localSecret)
+ if err != nil {
+ return err
+ }
+ remoteSecret.Data[remoteRef.GetProperty()] = value
+ } else {
+ // if secret key is defined, we will push that key from the local secret
+ remoteSecret.Data[remoteRef.GetProperty()] = localSecret.Data[remoteRef.GetSecretKey()]
+ }
+ return nil
+}
+
+func (c *Client) createOrUpdate(ctx context.Context, targetSecret *v1.Secret, f func() error) error {
+ target, err := c.userSecretClient.Get(ctx, targetSecret.Name, metav1.GetOptions{})
+ metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesGetSecret, err)
+ if err != nil {
+ if !apierrors.IsNotFound(err) {
+ return err
+ }
+ if err := f(); err != nil {
+ return err
+ }
+ _, err := c.userSecretClient.Create(ctx, targetSecret, metav1.CreateOptions{})
+ metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesCreateSecret, err)
+ if err != nil {
+ return err
}
+ return nil
+ }
- return c.updateMap(ctx, extSecret, secret.Data)
+ *targetSecret = *target
+ existing := targetSecret.DeepCopyObject()
+ if err := f(); err != nil {
+ return err
}
- // only a single property was pushed
- if v, ok := extSecret.Data[data.GetProperty()]; ok && bytes.Equal(v, secret.Data[data.GetSecretKey()]) {
+ if equality.Semantic.DeepEqual(existing, targetSecret) {
return nil
}
- return c.updateProperty(ctx, extSecret, data, secret.Data[data.GetSecretKey()])
+ _, err = c.userSecretClient.Update(ctx, targetSecret, metav1.UpdateOptions{})
+ metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesUpdateSecret, err)
+ if err != nil {
+ return err
+ }
+ return nil
}
func (c *Client) marshalData(secret *v1.Secret) ([]byte, error) {
@@ -336,41 +394,6 @@ func convertMap(in map[string][]byte) map[string]string {
return out
}
-func (c *Client) createSecret(ctx context.Context, secret *v1.Secret, typed v1.SecretType, remoteRef esv1beta1.PushSecretData) error {
- data := make(map[string][]byte)
-
- if remoteRef.GetProperty() != "" {
- // set a specific remote key
- if remoteRef.GetSecretKey() == "" {
- value, err := c.marshalData(secret)
- if err != nil {
- return err
- }
-
- data[remoteRef.GetProperty()] = value
- } else {
- // push a specific secret key into a specific remote property
- data[remoteRef.GetProperty()] = secret.Data[remoteRef.GetSecretKey()]
- }
- } else {
- // push the whole secret as is using each key of the secret as a property in the created secret
- data = secret.Data
- }
-
- s := v1.Secret{
- ObjectMeta: metav1.ObjectMeta{
- Name: remoteRef.GetRemoteKey(),
- Namespace: c.store.RemoteNamespace,
- },
- Data: data,
- Type: typed,
- }
-
- _, err := c.userSecretClient.Create(ctx, &s, metav1.CreateOptions{})
- metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesCreateSecret, err)
- return err
-}
-
// fullDelete removes remote secret completely.
func (c *Client) fullDelete(ctx context.Context, secretName string) error {
err := c.userSecretClient.Delete(ctx, secretName, metav1.DeleteOptions{})
@@ -391,33 +414,6 @@ func (c *Client) removeProperty(ctx context.Context, extSecret *v1.Secret, remot
return err
}
-func (c *Client) updateMap(ctx context.Context, extSecret *v1.Secret, values map[string][]byte) error {
- // update the existing map with values from the pushed secret but keep existing values in tack.
- for k, v := range values {
- extSecret.Data[k] = v
- }
-
- return c.updateSecret(ctx, extSecret)
-}
-
-func (c *Client) updateProperty(ctx context.Context, extSecret *v1.Secret, remoteRef esv1beta1.PushSecretRemoteRef, value []byte) error {
- if extSecret.Data == nil {
- extSecret.Data = make(map[string][]byte)
- }
-
- // otherwise update remote secret
- extSecret.Data[remoteRef.GetProperty()] = value
-
- return c.updateSecret(ctx, extSecret)
-}
-
-func (c *Client) updateSecret(ctx context.Context, extSecret *v1.Secret) error {
- _, err := c.userSecretClient.Update(ctx, extSecret, metav1.UpdateOptions{})
- metrics.ObserveAPICall(constants.ProviderKubernetes, constants.CallKubernetesUpdateSecret, err)
-
- return err
-}
-
func getSecret(secret *v1.Secret, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
s, found, err := getFromSecretMetadata(secret, ref)
diff --git a/pkg/provider/kubernetes/client_test.go b/pkg/provider/kubernetes/client_test.go
index 744ef968675..b55ee2d52e8 100644
--- a/pkg/provider/kubernetes/client_test.go
+++ b/pkg/provider/kubernetes/client_test.go
@@ -24,6 +24,7 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
apierrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
@@ -86,8 +87,9 @@ func (fk *fakeClient) Delete(_ context.Context, name string, _ metav1.DeleteOpti
func (fk *fakeClient) Create(_ context.Context, secret *v1.Secret, _ metav1.CreateOptions) (*v1.Secret, error) {
s := &v1.Secret{
- Data: secret.Data,
- Type: secret.Type,
+ Data: secret.Data,
+ ObjectMeta: secret.ObjectMeta,
+ Type: secret.Type,
}
fk.secretMap[secret.Name] = s
return s, nil
@@ -98,6 +100,7 @@ func (fk *fakeClient) Update(_ context.Context, secret *v1.Secret, _ metav1.Upda
if !ok {
return nil, errors.New("error while updating secret")
}
+ s.ObjectMeta = secret.ObjectMeta
s.Data = secret.Data
return s, nil
}
@@ -705,6 +708,9 @@ func TestDeleteSecret(t *testing.T) {
wantErr: false,
wantSecretMap: map[string]*v1.Secret{
"mysec": {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "mysec",
+ },
Data: map[string][]byte{
"secret": []byte(`bar`),
},
@@ -797,6 +803,11 @@ func TestPushSecret(t *testing.T) {
},
wantSecretMap: map[string]*v1.Secret{
"mysec": {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "mysec",
+ Labels: map[string]string{},
+ Annotations: map[string]string{},
+ },
Data: map[string][]byte{
"token": []byte(`foo`),
"token2": []byte(`foo`),
@@ -827,6 +838,11 @@ func TestPushSecret(t *testing.T) {
},
wantSecretMap: map[string]*v1.Secret{
"mysec": {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "mysec",
+ Labels: map[string]string{},
+ Annotations: map[string]string{},
+ },
Data: map[string][]byte{
"token": []byte(`{"foo":"bar"}`),
},
@@ -856,6 +872,11 @@ func TestPushSecret(t *testing.T) {
},
wantSecretMap: map[string]*v1.Secret{
"mysec": {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "mysec",
+ Labels: map[string]string{},
+ Annotations: map[string]string{},
+ },
Data: map[string][]byte{
"token": []byte(`foo`),
"token2": []byte(`{"foo":"bar"}`),
@@ -883,6 +904,11 @@ func TestPushSecret(t *testing.T) {
},
wantSecretMap: map[string]*v1.Secret{
"mysec": {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "mysec",
+ Labels: map[string]string{},
+ Annotations: map[string]string{},
+ },
Data: map[string][]byte{
"marshaled": []byte(`{"token":"foo","token2":"2"}`),
},
@@ -915,6 +941,11 @@ func TestPushSecret(t *testing.T) {
wantErr: false,
wantSecretMap: map[string]*v1.Secret{
"mysec": {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "mysec",
+ Labels: map[string]string{},
+ Annotations: map[string]string{},
+ },
Data: map[string][]byte{
"token": []byte(`foo`),
"secret": []byte(`bar`),
@@ -947,6 +978,56 @@ func TestPushSecret(t *testing.T) {
wantErr: false,
wantSecretMap: map[string]*v1.Secret{
"mysec": {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "mysec",
+ Labels: map[string]string{},
+ Annotations: map[string]string{},
+ },
+ Data: map[string][]byte{
+ "token": []byte(`bar`),
+ },
+ },
+ },
+ },
+ {
+ name: "replace existing property in existing secret with targetMergePolicy set to Ignore",
+ fields: fields{
+ Client: &fakeClient{
+ t: t,
+ secretMap: map[string]*v1.Secret{
+ "mysec": {
+ Data: map[string][]byte{
+ "token": []byte(`foo`),
+ },
+ },
+ },
+ },
+ },
+ secret: &v1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "mysec",
+ // these should be ignored as the targetMergePolicy is set to Ignore
+ Labels: map[string]string{"dev": "seb"},
+ Annotations: map[string]string{"date": "today"},
+ },
+ Data: map[string][]byte{secretKey: []byte("bar")},
+ },
+ data: testingfake.PushSecretData{
+ SecretKey: secretKey,
+ RemoteKey: "mysec",
+ Property: "token",
+ Metadata: &apiextensionsv1.JSON{
+ Raw: []byte(`{"apiVersion":"kubernetes.external-secrets.io/v1alpha1", "kind": "PushSecretMetadata", spec: {"targetMergePolicy": "Ignore"}}`),
+ },
+ },
+ wantErr: false,
+ wantSecretMap: map[string]*v1.Secret{
+ "mysec": {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "mysec",
+ Labels: map[string]string{},
+ Annotations: map[string]string{},
+ },
Data: map[string][]byte{
"token": []byte(`bar`),
},
@@ -954,7 +1035,65 @@ func TestPushSecret(t *testing.T) {
},
},
{
- name: "create new secret",
+ name: "replace existing property in existing secret with targetMergePolicy set to Replace",
+ fields: fields{
+ Client: &fakeClient{
+ t: t,
+ secretMap: map[string]*v1.Secret{
+ "mysec": {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "mysec",
+ Labels: map[string]string{
+ "already": "existing",
+ },
+ Annotations: map[string]string{
+ "already": "existing",
+ },
+ },
+ Data: map[string][]byte{
+ "token": []byte(`foo`),
+ },
+ },
+ },
+ },
+ },
+ secret: &v1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "mysec",
+ // these should replace existing metadata as the targetMergePolicy is set to Replace
+ Labels: map[string]string{"dev": "seb"},
+ Annotations: map[string]string{"date": "today"},
+ },
+ Data: map[string][]byte{secretKey: []byte("bar")},
+ },
+ data: testingfake.PushSecretData{
+ SecretKey: secretKey,
+ RemoteKey: "mysec",
+ Property: "token",
+ Metadata: &apiextensionsv1.JSON{
+ Raw: []byte(`{"apiVersion":"kubernetes.external-secrets.io/v1alpha1", "kind": "PushSecretMetadata", spec: {"targetMergePolicy": "Replace"}}`),
+ },
+ },
+ wantErr: false,
+ wantSecretMap: map[string]*v1.Secret{
+ "mysec": {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "mysec",
+ Labels: map[string]string{
+ "dev": "seb",
+ },
+ Annotations: map[string]string{
+ "date": "today",
+ },
+ },
+ Data: map[string][]byte{
+ "token": []byte(`bar`),
+ },
+ },
+ },
+ },
+ {
+ name: "create new secret, merging existing metadata",
fields: fields{
Client: &fakeClient{
t: t,
@@ -968,12 +1107,20 @@ func TestPushSecret(t *testing.T) {
},
},
secret: &v1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Annotations: map[string]string{
+ "this-annotation": "should be present on the targey secret",
+ },
+ },
Data: map[string][]byte{secretKey: []byte("bar")},
},
data: testingfake.PushSecretData{
SecretKey: secretKey,
RemoteKey: "mysec",
Property: "secret",
+ Metadata: &apiextensionsv1.JSON{
+ Raw: []byte(`{"apiVersion":"kubernetes.external-secrets.io/v1alpha1", "kind": "PushSecretMetadata", spec: {"annotations": {"date": "today"}, "labels": {"dev": "seb"}}}`),
+ },
},
wantErr: false,
wantSecretMap: map[string]*v1.Secret{
@@ -983,6 +1130,14 @@ func TestPushSecret(t *testing.T) {
},
},
"mysec": {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "mysec",
+ Annotations: map[string]string{
+ "date": "today",
+ "this-annotation": "should be present on the targey secret",
+ },
+ Labels: map[string]string{"dev": "seb"},
+ },
Data: map[string][]byte{
"secret": []byte(`bar`),
},
@@ -990,6 +1145,171 @@ func TestPushSecret(t *testing.T) {
},
},
},
+ {
+ name: "create new secret with metadata from secret metadata and remoteRef.metadata",
+ fields: fields{
+ Client: &fakeClient{
+ t: t,
+ secretMap: map[string]*v1.Secret{
+ "yoursec": {
+ Data: map[string][]byte{
+ "token": []byte(`foo`),
+ },
+ },
+ },
+ },
+ },
+ secret: &v1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Annotations: map[string]string{"date": "today"},
+ Labels: map[string]string{"dev": "seb"},
+ },
+ Data: map[string][]byte{secretKey: []byte("bar")},
+ },
+ data: testingfake.PushSecretData{
+ SecretKey: secretKey,
+ RemoteKey: "mysec",
+ Property: "secret",
+ Metadata: &apiextensionsv1.JSON{
+ Raw: []byte(`{"apiVersion":"kubernetes.external-secrets.io/v1alpha1", "kind": "PushSecretMetadata", spec: { "sourceMergePolicy": "Replace", "annotations": {"another-field": "from-remote-ref"}, "labels": {"other-label": "from-remote-ref"}}}`),
+ },
+ },
+ wantErr: false,
+ wantSecretMap: map[string]*v1.Secret{
+ "yoursec": {
+ Data: map[string][]byte{
+ "token": []byte(`foo`),
+ },
+ },
+ "mysec": {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "mysec",
+ Annotations: map[string]string{
+ "another-field": "from-remote-ref",
+ },
+ Labels: map[string]string{
+ "other-label": "from-remote-ref",
+ },
+ },
+ Data: map[string][]byte{
+ "secret": []byte(`bar`),
+ },
+ Type: v1.SecretTypeOpaque,
+ },
+ },
+ },
+ {
+ name: "invalid secret metadata structure results in error",
+ fields: fields{
+ Client: &fakeClient{
+ t: t,
+ secretMap: map[string]*v1.Secret{
+ "yoursec": {
+ Data: map[string][]byte{
+ "token": []byte(`foo`),
+ },
+ },
+ },
+ },
+ },
+ secret: &v1.Secret{
+ Data: map[string][]byte{secretKey: []byte("bar")},
+ },
+ data: testingfake.PushSecretData{
+ SecretKey: secretKey,
+ RemoteKey: "mysec",
+ Property: "secret",
+ Metadata: &apiextensionsv1.JSON{
+ Raw: []byte(`{}`),
+ },
+ },
+ wantErr: true,
+ wantSecretMap: map[string]*v1.Secret{
+ "yoursec": {
+ Data: map[string][]byte{
+ "token": []byte(`foo`),
+ },
+ },
+ },
+ },
+ {
+ name: "non-json secret metadata results in error",
+ fields: fields{
+ Client: &fakeClient{
+ t: t,
+ secretMap: map[string]*v1.Secret{
+ "yoursec": {
+ Data: map[string][]byte{
+ "token": []byte(`foo`),
+ },
+ },
+ },
+ },
+ },
+ secret: &v1.Secret{
+ Data: map[string][]byte{secretKey: []byte("bar")},
+ },
+ data: testingfake.PushSecretData{
+ SecretKey: secretKey,
+ RemoteKey: "mysec",
+ Property: "secret",
+ Metadata: &apiextensionsv1.JSON{
+ Raw: []byte(`--- not json ---`),
+ },
+ },
+ wantErr: true,
+ wantSecretMap: map[string]*v1.Secret{
+ "yoursec": {
+ Data: map[string][]byte{
+ "token": []byte(`foo`),
+ },
+ },
+ },
+ },
+ {
+ name: "create new secret with whole secret",
+ fields: fields{
+ Client: &fakeClient{
+ t: t,
+ secretMap: map[string]*v1.Secret{
+ "yoursec": {
+ Data: map[string][]byte{
+ "token": []byte(`foo`),
+ },
+ },
+ },
+ },
+ },
+ secret: &v1.Secret{
+ Data: map[string][]byte{
+ "foo": []byte("bar"),
+ "baz": []byte("bang"),
+ },
+ },
+ data: testingfake.PushSecretData{
+ RemoteKey: "mysec",
+ },
+ wantErr: false,
+ wantSecretMap: map[string]*v1.Secret{
+ "yoursec": {
+ Data: map[string][]byte{
+ "token": []byte(`foo`),
+ },
+ },
+ "mysec": {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "mysec",
+ Labels: map[string]string{},
+ Annotations: map[string]string{},
+ },
+ Data: map[string][]byte{
+ "foo": []byte("bar"),
+ "baz": []byte("bang"),
+ },
+ Type: v1.SecretTypeOpaque,
+ },
+ },
+ },
{
name: "create new dockerconfigjson secret",
fields: fields{
@@ -1021,13 +1341,19 @@ func TestPushSecret(t *testing.T) {
},
},
"mysec": {
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "mysec",
+ Labels: map[string]string{},
+ Annotations: map[string]string{},
+ },
Data: map[string][]byte{
"config.json": []byte(`{"auths": {"myregistry.localhost": {"username": "{{ .username }}", "password": "{{ .password }}"}}}`),
},
Type: v1.SecretTypeDockerConfigJson,
},
},
- }}
+ },
+ }
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
p := &Client{
diff --git a/pkg/provider/kubernetes/metadata.go b/pkg/provider/kubernetes/metadata.go
new file mode 100644
index 00000000000..651864f3a0c
--- /dev/null
+++ b/pkg/provider/kubernetes/metadata.go
@@ -0,0 +1,117 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package kubernetes
+
+import (
+ "fmt"
+
+ v1 "k8s.io/api/core/v1"
+
+ "github.com/external-secrets/external-secrets/pkg/utils/metadata"
+)
+
+type PushSecretMetadataSpec struct {
+ TargetMergePolicy targetMergePolicy `json:"targetMergePolicy,omitempty"`
+ SourceMergePolicy sourceMergePolicy `json:"sourceMergePolicy,omitempty"`
+
+ Labels map[string]string `json:"labels,omitempty"`
+ Annotations map[string]string `json:"annotations,omitempty"`
+}
+
+type targetMergePolicy string
+
+const (
+ targetMergePolicyMerge targetMergePolicy = "Merge"
+ targetMergePolicyReplace targetMergePolicy = "Replace"
+ targetMergePolicyIgnore targetMergePolicy = "Ignore"
+)
+
+type sourceMergePolicy string
+
+const (
+ sourceMergePolicyMerge sourceMergePolicy = "Merge"
+ sourceMergePolicyReplace sourceMergePolicy = "Replace"
+)
+
+// Takes the local secret metadata and merges it with the push metadata.
+// The push metadata takes precedence.
+// Depending on the policy, we either merge or overwrite the metadata from the local secret.
+func mergeSourceMetadata(localSecret *v1.Secret, pushMeta *metadata.PushSecretMetadata[PushSecretMetadataSpec]) (map[string]string, map[string]string, error) {
+ labels := localSecret.ObjectMeta.Labels
+ annotations := localSecret.ObjectMeta.Annotations
+ if pushMeta == nil {
+ return labels, annotations, nil
+ }
+ if labels == nil {
+ labels = make(map[string]string)
+ }
+ if annotations == nil {
+ annotations = make(map[string]string)
+ }
+
+ switch pushMeta.Spec.SourceMergePolicy {
+ case "", sourceMergePolicyMerge:
+ for k, v := range pushMeta.Spec.Labels {
+ labels[k] = v
+ }
+ for k, v := range pushMeta.Spec.Annotations {
+ annotations[k] = v
+ }
+ case sourceMergePolicyReplace:
+ labels = pushMeta.Spec.Labels
+ annotations = pushMeta.Spec.Annotations
+ default:
+ return nil, nil, fmt.Errorf("unexpected source merge policy %q", pushMeta.Spec.SourceMergePolicy)
+ }
+ return labels, annotations, nil
+}
+
+// Takes the remote secret metadata and merges it with the source metadata.
+// The source metadata may replace the existing labels/annotations
+// or merge into it depending on policy.
+func mergeTargetMetadata(remoteSecret *v1.Secret, pushMeta *metadata.PushSecretMetadata[PushSecretMetadataSpec], sourceLabels, sourceAnnotations map[string]string) (map[string]string, map[string]string, error) {
+ labels := remoteSecret.ObjectMeta.Labels
+ annotations := remoteSecret.ObjectMeta.Annotations
+ if labels == nil {
+ labels = make(map[string]string)
+ }
+ if annotations == nil {
+ annotations = make(map[string]string)
+ }
+ var targetMergePolicy targetMergePolicy
+ if pushMeta != nil {
+ targetMergePolicy = pushMeta.Spec.TargetMergePolicy
+ }
+
+ switch targetMergePolicy {
+ case "", targetMergePolicyMerge:
+ for k, v := range sourceLabels {
+ labels[k] = v
+ }
+ for k, v := range sourceAnnotations {
+ annotations[k] = v
+ }
+ case targetMergePolicyReplace:
+ labels = sourceLabels
+ annotations = sourceAnnotations
+ case targetMergePolicyIgnore:
+ // leave the target metadata as is
+ // this is useful when we only want to push data
+ // and the user does not want to touch the metadata
+ default:
+ return nil, nil, fmt.Errorf("unexpected target merge policy %q", targetMergePolicy)
+ }
+ return labels, annotations, nil
+}
diff --git a/pkg/provider/kubernetes/provider.go b/pkg/provider/kubernetes/provider.go
index c24df39adae..fbfd5ed30f7 100644
--- a/pkg/provider/kubernetes/provider.go
+++ b/pkg/provider/kubernetes/provider.go
@@ -16,6 +16,7 @@ package kubernetes
import (
"context"
+ "errors"
"fmt"
authv1 "k8s.io/api/authorization/v1"
@@ -23,7 +24,6 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/kubernetes"
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
- "k8s.io/client-go/rest"
kclient "sigs.k8s.io/controller-runtime/pkg/client"
ctrlcfg "sigs.k8s.io/controller-runtime/pkg/client/config"
@@ -73,11 +73,7 @@ type Client struct {
// namespace is the namespace of the
// ExternalSecret referencing this provider.
- namespace string
- Certificate []byte
- Key []byte
- CA []byte
- BearerToken []byte
+ namespace string
}
func init() {
@@ -106,7 +102,7 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore,
func (p *Provider) newClient(ctx context.Context, store esv1beta1.GenericStore, ctrlClient kclient.Client, ctrlClientset kubernetes.Interface, namespace string) (esv1beta1.SecretsClient, error) {
storeSpec := store.GetSpec()
if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.Kubernetes == nil {
- return nil, fmt.Errorf("no store type or wrong store type")
+ return nil, errors.New("no store type or wrong store type")
}
storeSpecKubernetes := storeSpec.Provider.Kubernetes
client := &Client{
@@ -123,22 +119,12 @@ func (p *Provider) newClient(ctx context.Context, store esv1beta1.GenericStore,
return client, nil
}
- if err := client.setAuth(ctx); err != nil {
- return nil, err
- }
-
- config := &rest.Config{
- Host: client.store.Server.URL,
- BearerToken: string(client.BearerToken),
- TLSClientConfig: rest.TLSClientConfig{
- Insecure: false,
- CertData: client.Certificate,
- KeyData: client.Key,
- CAData: client.CA,
- },
+ cfg, err := client.getAuth(ctx)
+ if err != nil {
+ return nil, fmt.Errorf("failed to prepare auth: %w", err)
}
- userClientset, err := kubernetes.NewForConfig(config)
+ userClientset, err := kubernetes.NewForConfig(cfg)
if err != nil {
return nil, fmt.Errorf("error configuring clientset: %w", err)
}
diff --git a/pkg/provider/kubernetes/provider_test.go b/pkg/provider/kubernetes/provider_test.go
index 26e3b083799..6a7e48c0f0e 100644
--- a/pkg/provider/kubernetes/provider_test.go
+++ b/pkg/provider/kubernetes/provider_test.go
@@ -51,6 +51,24 @@ mv+AggtK0aRFb9o47z/BypLdk5mhbf3Mmr88C8XBzEnfdYyf4JpTlZrYLBmDCu5d
9RLLsjXxhag8xqMtd1uLUM8XOTGzVWacw8iGY+CTtBKqyA+AE6/bDwZvEwVtsKtC
QJ85ioEpy00NioqcF0WyMZH80uMsPycfpnl5uF7RkW8u
-----END CERTIFICATE-----`
+ testKubeConfig = `apiVersion: v1
+clusters:
+- cluster:
+ server: https://api.my-domain.tld
+ name: mycluster
+contexts:
+- context:
+ cluster: mycluster
+ user: myuser
+ name: mycontext
+current-context: mycontext
+kind: Config
+preferences: {}
+users:
+- name: myuser
+ user:
+ token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE3MTkzOTY4OTksImV4cCI6MTc1MDkzMjg4NywiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.xXrfIl0akhfjWU_BDl7Ad54SXje0YlJdnugzwh96VmM
+`
)
func TestNewClient(t *testing.T) {
@@ -88,6 +106,40 @@ func TestNewClient(t *testing.T) {
},
wantErr: true,
},
+ {
+ name: "test auth ref",
+ fields: fields{},
+ args: args{
+ store: &esv1beta1.ClusterSecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.ClusterSecretStoreKind,
+ },
+ Spec: esv1beta1.SecretStoreSpec{
+ Provider: &esv1beta1.SecretStoreProvider{
+ Kubernetes: &esv1beta1.KubernetesProvider{
+ AuthRef: &v1.SecretKeySelector{
+ Name: "foo",
+ Namespace: pointer.To("default"),
+ Key: "config",
+ },
+ },
+ },
+ },
+ },
+ namespace: "",
+ kube: fclient.NewClientBuilder().WithObjects(&corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "foo",
+ Namespace: "default",
+ },
+ Data: map[string][]byte{
+ "config": []byte(testKubeConfig),
+ },
+ }).Build(),
+ clientset: clientgofake.NewSimpleClientset(),
+ },
+ want: true,
+ },
{
name: "test referent auth return",
fields: fields{},
@@ -100,6 +152,7 @@ func TestNewClient(t *testing.T) {
Provider: &esv1beta1.SecretStoreProvider{
Kubernetes: &esv1beta1.KubernetesProvider{
Server: esv1beta1.KubernetesServer{
+ URL: "https://my.test.tld",
CABundle: []byte(testCertificate),
},
Auth: esv1beta1.KubernetesAuth{
@@ -132,6 +185,7 @@ func TestNewClient(t *testing.T) {
Provider: &esv1beta1.SecretStoreProvider{
Kubernetes: &esv1beta1.KubernetesProvider{
Server: esv1beta1.KubernetesServer{
+ URL: "https://my.test.tld",
CABundle: []byte(testCertificate),
},
RemoteNamespace: "remote",
@@ -166,6 +220,7 @@ func TestNewClient(t *testing.T) {
Provider: &esv1beta1.SecretStoreProvider{
Kubernetes: &esv1beta1.KubernetesProvider{
Server: esv1beta1.KubernetesServer{
+ URL: "https://my.test.tld",
CABundle: []byte(testCertificate),
},
RemoteNamespace: "remote",
diff --git a/pkg/provider/kubernetes/validate.go b/pkg/provider/kubernetes/validate.go
index 8f264d0ccb3..16c9cab21a3 100644
--- a/pkg/provider/kubernetes/validate.go
+++ b/pkg/provider/kubernetes/validate.go
@@ -16,7 +16,9 @@ package kubernetes
import (
"context"
+ "errors"
"fmt"
+ "slices"
authv1 "k8s.io/api/authorization/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -31,20 +33,20 @@ import (
func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnings, error) {
storeSpec := store.GetSpec()
k8sSpec := storeSpec.Provider.Kubernetes
- if k8sSpec.Server.CABundle == nil && k8sSpec.Server.CAProvider == nil {
- return nil, fmt.Errorf("a CABundle or CAProvider is required")
+ if k8sSpec.AuthRef == nil && k8sSpec.Server.CABundle == nil && k8sSpec.Server.CAProvider == nil {
+ return nil, errors.New("a CABundle or CAProvider is required")
}
if store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind &&
k8sSpec.Server.CAProvider != nil &&
k8sSpec.Server.CAProvider.Namespace == nil {
- return nil, fmt.Errorf("CAProvider.namespace must not be empty with ClusterSecretStore")
+ return nil, errors.New("CAProvider.namespace must not be empty with ClusterSecretStore")
}
if k8sSpec.Auth.Cert != nil {
if k8sSpec.Auth.Cert.ClientCert.Name == "" {
- return nil, fmt.Errorf("ClientCert.Name cannot be empty")
+ return nil, errors.New("ClientCert.Name cannot be empty")
}
if k8sSpec.Auth.Cert.ClientCert.Key == "" {
- return nil, fmt.Errorf("ClientCert.Key cannot be empty")
+ return nil, errors.New("ClientCert.Key cannot be empty")
}
if err := utils.ValidateSecretSelector(store, k8sSpec.Auth.Cert.ClientCert); err != nil {
return nil, err
@@ -52,10 +54,10 @@ func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnin
}
if k8sSpec.Auth.Token != nil {
if k8sSpec.Auth.Token.BearerToken.Name == "" {
- return nil, fmt.Errorf("BearerToken.Name cannot be empty")
+ return nil, errors.New("BearerToken.Name cannot be empty")
}
if k8sSpec.Auth.Token.BearerToken.Key == "" {
- return nil, fmt.Errorf("BearerToken.Key cannot be empty")
+ return nil, errors.New("BearerToken.Key cannot be empty")
}
if err := utils.ValidateSecretSelector(store, k8sSpec.Auth.Token.BearerToken); err != nil {
return nil, err
@@ -88,20 +90,11 @@ func (c *Client) Validate() (esv1beta1.ValidationResult, error) {
return esv1beta1.ValidationResultUnknown, fmt.Errorf("could not verify if client is valid: %w", err)
}
for _, rev := range authReview.Status.ResourceRules {
- if (contains("secrets", rev.Resources) || contains("*", rev.Resources)) &&
- (contains("get", rev.Verbs) || contains("*", rev.Verbs)) &&
- (len(rev.APIGroups) == 0 || (contains("", rev.APIGroups) || contains("*", rev.APIGroups))) {
+ if (slices.Contains(rev.Resources, "secrets") || slices.Contains(rev.Resources, "*")) &&
+ (slices.Contains(rev.Verbs, "get") || slices.Contains(rev.Verbs, "*")) &&
+ (len(rev.APIGroups) == 0 || (slices.Contains(rev.APIGroups, "") || slices.Contains(rev.APIGroups, "*"))) {
return esv1beta1.ValidationResultReady, nil
}
}
- return esv1beta1.ValidationResultError, fmt.Errorf("client is not allowed to get secrets")
-}
-
-func contains(sub string, args []string) bool {
- for _, k := range args {
- if k == sub {
- return true
- }
- }
- return false
+ return esv1beta1.ValidationResultError, errors.New("client is not allowed to get secrets")
}
diff --git a/pkg/provider/onboardbase/client.go b/pkg/provider/onboardbase/client.go
index 195f19463b1..05ce8028fee 100644
--- a/pkg/provider/onboardbase/client.go
+++ b/pkg/provider/onboardbase/client.go
@@ -17,6 +17,7 @@ package onboardbase
import (
"context"
"encoding/json"
+ "errors"
"fmt"
"net/url"
"strings"
@@ -71,7 +72,7 @@ func (c *Client) setAuth(ctx context.Context) error {
credentialsSecret := &corev1.Secret{}
credentialsSecretName := c.store.Auth.OnboardbaseAPIKeyRef.Name
if credentialsSecretName == "" {
- return fmt.Errorf(errOnboardbaseAPIKeySecretName)
+ return errors.New(errOnboardbaseAPIKeySecretName)
}
objectKey := types.NamespacedName{
Name: credentialsSecretName,
@@ -80,7 +81,7 @@ func (c *Client) setAuth(ctx context.Context) error {
// only ClusterStore is allowed to set namespace (and then it's required)
if c.storeKind == esv1beta1.ClusterSecretStoreKind {
if c.store.Auth.OnboardbaseAPIKeyRef.Namespace == nil {
- return fmt.Errorf(errInvalidClusterStoreMissingOnboardbaseAPIKeyNamespace)
+ return errors.New(errInvalidClusterStoreMissingOnboardbaseAPIKeyNamespace)
}
objectKey.Namespace = *c.store.Auth.OnboardbaseAPIKeyRef.Namespace
}
@@ -188,7 +189,7 @@ func (c *Client) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretD
func (c *Client) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
if len(ref.Tags) > 0 {
- return nil, fmt.Errorf("find by tags not supported")
+ return nil, errors.New("find by tags not supported")
}
secrets, err := c.getSecrets(ctx)
diff --git a/pkg/provider/onboardbase/fake/fake.go b/pkg/provider/onboardbase/fake/fake.go
index fe1e61be16d..5f73257e3e9 100644
--- a/pkg/provider/onboardbase/fake/fake.go
+++ b/pkg/provider/onboardbase/fake/fake.go
@@ -15,7 +15,7 @@ limitations under the License.
package fake
import (
- "fmt"
+ "errors"
"net/url"
"github.com/google/go-cmp/cmp"
@@ -51,7 +51,7 @@ func (obbc *OnboardbaseClient) WithValue(request client.SecretRequest, response
if obbc != nil {
obbc.getSecret = func(requestIn client.SecretRequest) (*client.SecretResponse, error) {
if !cmp.Equal(requestIn, request) {
- return nil, fmt.Errorf("unexpected test argument")
+ return nil, errors.New("unexpected test argument")
}
return response, err
}
diff --git a/pkg/provider/onboardbase/onboardbase_test.go b/pkg/provider/onboardbase/onboardbase_test.go
index 8494afbab5c..82f2f46b30a 100644
--- a/pkg/provider/onboardbase/onboardbase_test.go
+++ b/pkg/provider/onboardbase/onboardbase_test.go
@@ -16,7 +16,7 @@ package onboardbase
import (
"context"
- "fmt"
+ "errors"
"strings"
"testing"
@@ -128,7 +128,7 @@ func TestGetSecret(t *testing.T) {
pstc.request.Name = missingSecret
pstc.response = nil
pstc.expectError = missingSecretErr
- pstc.apiErr = fmt.Errorf("")
+ pstc.apiErr = errors.New("")
}
setInvalidSecret := func(pstc *onboardbaseTestCase) {
@@ -137,14 +137,14 @@ func TestGetSecret(t *testing.T) {
pstc.request.Name = invalidSecret
pstc.response = nil
pstc.expectError = missingSecretErr
- pstc.apiErr = fmt.Errorf("")
+ pstc.apiErr = errors.New("")
}
setClientError := func(pstc *onboardbaseTestCase) {
pstc.label = "invalid client error"
pstc.response = &client.SecretResponse{}
pstc.expectError = missingSecretErr
- pstc.apiErr = fmt.Errorf("")
+ pstc.apiErr = errors.New("")
}
testCases := []*onboardbaseTestCase{
@@ -175,7 +175,7 @@ func TestDeleteSecret(t *testing.T) {
pstc.request.Name = missingSecret
pstc.response = nil
pstc.expectError = missingSecretErr
- pstc.apiErr = fmt.Errorf("")
+ pstc.apiErr = errors.New("")
}
setInvalidSecret := func(pstc *onboardbaseTestCase) {
@@ -185,7 +185,7 @@ func TestDeleteSecret(t *testing.T) {
pstc.request.Name = invalidSecret
pstc.response = nil
pstc.expectError = missingSecretErr
- pstc.apiErr = fmt.Errorf("")
+ pstc.apiErr = errors.New("")
}
deleteSecret := func(pstc *onboardbaseTestCase) {
@@ -237,7 +237,7 @@ func TestGetSecretMap(t *testing.T) {
pstc.label = "client error"
pstc.response = &client.SecretResponse{}
pstc.expectError = missingSecretErr
- pstc.apiErr = fmt.Errorf("")
+ pstc.apiErr = errors.New("")
}
testCases := []*onboardbaseTestCase{
@@ -319,17 +319,17 @@ func TestValidateStore(t *testing.T) {
{
label: "invalid store missing onboardbaseAPIKey.name",
store: makeSecretStore(withAuth("", "", nil, "")),
- err: fmt.Errorf("invalid store: onboardbaseAPIKey.name cannot be empty"),
+ err: errors.New("invalid store: onboardbaseAPIKey.name cannot be empty"),
},
{
label: "invalid store missing onboardbasePasscode.name",
store: makeSecretStore(withAuth(secretName, "", nil, "")),
- err: fmt.Errorf("invalid store: onboardbasePasscode.name cannot be empty"),
+ err: errors.New("invalid store: onboardbasePasscode.name cannot be empty"),
},
{
label: "invalid store namespace not allowed",
store: makeSecretStore(withAuth(secretName, "", &namespace, "passcode")),
- err: fmt.Errorf("invalid store: namespace not allowed with namespaced SecretStore"),
+ err: errors.New("invalid store: namespace should either be empty or match the namespace of the SecretStore for a namespaced SecretStore"),
},
{
label: "valid provide optional onboardbaseAPIKey.key",
diff --git a/pkg/provider/onboardbase/provider.go b/pkg/provider/onboardbase/provider.go
index 915eb2c8ff9..d93cc2724fc 100644
--- a/pkg/provider/onboardbase/provider.go
+++ b/pkg/provider/onboardbase/provider.go
@@ -16,6 +16,7 @@ package onboardbase
import (
"context"
+ "errors"
"fmt"
kclient "sigs.k8s.io/controller-runtime/pkg/client"
@@ -53,7 +54,7 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore,
storeSpec := store.GetSpec()
if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.Onboardbase == nil {
- return nil, fmt.Errorf(errOnboardbaseStore)
+ return nil, errors.New(errOnboardbaseStore)
}
onboardbaseStoreSpec := storeSpec.Provider.Onboardbase
diff --git a/pkg/provider/onepassword/onepassword.go b/pkg/provider/onepassword/onepassword.go
index 256c3e70d1c..467773f4f57 100644
--- a/pkg/provider/onepassword/onepassword.go
+++ b/pkg/provider/onepassword/onepassword.go
@@ -20,6 +20,7 @@ import (
"fmt"
"net/url"
"sort"
+ "strings"
"time"
"github.com/1Password/connect-sdk-go/connect"
@@ -31,6 +32,7 @@ import (
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
"github.com/external-secrets/external-secrets/pkg/find"
"github.com/external-secrets/external-secrets/pkg/utils"
+ "github.com/external-secrets/external-secrets/pkg/utils/metadata"
"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
)
@@ -56,13 +58,17 @@ const (
errCreateItem = "error creating 1Password Item: %w"
errDeleteItem = "error deleting 1Password Item: %w"
// custom error messages.
- errKeyNotFoundMsg = "key not found in 1Password Vaults"
- errNoVaultsMsg = "no vaults found"
- errExpectedOneItemMsg = "expected one 1Password Item matching"
- errExpectedOneFieldMsg = "expected one 1Password ItemField matching"
- errExpectedOneFieldMsgF = "%w: '%s' in '%s', got %d"
+ errKeyNotFoundMsg = "key not found in 1Password Vaults"
+ errNoVaultsMsg = "no vaults found"
+ errMetadataVaultNotinProvider = "metadata vault '%s' not in provider vaults"
+ errExpectedOneItemMsg = "expected one 1Password Item matching"
+ errExpectedOneFieldMsg = "expected one 1Password ItemField matching"
+ errExpectedOneFieldMsgF = "%w: '%s' in '%s', got %d"
documentCategory = "DOCUMENT"
+ fieldPrefix = "field"
+ filePrefix = "file"
+ prefixSplitter = "/"
)
// Custom Errors //.
@@ -83,6 +89,11 @@ type ProviderOnePassword struct {
client connect.Client
}
+type PushSecretMetadataSpec struct {
+ Tags []string `json:"tags,omitempty"`
+ Vault string `json:"vault,omitempty"`
+}
+
// https://github.com/external-secrets/external-secrets/issues/644
var (
_ esv1beta1.SecretsClient = &ProviderOnePassword{}
@@ -121,22 +132,22 @@ func validateStore(store esv1beta1.GenericStore) error {
// check nils
storeSpec := store.GetSpec()
if storeSpec == nil {
- return fmt.Errorf(errOnePasswordStore, fmt.Errorf(errOnePasswordStoreNilSpec))
+ return fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreNilSpec))
}
if storeSpec.Provider == nil {
- return fmt.Errorf(errOnePasswordStore, fmt.Errorf(errOnePasswordStoreNilSpecProvider))
+ return fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreNilSpecProvider))
}
if storeSpec.Provider.OnePassword == nil {
- return fmt.Errorf(errOnePasswordStore, fmt.Errorf(errOnePasswordStoreNilSpecProviderOnePassword))
+ return fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreNilSpecProviderOnePassword))
}
// check mandatory fields
config := storeSpec.Provider.OnePassword
if config.Auth.SecretRef.ConnectToken.Name == "" {
- return fmt.Errorf(errOnePasswordStore, fmt.Errorf(errOnePasswordStoreMissingRefName))
+ return fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreMissingRefName))
}
if config.Auth.SecretRef.ConnectToken.Key == "" {
- return fmt.Errorf(errOnePasswordStore, fmt.Errorf(errOnePasswordStoreMissingRefKey))
+ return fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreMissingRefKey))
}
// check namespace compared to kind
@@ -146,12 +157,12 @@ func validateStore(store esv1beta1.GenericStore) error {
// check at least one vault
if len(config.Vaults) == 0 {
- return fmt.Errorf(errOnePasswordStore, fmt.Errorf(errOnePasswordStoreAtLeastOneVault))
+ return fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreAtLeastOneVault))
}
// ensure vault numbers are unique
if !hasUniqueVaultNumbers(config.Vaults) {
- return fmt.Errorf(errOnePasswordStore, fmt.Errorf(errOnePasswordStoreNonUniqueVaultNumbers))
+ return fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreNonUniqueVaultNumbers))
}
// check valid URL
@@ -209,7 +220,7 @@ func (provider *ProviderOnePassword) DeleteSecret(_ context.Context, ref esv1bet
}
func (provider *ProviderOnePassword) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf("not implemented")
+ return false, errors.New("not implemented")
}
const (
@@ -218,18 +229,40 @@ const (
// createItem creates a new item in the first vault. If no vaults exist, it returns an error.
func (provider *ProviderOnePassword) createItem(val []byte, ref esv1beta1.PushSecretData) error {
- // Get the first vault
- sortedVaults := sortVaults(provider.vaults)
- if len(sortedVaults) == 0 {
- return ErrNoVaults
+ // Get the metadata
+ metadata, err := metadata.ParseMetadataParameters[PushSecretMetadataSpec](ref.GetMetadata())
+ if err != nil {
+ return fmt.Errorf("failed to parse push secret metadata: %w", err)
+ }
+
+ // Check if there is a vault is specified in the metadata
+ vaultID := ""
+ if metadata != nil && metadata.Spec.Vault != "" {
+ // check if metadata.Spec.Vault is in provider.vaults
+ if _, ok := provider.vaults[metadata.Spec.Vault]; !ok {
+ return fmt.Errorf(errMetadataVaultNotinProvider, metadata.Spec.Vault)
+ }
+ vaultID = metadata.Spec.Vault
+ } else {
+ // Get the first vault from the provider
+ sortedVaults := sortVaults(provider.vaults)
+ if len(sortedVaults) == 0 {
+ return ErrNoVaults
+ }
+ vaultID = sortedVaults[0]
}
- vaultID := sortedVaults[0]
+
// Get the label
label := ref.GetProperty()
if label == "" {
label = passwordLabel
}
+ var tags []string
+ if metadata != nil && metadata.Spec.Tags != nil {
+ tags = metadata.Spec.Tags
+ }
+
// Create the item
item := &onepassword.Item{
Title: ref.GetRemoteKey(),
@@ -240,9 +273,10 @@ func (provider *ProviderOnePassword) createItem(val []byte, ref esv1beta1.PushSe
Fields: []*onepassword.ItemField{
generateNewItemField(label, string(val)),
},
+ Tags: tags,
}
- _, err := provider.client.CreateItem(item, vaultID)
+ _, err = provider.client.CreateItem(item, vaultID)
return err
}
@@ -313,6 +347,14 @@ func (provider *ProviderOnePassword) PushSecret(ctx context.Context, secret *cor
label = passwordLabel
}
+ metadata, err := metadata.ParseMetadataParameters[PushSecretMetadataSpec](ref.GetMetadata())
+ if err != nil {
+ return fmt.Errorf("failed to parse push secret metadata: %w", err)
+ }
+ if metadata != nil && metadata.Spec.Tags != nil {
+ providerItem.Tags = metadata.Spec.Tags
+ }
+
providerItem.Fields, err = updateFieldValue(providerItem.Fields, label, string(val))
if err != nil {
return fmt.Errorf(errUpdateItem, err)
@@ -329,10 +371,26 @@ func (provider *ProviderOnePassword) PushSecret(ctx context.Context, secret *cor
return nil
}
+// Clean property string by removing property prefix if needed.
+func getObjType(documentType onepassword.ItemCategory, property string) (string, string) {
+ if strings.HasPrefix(property, fieldPrefix+prefixSplitter) {
+ return fieldPrefix, property[6:]
+ }
+ if strings.HasPrefix(property, filePrefix+prefixSplitter) {
+ return filePrefix, property[5:]
+ }
+
+ if documentType == documentCategory {
+ return filePrefix, property
+ }
+
+ return fieldPrefix, property
+}
+
// GetSecret returns a single secret from the provider.
func (provider *ProviderOnePassword) GetSecret(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
if ref.Version != "" {
- return nil, fmt.Errorf(errVersionNotImplemented)
+ return nil, errors.New(errVersionNotImplemented)
}
item, err := provider.findItem(ref.Key)
@@ -340,14 +398,11 @@ func (provider *ProviderOnePassword) GetSecret(_ context.Context, ref esv1beta1.
return nil, err
}
- // handle files
- if item.Category == documentCategory {
- // default to the first file when ref.Property is empty
- return provider.getFile(item, ref.Property)
+ propertyType, property := getObjType(item.Category, ref.Property)
+ if propertyType == filePrefix {
+ return provider.getFile(item, property)
}
-
- // handle fields
- return provider.getField(item, ref.Property)
+ return provider.getField(item, property)
}
// Validate checks if the client is configured correctly
@@ -366,7 +421,7 @@ func (provider *ProviderOnePassword) Validate() (esv1beta1.ValidationResult, err
// GetSecretMap returns multiple k/v pairs from the provider, for dataFrom.extract.
func (provider *ProviderOnePassword) GetSecretMap(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
if ref.Version != "" {
- return nil, fmt.Errorf(errVersionNotImplemented)
+ return nil, errors.New(errVersionNotImplemented)
}
item, err := provider.findItem(ref.Key)
@@ -374,19 +429,17 @@ func (provider *ProviderOnePassword) GetSecretMap(_ context.Context, ref esv1bet
return nil, err
}
- // handle files
- if item.Category == documentCategory {
- return provider.getFiles(item, ref.Property)
+ propertyType, property := getObjType(item.Category, ref.Property)
+ if propertyType == filePrefix {
+ return provider.getFiles(item, property)
}
-
- // handle fields
- return provider.getFields(item, ref.Property)
+ return provider.getFields(item, property)
}
// GetAllSecrets syncs multiple 1Password Items into a single Kubernetes Secret, for dataFrom.find.
func (provider *ProviderOnePassword) GetAllSecrets(_ context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
if ref.Tags != nil {
- return nil, fmt.Errorf(errTagsNotImplemented)
+ return nil, errors.New(errTagsNotImplemented)
}
secretData := make(map[string][]byte)
@@ -568,13 +621,9 @@ func (provider *ProviderOnePassword) getAllForVault(vaultID string, ref esv1beta
}
// handle files
- if item.Category == documentCategory {
- err = provider.getAllFiles(item, ref, secretData)
- if err != nil {
- return err
- }
-
- continue
+ err = provider.getAllFiles(item, ref, secretData)
+ if err != nil {
+ return err
}
// handle fields
diff --git a/pkg/provider/onepassword/onepassword_test.go b/pkg/provider/onepassword/onepassword_test.go
index 27b7a96ea2d..77c2d92545b 100644
--- a/pkg/provider/onepassword/onepassword_test.go
+++ b/pkg/provider/onepassword/onepassword_test.go
@@ -16,6 +16,7 @@ package onepassword
import (
"context"
+ "encoding/json"
"errors"
"fmt"
"reflect"
@@ -30,6 +31,7 @@ import (
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
"github.com/external-secrets/external-secrets/pkg/provider/onepassword/fake"
+ "github.com/external-secrets/external-secrets/pkg/utils/metadata"
)
const (
@@ -178,7 +180,7 @@ func TestFindItem(t *testing.T) {
{
checkNote: "two vaults",
findItemName: myItem,
- expectedErr: fmt.Errorf("key not found in 1Password Vaults: my-item in: map[my-shared-vault:2 my-vault:1]"),
+ expectedErr: errors.New("key not found in 1Password Vaults: my-item in: map[my-shared-vault:2 my-vault:1]"),
},
},
},
@@ -371,7 +373,7 @@ func TestValidateStore(t *testing.T) {
Provider: nil,
},
},
- expectedErr: fmt.Errorf(errOnePasswordStore, fmt.Errorf(errOnePasswordStoreNilSpecProvider)),
+ expectedErr: fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreNilSpecProvider)),
},
{
checkNote: "invalid: nil OnePassword provider spec",
@@ -385,7 +387,7 @@ func TestValidateStore(t *testing.T) {
},
},
},
- expectedErr: fmt.Errorf(errOnePasswordStore, fmt.Errorf(errOnePasswordStoreNilSpecProviderOnePassword)),
+ expectedErr: fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreNilSpecProviderOnePassword)),
},
{
checkNote: "valid secretStore",
@@ -441,7 +443,7 @@ func TestValidateStore(t *testing.T) {
},
},
},
- expectedErr: fmt.Errorf(errOnePasswordStore, fmt.Errorf("namespace not allowed with namespaced SecretStore")),
+ expectedErr: fmt.Errorf(errOnePasswordStore, errors.New("namespace should either be empty or match the namespace of the SecretStore for a namespaced SecretStore")),
},
{
checkNote: "invalid: more than one vault with the same number",
@@ -469,7 +471,7 @@ func TestValidateStore(t *testing.T) {
},
},
},
- expectedErr: fmt.Errorf(errOnePasswordStore, fmt.Errorf(errOnePasswordStoreNonUniqueVaultNumbers)),
+ expectedErr: fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreNonUniqueVaultNumbers)),
},
{
checkNote: "valid: clusterSecretStore",
@@ -525,7 +527,7 @@ func TestValidateStore(t *testing.T) {
},
},
},
- expectedErr: fmt.Errorf(errOnePasswordStore, fmt.Errorf("cluster scope requires namespace")),
+ expectedErr: fmt.Errorf(errOnePasswordStore, errors.New("cluster scope requires namespace")),
},
{
checkNote: "invalid: missing connectTokenSecretRef.name",
@@ -552,7 +554,7 @@ func TestValidateStore(t *testing.T) {
},
},
},
- expectedErr: fmt.Errorf(errOnePasswordStore, fmt.Errorf(errOnePasswordStoreMissingRefName)),
+ expectedErr: fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreMissingRefName)),
},
{
checkNote: "invalid: missing connectTokenSecretRef.key",
@@ -579,7 +581,7 @@ func TestValidateStore(t *testing.T) {
},
},
},
- expectedErr: fmt.Errorf(errOnePasswordStore, fmt.Errorf(errOnePasswordStoreMissingRefKey)),
+ expectedErr: fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreMissingRefKey)),
},
{
checkNote: "invalid: at least one vault",
@@ -604,7 +606,7 @@ func TestValidateStore(t *testing.T) {
},
},
},
- expectedErr: fmt.Errorf(errOnePasswordStore, fmt.Errorf(errOnePasswordStoreAtLeastOneVault)),
+ expectedErr: fmt.Errorf(errOnePasswordStore, errors.New(errOnePasswordStoreAtLeastOneVault)),
},
{
checkNote: "invalid: url",
@@ -631,7 +633,7 @@ func TestValidateStore(t *testing.T) {
},
},
},
- expectedErr: fmt.Errorf(errOnePasswordStore, fmt.Errorf(errOnePasswordStoreInvalidConnectHost, fmt.Errorf("parse \":/invalid.invalid\": missing protocol scheme"))),
+ expectedErr: fmt.Errorf(errOnePasswordStore, fmt.Errorf(errOnePasswordStoreInvalidConnectHost, errors.New("parse \":/invalid.invalid\": missing protocol scheme"))),
},
}
@@ -685,11 +687,26 @@ func TestGetSecret(t *testing.T) {
vaults: map[string]int{myVault: 1},
client: fake.NewMockClient().
AddPredictableVault(myVault).
- AddPredictableItemWithField(myVault, myItem, key1, value1).
+ AppendItem(myVaultID, onepassword.Item{
+ ID: myItemID,
+ Title: myItem,
+ Vault: onepassword.ItemVault{ID: myVaultID},
+ Files: []*onepassword.File{
+ {
+ ID: myFilePNGID,
+ Name: myFilePNG,
+ },
+ },
+ }).
AppendItemField(myVaultID, myItemID, onepassword.ItemField{
Label: password,
Value: value2,
- }),
+ }).
+ AppendItemField(myVaultID, myItemID, onepassword.ItemField{
+ Label: key1,
+ Value: value1,
+ }).
+ SetFileContents(myFilePNG, []byte(myContents)),
},
checks: []check{
{
@@ -701,6 +718,15 @@ func TestGetSecret(t *testing.T) {
expectedValue: value1,
expectedErr: nil,
},
+ {
+ checkNote: key1 + " with prefix",
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: myItem,
+ Property: fieldPrefix + prefixSplitter + key1,
+ },
+ expectedValue: value1,
+ expectedErr: nil,
+ },
{
checkNote: "'password' (defaulted property)",
ref: esv1beta1.ExternalSecretDataRemoteRef{
@@ -716,7 +742,16 @@ func TestGetSecret(t *testing.T) {
Property: key1,
Version: "123",
},
- expectedErr: fmt.Errorf(errVersionNotImplemented),
+ expectedErr: errors.New(errVersionNotImplemented),
+ },
+ {
+ checkNote: "file named my-file.png with prefix",
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: myItem,
+ Property: filePrefix + prefixSplitter + myFilePNG,
+ },
+ expectedValue: myContents,
+ expectedErr: nil,
},
},
},
@@ -738,9 +773,22 @@ func TestGetSecret(t *testing.T) {
},
},
}).
+ AppendItemField(myVaultID, myItemID, onepassword.ItemField{
+ Label: key1,
+ Value: value2,
+ }).
SetFileContents(myFilePNG, []byte(myContents)),
},
checks: []check{
+ {
+ checkNote: "field named password",
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: myItem,
+ Property: fieldPrefix + prefixSplitter + key1,
+ },
+ expectedValue: value2,
+ expectedErr: nil,
+ },
{
checkNote: "file named my-file.png",
ref: esv1beta1.ExternalSecretDataRemoteRef{
@@ -750,6 +798,15 @@ func TestGetSecret(t *testing.T) {
expectedValue: myContents,
expectedErr: nil,
},
+ {
+ checkNote: "file named my-file.png with prefix",
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: myItem,
+ Property: filePrefix + prefixSplitter + myFilePNG,
+ },
+ expectedValue: myContents,
+ expectedErr: nil,
+ },
{
checkNote: "empty ref.Property",
ref: esv1beta1.ExternalSecretDataRemoteRef{
@@ -764,10 +821,19 @@ func TestGetSecret(t *testing.T) {
Key: myItem,
Property: "you-cant-find-me.png",
},
- expectedErr: fmt.Errorf(errDocumentNotFound, fmt.Errorf("'my-item', 'you-cant-find-me.png'")),
+ expectedErr: fmt.Errorf(errDocumentNotFound, errors.New("'my-item', 'you-cant-find-me.png'")),
+ },
+ {
+ checkNote: "file non existent with prefix",
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: myItem,
+ Property: "file/you-cant-find-me.png",
+ },
+ expectedErr: fmt.Errorf(errDocumentNotFound, errors.New("'my-item', 'you-cant-find-me.png'")),
},
},
},
+
{
setupNote: "one vault, one item, two fields w/ same Label",
provider: &ProviderOnePassword{
@@ -845,11 +911,31 @@ func TestGetSecretMap(t *testing.T) {
vaults: map[string]int{myVault: 1},
client: fake.NewMockClient().
AddPredictableVault(myVault).
- AddPredictableItemWithField(myVault, myItem, key1, value1).
+ AppendItem(myVaultID, onepassword.Item{
+ ID: myItemID,
+ Title: myItem,
+ Vault: onepassword.ItemVault{ID: myVaultID},
+ Files: []*onepassword.File{
+ {
+ ID: myFilePNGID,
+ Name: myFilePNG,
+ },
+ {
+ ID: myFile2ID,
+ Name: myFile2PNG,
+ },
+ },
+ }).
+ AppendItemField(myVaultID, myItemID, onepassword.ItemField{
+ Label: key1,
+ Value: value1,
+ }).
AppendItemField(myVaultID, myItemID, onepassword.ItemField{
Label: password,
Value: value2,
- }),
+ }).
+ SetFileContents(myFilePNG, []byte(myContents)).
+ SetFileContents(myFile2PNG, []byte(myContents2)),
},
checks: []check{
{
@@ -881,7 +967,18 @@ func TestGetSecretMap(t *testing.T) {
Property: key1,
Version: "123",
},
- expectedErr: fmt.Errorf(errVersionNotImplemented),
+ expectedErr: errors.New(errVersionNotImplemented),
+ },
+ {
+ checkNote: "limit by Property with prefix",
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: myItem,
+ Property: filePrefix + prefixSplitter + myFilePNG,
+ },
+ expectedMap: map[string][]byte{
+ myFilePNG: []byte(myContents),
+ },
+ expectedErr: nil,
},
},
},
@@ -907,6 +1004,10 @@ func TestGetSecretMap(t *testing.T) {
},
},
}).
+ AppendItemField(myVaultID, myItemID, onepassword.ItemField{
+ Label: key1,
+ Value: value2,
+ }).
SetFileContents(myFilePNG, []byte(myContents)).
SetFileContents(myFile2PNG, []byte(myContents2)),
},
@@ -933,6 +1034,28 @@ func TestGetSecretMap(t *testing.T) {
},
expectedErr: nil,
},
+ {
+ checkNote: "limit by Property with prefix",
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: myItem,
+ Property: filePrefix + prefixSplitter + myFilePNG,
+ },
+ expectedMap: map[string][]byte{
+ myFilePNG: []byte(myContents),
+ },
+ expectedErr: nil,
+ },
+ {
+ checkNote: "get field limit by Property",
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: myItem,
+ Property: fieldPrefix + prefixSplitter + key1,
+ },
+ expectedMap: map[string][]byte{
+ key1: []byte(value2),
+ },
+ expectedErr: nil,
+ },
},
},
{
@@ -1096,7 +1219,7 @@ func TestGetAllSecrets(t *testing.T) {
"asdf": "fdas",
},
},
- expectedErr: fmt.Errorf(errTagsNotImplemented),
+ expectedErr: errors.New(errTagsNotImplemented),
},
},
},
@@ -1418,6 +1541,7 @@ type fakeRef struct {
key string
prop string
secretKey string
+ metadata *apiextensionsv1.JSON
}
func (f fakeRef) GetRemoteKey() string {
@@ -1433,7 +1557,7 @@ func (f fakeRef) GetSecretKey() string {
}
func (f fakeRef) GetMetadata() *apiextensionsv1.JSON {
- return nil
+ return f.metadata
}
func validateItem(t *testing.T, expectedItem, actualItem *onepassword.Item) {
@@ -1453,9 +1577,20 @@ func TestProviderOnePasswordCreateItem(t *testing.T) {
ref esv1beta1.PushSecretData
}
const vaultName = "vault1"
+ const fallbackVaultName = "vault2"
thridPartyErr := errors.New("third party error")
+ metadata := &metadata.PushSecretMetadata[PushSecretMetadataSpec]{
+ APIVersion: metadata.APIVersion,
+ Kind: metadata.Kind,
+ Spec: PushSecretMetadataSpec{
+ Tags: []string{"tag1", "tag2"},
+ Vault: fallbackVaultName,
+ },
+ }
+ metadataRaw, _ := json.Marshal(metadata)
+
testCases := []testCase{
{
setupNote: "standard create",
@@ -1466,7 +1601,8 @@ func TestProviderOnePasswordCreateItem(t *testing.T) {
},
expectedErr: nil,
vaults: map[string]int{
- vaultName: 1,
+ vaultName: 1,
+ fallbackVaultName: 2,
},
createValidateFunc: func(t *testing.T, item *onepassword.Item, s string) (*onepassword.Item, error) {
validateItem(t, &onepassword.Item{
@@ -1545,6 +1681,36 @@ func TestProviderOnePasswordCreateItem(t *testing.T) {
return nil, thridPartyErr
},
},
+ {
+ setupNote: "valid metadata overrides",
+ val: []byte("testing"),
+ ref: fakeRef{
+ key: "another",
+ prop: "property",
+ metadata: &apiextensionsv1.JSON{
+ Raw: metadataRaw,
+ },
+ },
+ vaults: map[string]int{
+ vaultName: 1,
+ fallbackVaultName: 2,
+ },
+ expectedErr: nil,
+ createValidateFunc: func(t *testing.T, item *onepassword.Item, s string) (*onepassword.Item, error) {
+ validateItem(t, &onepassword.Item{
+ Title: "another",
+ Category: onepassword.Server,
+ Vault: onepassword.ItemVault{
+ ID: fallbackVaultName,
+ },
+ Fields: []*onepassword.ItemField{
+ generateNewItemField("property", "testing"),
+ },
+ Tags: []string{"tag1", "tag2"},
+ }, item)
+ return item, nil
+ },
+ },
}
provider := &ProviderOnePassword{}
for _, tc := range testCases {
@@ -1929,6 +2095,16 @@ func TestProviderOnePasswordPushSecret(t *testing.T) {
ID: vaultName,
}
)
+
+ metadata := &metadata.PushSecretMetadata[PushSecretMetadataSpec]{
+ APIVersion: metadata.APIVersion,
+ Kind: metadata.Kind,
+ Spec: PushSecretMetadataSpec{
+ Tags: []string{"tag1", "tag2"},
+ },
+ }
+ metadataRaw, _ := json.Marshal(metadata)
+
testCases := []testCase{
{
vaults: map[string]int{
@@ -2077,6 +2253,38 @@ func TestProviderOnePasswordPushSecret(t *testing.T) {
},
},
},
+ {
+ setupNote: "create item with metadata overwrites success",
+ expectedErr: nil,
+ val: &corev1.Secret{Data: map[string][]byte{
+ key1: []byte("testing"),
+ }},
+ ref: fakeRef{
+ key: key1,
+ prop: "prop",
+ secretKey: key1,
+ metadata: &apiextensionsv1.JSON{
+ Raw: metadataRaw,
+ },
+ },
+ vaults: map[string]int{
+ vaultName: 1,
+ },
+ createValidateFunc: func(item *onepassword.Item, s string) (*onepassword.Item, error) {
+ validateItem(t, &onepassword.Item{
+ Title: key1,
+ Category: onepassword.Server,
+ Vault: onepassword.ItemVault{
+ ID: vaultName,
+ },
+ Fields: []*onepassword.ItemField{
+ generateNewItemField("prop", "testing"),
+ },
+ Tags: []string{"tag1", "tag2"},
+ }, item)
+ return item, nil
+ },
+ },
}
provider := &ProviderOnePassword{}
for _, tc := range testCases {
diff --git a/pkg/provider/oracle/oracle.go b/pkg/provider/oracle/oracle.go
index 41a01ecfb74..0ecde55bf17 100644
--- a/pkg/provider/oracle/oracle.go
+++ b/pkg/provider/oracle/oracle.go
@@ -58,6 +58,7 @@ const (
errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
errMissingKey = "missing Key in secret: %s"
errUnexpectedContent = "unexpected secret bundle content"
+ errSettingOCIEnvVariables = "unable to set OCI SDK environment variable %s: %w"
)
// https://github.com/external-secrets/external-secrets/issues/644
@@ -96,11 +97,22 @@ const (
)
func (vms *VaultManagementService) PushSecret(ctx context.Context, secret *corev1.Secret, data esv1beta1.PushSecretData) error {
+ if vms.encryptionKey == "" {
+ return errors.New("SecretStore must reference encryption key")
+ }
+ value := secret.Data[data.GetSecretKey()]
if data.GetSecretKey() == "" {
- return fmt.Errorf("pushing the whole secret is not yet implemented")
+ secretData := map[string]string{}
+ for k, v := range secret.Data {
+ secretData[k] = string(v)
+ }
+ jsonSecret, err := json.Marshal(secretData)
+ if err != nil {
+ return fmt.Errorf("unable to create json %v from value: %v", value, secretData)
+ }
+ value = jsonSecret
}
- value := secret.Data[data.GetSecretKey()]
secretName := data.GetRemoteKey()
encodedValue := base64.StdEncoding.EncodeToString(value)
sec, action, err := vms.getSecretBundleWithCode(ctx, secretName)
@@ -160,7 +172,7 @@ func (vms *VaultManagementService) DeleteSecret(ctx context.Context, remoteRef e
}
func (vms *VaultManagementService) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf("not implemented")
+ return false, errors.New("not implemented")
}
func (vms *VaultManagementService) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
@@ -187,7 +199,7 @@ func (vms *VaultManagementService) GetAllSecrets(ctx context.Context, ref esv1be
func (vms *VaultManagementService) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
if utils.IsNil(vms.Client) {
- return nil, fmt.Errorf(errUninitalizedOracleProvider)
+ return nil, errors.New(errUninitalizedOracleProvider)
}
sec, err := vms.Client.GetSecretBundleByName(ctx, secrets.GetSecretBundleByNameRequest{
@@ -218,7 +230,7 @@ func (vms *VaultManagementService) GetSecret(ctx context.Context, ref esv1beta1.
func decodeBundle(sec secrets.GetSecretBundleByNameResponse) ([]byte, error) {
bt, ok := sec.SecretBundleContent.(secrets.Base64SecretBundleContentDetails)
if !ok {
- return nil, fmt.Errorf(errUnexpectedContent)
+ return nil, errors.New(errUnexpectedContent)
}
payload, err := base64.StdEncoding.DecodeString(*bt.Content)
if err != nil {
@@ -255,27 +267,16 @@ func (vms *VaultManagementService) NewClient(ctx context.Context, store esv1beta
oracleSpec := storeSpec.Provider.Oracle
if oracleSpec.Vault == "" {
- return nil, fmt.Errorf(errMissingVault)
+ return nil, errors.New(errMissingVault)
}
if oracleSpec.Region == "" {
- return nil, fmt.Errorf(errMissingRegion)
+ return nil, errors.New(errMissingRegion)
}
- var (
- err error
- configurationProvider common.ConfigurationProvider
- )
-
- if oracleSpec.PrincipalType == esv1beta1.WorkloadPrincipal {
- configurationProvider, err = vms.getWorkloadIdentityProvider(store, oracleSpec.ServiceAccountRef, oracleSpec.Region, namespace)
- } else if oracleSpec.PrincipalType == esv1beta1.InstancePrincipal || oracleSpec.Auth == nil {
- configurationProvider, err = auth.InstancePrincipalConfigurationProvider()
- } else {
- configurationProvider, err = getUserAuthConfigurationProvider(ctx, kube, oracleSpec, namespace, store.GetObjectKind().GroupVersionKind().Kind, oracleSpec.Region)
- }
+ configurationProvider, err := vms.constructProvider(ctx, store, oracleSpec, kube, namespace)
if err != nil {
- return nil, fmt.Errorf(errOracleClient, err)
+ return nil, err
}
secretManagementService, err := secrets.NewSecretsClientWithConfigurationProvider(configurationProvider)
@@ -297,33 +298,9 @@ func (vms *VaultManagementService) NewClient(ctx context.Context, store esv1beta
vaultClient.SetRegion(oracleSpec.Region)
if storeSpec.RetrySettings != nil {
- opts := []common.RetryPolicyOption{common.WithShouldRetryOperation(common.DefaultShouldRetryOperation)}
-
- if mr := storeSpec.RetrySettings.MaxRetries; mr != nil {
- opts = append(opts, common.WithMaximumNumberAttempts(uint(*mr)))
- }
-
- if ri := storeSpec.RetrySettings.RetryInterval; ri != nil {
- i, err := time.ParseDuration(*storeSpec.RetrySettings.RetryInterval)
- if err != nil {
- return nil, fmt.Errorf(errOracleClient, err)
- }
- opts = append(opts, common.WithFixedBackoff(i))
+ if err := vms.configureRetryPolicy(storeSpec, secretManagementService, kmsVaultClient, vaultClient); err != nil {
+ return nil, fmt.Errorf(errOracleClient, err)
}
-
- customRetryPolicy := common.NewRetryPolicyWithOptions(opts...)
-
- secretManagementService.SetCustomClientConfiguration(common.CustomClientConfiguration{
- RetryPolicy: &customRetryPolicy,
- })
-
- kmsVaultClient.SetCustomClientConfiguration(common.CustomClientConfiguration{
- RetryPolicy: &customRetryPolicy,
- })
-
- vaultClient.SetCustomClientConfiguration(common.CustomClientConfiguration{
- RetryPolicy: &customRetryPolicy,
- })
}
return &VaultManagementService{
@@ -336,6 +313,32 @@ func (vms *VaultManagementService) NewClient(ctx context.Context, store esv1beta
}, nil
}
+func (vms *VaultManagementService) constructOptions(storeSpec *esv1beta1.SecretStoreSpec) ([]common.RetryPolicyOption, error) {
+ opts := []common.RetryPolicyOption{common.WithShouldRetryOperation(common.DefaultShouldRetryOperation)}
+
+ if mr := storeSpec.RetrySettings.MaxRetries; mr != nil {
+ attempts := safeConvert(*mr)
+ opts = append(opts, common.WithMaximumNumberAttempts(attempts))
+ }
+
+ if ri := storeSpec.RetrySettings.RetryInterval; ri != nil {
+ i, err := time.ParseDuration(*storeSpec.RetrySettings.RetryInterval)
+ if err != nil {
+ return nil, fmt.Errorf(errOracleClient, err)
+ }
+ opts = append(opts, common.WithFixedBackoff(i))
+ }
+ return opts, nil
+}
+
+func safeConvert(i int32) uint {
+ if i < 0 {
+ return 0
+ }
+
+ return uint(i)
+}
+
func (vms *VaultManagementService) getSecretBundleWithCode(ctx context.Context, secretName string) (secrets.GetSecretBundleByNameResponse, int, error) {
// Try to look up the secret, which will determine if we should create or update the secret.
resp, err := vms.Client.GetSecretBundleByName(ctx, secrets.GetSecretBundleByNameRequest{
@@ -401,7 +404,7 @@ func matchesRef(secretSummary vault.SecretSummary, ref esv1beta1.ExternalSecretF
func getSecretData(ctx context.Context, kube kclient.Client, namespace, storeKind string, secretRef esmeta.SecretKeySelector) (string, error) {
if secretRef.Name == "" {
- return "", fmt.Errorf(errORACLECredSecretName)
+ return "", errors.New(errORACLECredSecretName)
}
secret, err := resolvers.SecretKeyRef(
ctx,
@@ -422,7 +425,7 @@ func getUserAuthConfigurationProvider(ctx context.Context, kube kclient.Client,
return nil, err
}
if privateKey == "" {
- return nil, fmt.Errorf(errMissingPK)
+ return nil, errors.New(errMissingPK)
}
fingerprint, err := getSecretData(ctx, kube, namespace, storeKind, store.Auth.SecretRef.Fingerprint)
@@ -430,15 +433,15 @@ func getUserAuthConfigurationProvider(ctx context.Context, kube kclient.Client,
return nil, err
}
if fingerprint == "" {
- return nil, fmt.Errorf(errMissingFingerprint)
+ return nil, errors.New(errMissingFingerprint)
}
if store.Auth.User == "" {
- return nil, fmt.Errorf(errMissingUser)
+ return nil, errors.New(errMissingUser)
}
if store.Auth.Tenancy == "" {
- return nil, fmt.Errorf(errMissingTenancy)
+ return nil, errors.New(errMissingTenancy)
}
return common.NewRawConfigurationProvider(store.Auth.Tenancy, store.Auth.User, region, fingerprint, privateKey, nil), nil
@@ -489,12 +492,12 @@ func (vms *VaultManagementService) ValidateStore(store esv1beta1.GenericStore) (
vault := oracleSpec.Vault
if vault == "" {
- return nil, fmt.Errorf("vault cannot be empty")
+ return nil, errors.New("vault cannot be empty")
}
region := oracleSpec.Region
if region == "" {
- return nil, fmt.Errorf("region cannot be empty")
+ return nil, errors.New("region cannot be empty")
}
auth := oracleSpec.Auth
@@ -504,21 +507,21 @@ func (vms *VaultManagementService) ValidateStore(store esv1beta1.GenericStore) (
user := oracleSpec.Auth.User
if user == "" {
- return nil, fmt.Errorf("user cannot be empty")
+ return nil, errors.New("user cannot be empty")
}
tenant := oracleSpec.Auth.Tenancy
if tenant == "" {
- return nil, fmt.Errorf("tenant cannot be empty")
+ return nil, errors.New("tenant cannot be empty")
}
privateKey := oracleSpec.Auth.SecretRef.PrivateKey
if privateKey.Name == "" {
- return nil, fmt.Errorf("privateKey.name cannot be empty")
+ return nil, errors.New("privateKey.name cannot be empty")
}
if privateKey.Key == "" {
- return nil, fmt.Errorf("privateKey.key cannot be empty")
+ return nil, errors.New("privateKey.key cannot be empty")
}
err := utils.ValidateSecretSelector(store, privateKey)
@@ -529,11 +532,11 @@ func (vms *VaultManagementService) ValidateStore(store esv1beta1.GenericStore) (
fingerprint := oracleSpec.Auth.SecretRef.Fingerprint
if fingerprint.Name == "" {
- return nil, fmt.Errorf("fingerprint.name cannot be empty")
+ return nil, errors.New("fingerprint.name cannot be empty")
}
if fingerprint.Key == "" {
- return nil, fmt.Errorf("fingerprint.key cannot be empty")
+ return nil, errors.New("fingerprint.key cannot be empty")
}
err = utils.ValidateSecretSelector(store, fingerprint)
@@ -553,7 +556,7 @@ func (vms *VaultManagementService) ValidateStore(store esv1beta1.GenericStore) (
func (vms *VaultManagementService) getWorkloadIdentityProvider(store esv1beta1.GenericStore, serviceAcccountRef *esmeta.ServiceAccountSelector, region, namespace string) (configurationProvider common.ConfigurationProvider, err error) {
defer func() {
if uerr := os.Unsetenv(auth.ResourcePrincipalVersionEnvVar); uerr != nil {
- err = errors.Join(err, fmt.Errorf("unable to set OCI SDK environment variable %s: %w", auth.ResourcePrincipalRegionEnvVar, uerr))
+ err = errors.Join(err, fmt.Errorf(errSettingOCIEnvVariables, auth.ResourcePrincipalRegionEnvVar, uerr))
}
if uerr := os.Unsetenv(auth.ResourcePrincipalRegionEnvVar); uerr != nil {
err = errors.Join(err, fmt.Errorf("unabled to unset OCI SDK environment variable %s: %w", auth.ResourcePrincipalVersionEnvVar, uerr))
@@ -563,10 +566,10 @@ func (vms *VaultManagementService) getWorkloadIdentityProvider(store esv1beta1.G
vms.workloadIdentityMutex.Lock()
// OCI SDK requires specific environment variables for workload identity.
if err := os.Setenv(auth.ResourcePrincipalVersionEnvVar, auth.ResourcePrincipalVersion2_2); err != nil {
- return nil, fmt.Errorf("unable to set OCI SDK environment variable %s: %w", auth.ResourcePrincipalVersionEnvVar, err)
+ return nil, fmt.Errorf(errSettingOCIEnvVariables, auth.ResourcePrincipalVersionEnvVar, err)
}
if err := os.Setenv(auth.ResourcePrincipalRegionEnvVar, region); err != nil {
- return nil, fmt.Errorf("unable to set OCI SDK environment variable %s: %w", auth.ResourcePrincipalRegionEnvVar, err)
+ return nil, fmt.Errorf(errSettingOCIEnvVariables, auth.ResourcePrincipalRegionEnvVar, err)
}
// If no service account is specified, use the pod service account to create the Workload Identity provider.
if serviceAcccountRef == nil {
@@ -588,6 +591,54 @@ func (vms *VaultManagementService) getWorkloadIdentityProvider(store esv1beta1.G
return auth.OkeWorkloadIdentityConfigurationProviderWithServiceAccountTokenProvider(tokenProvider)
}
+func (vms *VaultManagementService) constructProvider(ctx context.Context, store esv1beta1.GenericStore, oracleSpec *esv1beta1.OracleProvider, kube kclient.Client, namespace string) (common.ConfigurationProvider, error) {
+ var (
+ configurationProvider common.ConfigurationProvider
+ err error
+ )
+
+ if oracleSpec.PrincipalType == esv1beta1.WorkloadPrincipal {
+ configurationProvider, err = vms.getWorkloadIdentityProvider(store, oracleSpec.ServiceAccountRef, oracleSpec.Region, namespace)
+ } else if oracleSpec.PrincipalType == esv1beta1.InstancePrincipal || oracleSpec.Auth == nil {
+ configurationProvider, err = auth.InstancePrincipalConfigurationProvider()
+ } else {
+ configurationProvider, err = getUserAuthConfigurationProvider(ctx, kube, oracleSpec, namespace, store.GetObjectKind().GroupVersionKind().Kind, oracleSpec.Region)
+ }
+ if err != nil {
+ return nil, fmt.Errorf(errOracleClient, err)
+ }
+
+ return configurationProvider, nil
+}
+
+func (vms *VaultManagementService) configureRetryPolicy(
+ storeSpec *esv1beta1.SecretStoreSpec,
+ secretManagementService secrets.SecretsClient,
+ kmsVaultClient keymanagement.KmsVaultClient,
+ vaultClient vault.VaultsClient,
+) error {
+ opts, err := vms.constructOptions(storeSpec)
+ if err != nil {
+ return err
+ }
+
+ customRetryPolicy := common.NewRetryPolicyWithOptions(opts...)
+
+ secretManagementService.SetCustomClientConfiguration(common.CustomClientConfiguration{
+ RetryPolicy: &customRetryPolicy,
+ })
+
+ kmsVaultClient.SetCustomClientConfiguration(common.CustomClientConfiguration{
+ RetryPolicy: &customRetryPolicy,
+ })
+
+ vaultClient.SetCustomClientConfiguration(common.CustomClientConfiguration{
+ RetryPolicy: &customRetryPolicy,
+ })
+
+ return err
+}
+
func sanitizeOCISDKErr(err error) error {
if err == nil {
return nil
diff --git a/pkg/provider/oracle/oracle_test.go b/pkg/provider/oracle/oracle_test.go
index a472bb17a90..5f618835dec 100644
--- a/pkg/provider/oracle/oracle_test.go
+++ b/pkg/provider/oracle/oracle_test.go
@@ -21,6 +21,7 @@ import (
"crypto/x509"
"encoding/base64"
"encoding/pem"
+ "errors"
"fmt"
"reflect"
"strings"
@@ -112,7 +113,7 @@ func makeValidVaultTestCaseCustom(tweaks ...func(smtc *vaultTestCase)) *vaultTes
// This case can be shared by both GetSecret and GetSecretMap tests.
// bad case: set apiErr.
var setAPIErr = func(smtc *vaultTestCase) {
- smtc.apiErr = fmt.Errorf("oh no")
+ smtc.apiErr = errors.New("oh no")
smtc.expectError = "oh no"
}
@@ -264,43 +265,43 @@ func TestValidateStore(t *testing.T) {
testCases := []ValidateStoreTestCase{
{
store: makeSecretStore("", region),
- err: fmt.Errorf("vault cannot be empty"),
+ err: errors.New("vault cannot be empty"),
},
{
store: makeSecretStore(vaultOCID, ""),
- err: fmt.Errorf("region cannot be empty"),
+ err: errors.New("region cannot be empty"),
},
{
store: makeSecretStore(vaultOCID, region, withSecretAuth("", tenant)),
- err: fmt.Errorf("user cannot be empty"),
+ err: errors.New("user cannot be empty"),
},
{
store: makeSecretStore(vaultOCID, region, withSecretAuth(userOCID, "")),
- err: fmt.Errorf("tenant cannot be empty"),
+ err: errors.New("tenant cannot be empty"),
},
{
store: makeSecretStore(vaultOCID, region, withSecretAuth(userOCID, tenant), withPrivateKey("", secretKey, nil)),
- err: fmt.Errorf("privateKey.name cannot be empty"),
+ err: errors.New("privateKey.name cannot be empty"),
},
{
store: makeSecretStore(vaultOCID, region, withSecretAuth(userOCID, tenant), withPrivateKey(secretName, secretKey, &namespace)),
- err: fmt.Errorf("namespace not allowed with namespaced SecretStore"),
+ err: errors.New("namespace should either be empty or match the namespace of the SecretStore for a namespaced SecretStore"),
},
{
store: makeSecretStore(vaultOCID, region, withSecretAuth(userOCID, tenant), withPrivateKey(secretName, "", nil)),
- err: fmt.Errorf("privateKey.key cannot be empty"),
+ err: errors.New("privateKey.key cannot be empty"),
},
{
store: makeSecretStore(vaultOCID, region, withSecretAuth(userOCID, tenant), withPrivateKey(secretName, secretKey, nil), withFingerprint("", secretKey, nil)),
- err: fmt.Errorf("fingerprint.name cannot be empty"),
+ err: errors.New("fingerprint.name cannot be empty"),
},
{
store: makeSecretStore(vaultOCID, region, withSecretAuth(userOCID, tenant), withPrivateKey(secretName, secretKey, nil), withFingerprint(secretName, secretKey, &namespace)),
- err: fmt.Errorf("namespace not allowed with namespaced SecretStore"),
+ err: errors.New("namespace should either be empty or match the namespace of the SecretStore for a namespaced SecretStore"),
},
{
store: makeSecretStore(vaultOCID, region, withSecretAuth(userOCID, tenant), withPrivateKey(secretName, secretKey, nil), withFingerprint(secretName, "", nil)),
- err: fmt.Errorf("fingerprint.key cannot be empty"),
+ err: errors.New("fingerprint.key cannot be empty"),
},
{
store: makeSecretStore(vaultOCID, region),
@@ -320,7 +321,7 @@ func TestValidateStore(t *testing.T) {
}
}
-func TestVaultManagementService_NewClient(t *testing.T) {
+func TestVaultManagementServiceNewClient(t *testing.T) {
t.Parallel()
namespace := "default"
@@ -581,6 +582,7 @@ func TestOracleVaultGetAllSecrets(t *testing.T) {
func TestOracleVaultPushSecret(t *testing.T) {
testSecretKey := "test-secret-key"
+ encryptionKey := "must-not-be-blank-for-push"
var testCases = map[string]struct {
vms *VaultManagementService
data testingfake.PushSecretData
@@ -589,6 +591,7 @@ func TestOracleVaultPushSecret(t *testing.T) {
}{
"create a secret if not exists": {
&VaultManagementService{
+ encryptionKey: encryptionKey,
Client: &fakeoracle.OracleMockClient{
SecretBundles: map[string]secrets.SecretBundle{
s2id: s2bundle,
@@ -605,8 +608,28 @@ func TestOracleVaultPushSecret(t *testing.T) {
},
"created",
},
+ "create a json secret if not exists": {
+ &VaultManagementService{
+ encryptionKey: encryptionKey,
+ Client: &fakeoracle.OracleMockClient{
+ SecretBundles: map[string]secrets.SecretBundle{
+ s2id: s2bundle,
+ },
+ },
+ VaultClient: &fakeoracle.OracleMockVaultClient{},
+ },
+ testingfake.PushSecretData{
+ SecretKey: testSecretKey,
+ RemoteKey: s1id,
+ },
+ func(vms *VaultManagementService) bool {
+ return vms.VaultClient.(*fakeoracle.OracleMockVaultClient).CreatedCount == 1
+ },
+ "{'key-a':'secret-a', 'key-b': 'secret-b'}",
+ },
"update a secret if exists": {
&VaultManagementService{
+ encryptionKey: encryptionKey,
Client: &fakeoracle.OracleMockClient{
SecretBundles: map[string]secrets.SecretBundle{
s1id: s1bundle,
@@ -626,6 +649,7 @@ func TestOracleVaultPushSecret(t *testing.T) {
},
"neither create nor update if secret content is unchanged": {
&VaultManagementService{
+ encryptionKey: encryptionKey,
Client: &fakeoracle.OracleMockClient{
SecretBundles: map[string]secrets.SecretBundle{
s1id: s1bundle,
diff --git a/pkg/provider/passbolt/passbolt.go b/pkg/provider/passbolt/passbolt.go
index cb6418d79a2..293d33206d0 100644
--- a/pkg/provider/passbolt/passbolt.go
+++ b/pkg/provider/passbolt/passbolt.go
@@ -98,7 +98,7 @@ func (provider *ProviderPassbolt) NewClient(ctx context.Context, store esv1beta1
}
func (provider *ProviderPassbolt) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf(errNotImplemented)
+ return false, errors.New(errNotImplemented)
}
func (provider *ProviderPassbolt) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
@@ -119,11 +119,11 @@ func (provider *ProviderPassbolt) GetSecret(ctx context.Context, ref esv1beta1.E
}
func (provider *ProviderPassbolt) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
- return fmt.Errorf(errNotImplemented)
+ return errors.New(errNotImplemented)
}
func (provider *ProviderPassbolt) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
- return fmt.Errorf(errNotImplemented)
+ return errors.New(errNotImplemented)
}
func (provider *ProviderPassbolt) Validate() (esv1beta1.ValidationResult, error) {
@@ -131,7 +131,7 @@ func (provider *ProviderPassbolt) Validate() (esv1beta1.ValidationResult, error)
}
func (provider *ProviderPassbolt) GetSecretMap(_ context.Context, _ esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
- return nil, fmt.Errorf(errNotImplemented)
+ return nil, errors.New(errNotImplemented)
}
func (provider *ProviderPassbolt) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
diff --git a/pkg/provider/passbolt/passbolt_test.go b/pkg/provider/passbolt/passbolt_test.go
index e2461cf9c2c..86f1fb9e74d 100644
--- a/pkg/provider/passbolt/passbolt_test.go
+++ b/pkg/provider/passbolt/passbolt_test.go
@@ -17,7 +17,6 @@ package passbolt
import (
"context"
"errors"
- "fmt"
"strings"
"testing"
@@ -29,6 +28,13 @@ import (
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
)
+const (
+ someKey1 = "some-key1"
+ someKey2 = "some-key2"
+ someURI1 = "some-uri1"
+ someURI2 = "some-uri2"
+)
+
type PassboltClientMock struct {
}
@@ -43,8 +49,8 @@ func (p *PassboltClientMock) Logout(_ context.Context) error {
}
func (p *PassboltClientMock) GetResource(_ context.Context, resourceID string) (*api.Resource, error) {
resmap := map[string]api.Resource{
- "some-key1": {ID: "some-key1", Name: "some-name1", URI: "some-uri1"},
- "some-key2": {ID: "some-key2", Name: "some-name2", URI: "some-uri2"},
+ someKey1: {ID: someKey1, Name: "some-name1", URI: someURI1},
+ someKey2: {ID: someKey2, Name: "some-name2", URI: someURI2},
}
if res, ok := resmap[resourceID]; ok {
@@ -56,8 +62,8 @@ func (p *PassboltClientMock) GetResource(_ context.Context, resourceID string) (
func (p *PassboltClientMock) GetResources(_ context.Context, _ *api.GetResourcesOptions) ([]api.Resource, error) {
res := []api.Resource{
- {ID: "some-key1", Name: "some-name1", URI: "some-uri1"},
- {ID: "some-key2", Name: "some-name2", URI: "some-uri2"},
+ {ID: someKey1, Name: "some-name1", URI: someURI1},
+ {ID: someKey2, Name: "some-name2", URI: someURI2},
}
return res, nil
}
@@ -73,8 +79,8 @@ func (p *PassboltClientMock) DecryptMessage(message string) (string, error) {
func (p *PassboltClientMock) GetSecret(_ context.Context, resourceID string) (*api.Secret, error) {
resmap := map[string]api.Secret{
- "some-key1": {Data: `{"password": "some-password1", "description": "some-description1"}`},
- "some-key2": {Data: `{"password": "some-password2", "description": "some-description2"}`},
+ someKey1: {Data: `{"password": "some-password1", "description": "some-description1"}`},
+ someKey2: {Data: `{"password": "some-password2", "description": "some-description2"}`},
}
if res, ok := resmap[resourceID]; ok {
@@ -100,21 +106,21 @@ func TestValidateStore(t *testing.T) {
// missing auth
_, err := p.ValidateStore(store)
- g.Expect(err).To(g.BeEquivalentTo(fmt.Errorf(errPassboltStoreMissingAuth)))
+ g.Expect(err).To(g.BeEquivalentTo(errors.New(errPassboltStoreMissingAuth)))
// missing password
store.Spec.Provider.Passbolt.Auth = &esv1beta1.PassboltAuth{
PrivateKeySecretRef: &esmeta.SecretKeySelector{Key: "some-secret", Name: "privatekey"},
}
_, err = p.ValidateStore(store)
- g.Expect(err).To(g.BeEquivalentTo(fmt.Errorf(errPassboltStoreMissingAuthPassword)))
+ g.Expect(err).To(g.BeEquivalentTo(errors.New(errPassboltStoreMissingAuthPassword)))
// missing privateKey
store.Spec.Provider.Passbolt.Auth = &esv1beta1.PassboltAuth{
PasswordSecretRef: &esmeta.SecretKeySelector{Key: "some-secret", Name: "password"},
}
_, err = p.ValidateStore(store)
- g.Expect(err).To(g.BeEquivalentTo(fmt.Errorf(errPassboltStoreMissingAuthPrivateKey)))
+ g.Expect(err).To(g.BeEquivalentTo(errors.New(errPassboltStoreMissingAuthPrivateKey)))
store.Spec.Provider.Passbolt.Auth = &esv1beta1.PassboltAuth{
@@ -124,12 +130,12 @@ func TestValidateStore(t *testing.T) {
// missing host
_, err = p.ValidateStore(store)
- g.Expect(err).To(g.BeEquivalentTo(fmt.Errorf(errPassboltStoreMissingHost)))
+ g.Expect(err).To(g.BeEquivalentTo(errors.New(errPassboltStoreMissingHost)))
// not https
store.Spec.Provider.Passbolt.Host = "http://passbolt.test"
_, err = p.ValidateStore(store)
- g.Expect(err).To(g.BeEquivalentTo(fmt.Errorf(errPassboltStoreHostSchemeNotHTTPS)))
+ g.Expect(err).To(g.BeEquivalentTo(errors.New(errPassboltStoreHostSchemeNotHTTPS)))
// spec ok
store.Spec.Provider.Passbolt.Host = "https://passbolt.test"
@@ -168,8 +174,8 @@ func TestGetAllSecrets(t *testing.T) {
},
},
expected: map[string][]byte{
- "some-key1": []byte(`{"name":"some-name1","username":"","password":"some-password1","uri":"some-uri1","description":"some-description1"}`),
- "some-key2": []byte(`{"name":"some-name2","username":"","password":"some-password2","uri":"some-uri2","description":"some-description2"}`),
+ someKey1: []byte(`{"name":"some-name1","username":"","password":"some-password1","uri":"some-uri1","description":"some-description1"}`),
+ someKey2: []byte(`{"name":"some-name2","username":"","password":"some-password2","uri":"some-uri2","description":"some-description2"}`),
},
},
{
@@ -235,7 +241,7 @@ func TestGetSecret(t *testing.T) {
{
name: "get property from secret",
request: esv1beta1.ExternalSecretDataRemoteRef{
- Key: "some-key1",
+ Key: someKey1,
Property: "password",
},
expValue: "some-password1",
@@ -243,14 +249,14 @@ func TestGetSecret(t *testing.T) {
{
name: "get full secret",
request: esv1beta1.ExternalSecretDataRemoteRef{
- Key: "some-key1",
+ Key: someKey1,
},
expValue: `{"name":"some-name1","username":"","password":"some-password1","uri":"some-uri1","description":"some-description1"}`,
},
{
name: "return err when using invalid property",
request: esv1beta1.ExternalSecretDataRemoteRef{
- Key: "some-key1",
+ Key: someKey1,
Property: "invalid",
},
expErr: errPassboltSecretPropertyInvalid,
@@ -276,23 +282,23 @@ func TestSecretExists(t *testing.T) {
p := &ProviderPassbolt{client: clientMock}
g.RegisterTestingT(t)
_, err := p.SecretExists(context.TODO(), nil)
- g.Expect(err).To(g.BeEquivalentTo(fmt.Errorf(errNotImplemented)))
+ g.Expect(err).To(g.BeEquivalentTo(errors.New(errNotImplemented)))
}
func TestPushSecret(t *testing.T) {
p := &ProviderPassbolt{client: clientMock}
g.RegisterTestingT(t)
err := p.PushSecret(context.TODO(), nil, nil)
- g.Expect(err).To(g.BeEquivalentTo(fmt.Errorf(errNotImplemented)))
+ g.Expect(err).To(g.BeEquivalentTo(errors.New(errNotImplemented)))
}
func TestDeleteSecret(t *testing.T) {
p := &ProviderPassbolt{client: clientMock}
g.RegisterTestingT(t)
err := p.DeleteSecret(context.TODO(), nil)
- g.Expect(err).To(g.BeEquivalentTo(fmt.Errorf(errNotImplemented)))
+ g.Expect(err).To(g.BeEquivalentTo(errors.New(errNotImplemented)))
}
func TestGetSecretMap(t *testing.T) {
p := &ProviderPassbolt{client: clientMock}
g.RegisterTestingT(t)
_, err := p.GetSecretMap(context.TODO(), esv1beta1.ExternalSecretDataRemoteRef{})
- g.Expect(err).To(g.BeEquivalentTo(fmt.Errorf(errNotImplemented)))
+ g.Expect(err).To(g.BeEquivalentTo(errors.New(errNotImplemented)))
}
diff --git a/pkg/provider/passworddepot/passworddepot.go b/pkg/provider/passworddepot/passworddepot.go
index 7bcdcd9aef2..a8025b2aa4a 100644
--- a/pkg/provider/passworddepot/passworddepot.go
+++ b/pkg/provider/passworddepot/passworddepot.go
@@ -35,7 +35,7 @@ const (
errFetchSAKSecret = "couldn't find secret on cluster: %w"
errMissingSAK = "missing credentials while setting auth"
errUninitalizedPasswordDepotProvider = "provider passworddepot is not initialized"
- errJSONSecretUnmarshal = "unable to unmarshal secret: %w"
+ errNotImplemented = "%s not implemented"
)
type Client interface {
@@ -69,7 +69,7 @@ func (c *passwordDepotClient) getAuth(ctx context.Context) (string, string, erro
credentialsSecret := &corev1.Secret{}
credentialsSecretName := c.store.Auth.SecretRef.Credentials.Name
if credentialsSecretName == "" {
- return "", "", fmt.Errorf(errPasswordDepotCredSecretName)
+ return "", "", errors.New(errPasswordDepotCredSecretName)
}
objectKey := types.NamespacedName{
Name: credentialsSecretName,
@@ -78,7 +78,7 @@ func (c *passwordDepotClient) getAuth(ctx context.Context) (string, string, erro
// only ClusterStore is allowed to set namespace (and then it's required)
if c.storeKind == esv1beta1.ClusterSecretStoreKind {
if c.store.Auth.SecretRef.Credentials.Namespace == nil {
- return "", "", fmt.Errorf(errInvalidClusterStoreMissingSAKNamespace)
+ return "", "", errors.New(errInvalidClusterStoreMissingSAKNamespace)
}
objectKey.Namespace = *c.store.Auth.SecretRef.Credentials.Namespace
}
@@ -91,22 +91,17 @@ func (c *passwordDepotClient) getAuth(ctx context.Context) (string, string, erro
username := credentialsSecret.Data["username"]
password := credentialsSecret.Data["password"]
if (username == nil) || (len(username) == 0 || password == nil) || (len(password) == 0) {
- return "", "", fmt.Errorf(errMissingSAK)
+ return "", "", errors.New(errMissingSAK)
}
return string(username), string(password), nil
}
-// Function newPasswordDepotProvider returns a reference to a new instance of a 'PasswordDepot' struct.
-func NewPasswordDepotProvider() *PasswordDepot {
- return &PasswordDepot{}
-}
-
-// Method on PasswordDepot Provider to set up client with credentials and populate projectID.
+// NewClient Method on PasswordDepot Provider to set up client with credentials and populate projectID.
func (p *PasswordDepot) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) {
storeSpec := store.GetSpec()
if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.PasswordDepot == nil {
- return nil, fmt.Errorf("no store type or wrong store type")
+ return nil, errors.New("no store type or wrong store type")
}
storeSpecPasswordDepot := storeSpec.Provider.PasswordDepot
@@ -135,7 +130,7 @@ func (p *PasswordDepot) NewClient(ctx context.Context, store esv1beta1.GenericSt
}
func (p *PasswordDepot) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf("not implemented")
+ return false, fmt.Errorf(errNotImplemented, "SecretExists")
}
func (p *PasswordDepot) Validate() (esv1beta1.ValidationResult, error) {
@@ -143,20 +138,20 @@ func (p *PasswordDepot) Validate() (esv1beta1.ValidationResult, error) {
}
func (p *PasswordDepot) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
- return fmt.Errorf("not implemented")
+ return fmt.Errorf(errNotImplemented, "PushSecret")
}
func (p *PasswordDepot) GetAllSecrets(_ context.Context, _ esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
- return nil, fmt.Errorf("GetAllSecrets not implemented")
+ return nil, fmt.Errorf(errNotImplemented, "GetAllSecrets")
}
func (p *PasswordDepot) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
- return fmt.Errorf("not implemented")
+ return fmt.Errorf(errNotImplemented, "DeleteSecret")
}
func (p *PasswordDepot) GetSecret(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
if utils.IsNil(p.client) {
- return nil, fmt.Errorf(errUninitalizedPasswordDepotProvider)
+ return nil, errors.New(errUninitalizedPasswordDepotProvider)
}
data, err := p.client.GetSecret(p.database, ref.Key)
diff --git a/pkg/provider/previder/client_test.go b/pkg/provider/previder/client_test.go
new file mode 100644
index 00000000000..effee50e368
--- /dev/null
+++ b/pkg/provider/previder/client_test.go
@@ -0,0 +1,46 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package previder
+
+import (
+ "errors"
+
+ "github.com/previder/vault-cli/pkg"
+ "github.com/previder/vault-cli/pkg/model"
+)
+
+type PreviderVaultFakeClient struct {
+ pkg.PreviderVaultClient
+}
+
+var (
+ secrets = map[string]string{"secret1": "secret1content", "secret2": "secret2content"}
+)
+
+func (v *PreviderVaultFakeClient) DecryptSecret(id string) (*model.SecretDecrypt, error) {
+ for k, v := range secrets {
+ if k == id {
+ return &model.SecretDecrypt{Secret: v}, nil
+ }
+ }
+ return nil, errors.New("404 not found")
+}
+
+func (v *PreviderVaultFakeClient) GetSecrets() ([]model.Secret, error) {
+ secretList := make([]model.Secret, 0)
+ for k := range secrets {
+ secretList = append(secretList, model.Secret{Description: k})
+ }
+ return secretList, nil
+}
diff --git a/pkg/provider/previder/provider.go b/pkg/provider/previder/provider.go
new file mode 100644
index 00000000000..c9648932048
--- /dev/null
+++ b/pkg/provider/previder/provider.go
@@ -0,0 +1,136 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package previder
+
+import (
+ "context"
+ "errors"
+ "fmt"
+
+ previderclient "github.com/previder/vault-cli/pkg"
+ corev1 "k8s.io/api/core/v1"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ "github.com/external-secrets/external-secrets/pkg/utils/resolvers"
+)
+
+const (
+ errNotImplemented = "not implemented"
+)
+
+var _ esv1beta1.Provider = &SecretManager{}
+
+type SecretManager struct {
+ VaultClient previderclient.PreviderVaultClient
+}
+
+func init() {
+ esv1beta1.Register(&SecretManager{}, &esv1beta1.SecretStoreProvider{
+ Previder: &esv1beta1.PreviderProvider{},
+ })
+}
+
+func (s *SecretManager) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
+ if store == nil {
+ return nil, fmt.Errorf("secret store not found: %v", "nil store")
+ }
+ storeSpec := store.GetSpec().Provider.Previder
+
+ storeKind := store.GetObjectKind().GroupVersionKind().Kind
+ accessToken, err := resolvers.SecretKeyRef(ctx, kube, storeKind, namespace, &storeSpec.Auth.SecretRef.AccessToken)
+ if err != nil {
+ return nil, fmt.Errorf(accessToken, err)
+ }
+
+ s.VaultClient, err = previderclient.NewVaultClient(storeSpec.BaseURI, accessToken)
+
+ if err != nil {
+ return nil, err
+ }
+ return s, nil
+}
+
+func (s *SecretManager) ValidateStore(store esv1beta1.GenericStore) (admission.Warnings, error) {
+ storeSpec := store.GetSpec()
+ previderSpec := storeSpec.Provider.Previder
+ if previderSpec == nil {
+ return nil, errors.New("missing Previder spec")
+ }
+ if previderSpec.Auth.SecretRef == nil {
+ return nil, errors.New("missing Previder Auth SecretRef")
+ }
+ accessToken := previderSpec.Auth.SecretRef.AccessToken
+
+ if accessToken.Name == "" {
+ return nil, errors.New("missing Previder accessToken name")
+ }
+ if accessToken.Key == "" {
+ return nil, errors.New("missing Previder accessToken key")
+ }
+
+ return nil, nil
+}
+
+func (s *SecretManager) Capabilities() esv1beta1.SecretStoreCapabilities {
+ return esv1beta1.SecretStoreReadOnly
+}
+
+func (s *SecretManager) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
+ secret, err := s.VaultClient.DecryptSecret(ref.Key)
+ if err != nil {
+ return nil, err
+ }
+ return []byte(secret.Secret), nil
+}
+
+func (s *SecretManager) PushSecret(ctx context.Context, secret *corev1.Secret, data esv1beta1.PushSecretData) error {
+ return errors.New(errNotImplemented)
+}
+
+func (s *SecretManager) DeleteSecret(ctx context.Context, remoteRef esv1beta1.PushSecretRemoteRef) error {
+ return errors.New(errNotImplemented)
+}
+
+func (s *SecretManager) SecretExists(ctx context.Context, remoteRef esv1beta1.PushSecretRemoteRef) (bool, error) {
+ return false, errors.New(errNotImplemented)
+}
+
+func (s *SecretManager) Validate() (esv1beta1.ValidationResult, error) {
+ _, err := s.VaultClient.GetSecrets()
+ if err != nil {
+ return esv1beta1.ValidationResultError, err
+ }
+
+ return esv1beta1.ValidationResultReady, nil
+}
+
+func (s *SecretManager) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
+ secrets, err := s.GetSecret(ctx, ref)
+ if err != nil {
+ return nil, err
+ }
+ secretData := make(map[string][]byte)
+ secretData[ref.Key] = secrets
+ return secretData, nil
+}
+
+func (s *SecretManager) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
+ return nil, errors.New(errNotImplemented)
+}
+
+func (s *SecretManager) Close(ctx context.Context) error {
+ return nil
+}
diff --git a/pkg/provider/previder/provider_test.go b/pkg/provider/previder/provider_test.go
new file mode 100644
index 00000000000..be90049af5e
--- /dev/null
+++ b/pkg/provider/previder/provider_test.go
@@ -0,0 +1,160 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package previder
+
+import (
+ "context"
+ "testing"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ v1 "github.com/external-secrets/external-secrets/apis/meta/v1"
+)
+
+func TestSecretManagerCapabilities(t *testing.T) {
+ previderProvider := &SecretManager{}
+ if previderProvider.Capabilities() != esv1beta1.SecretStoreReadOnly {
+ t.Errorf("Store does not return correct value for capabilities")
+ }
+}
+
+func TestSecretManagerClose(t *testing.T) {
+ previderProvider := &SecretManager{}
+ ctx := context.Background()
+ if previderProvider.Close(ctx) != nil {
+ t.Errorf("Store close acts different than expected")
+ }
+}
+
+func TestSecretManagerGetAllSecrets(t *testing.T) {
+ previderProvider := &SecretManager{}
+ ctx := context.Background()
+ ref := esv1beta1.ExternalSecretFind{}
+ result, err := previderProvider.GetAllSecrets(ctx, ref)
+ if result != nil || err == nil {
+ t.Errorf("Store close acts different than expected")
+ }
+}
+
+func TestSecretManagerGetSecret(t *testing.T) {
+ previderProvider := &SecretManager{VaultClient: &PreviderVaultFakeClient{}}
+ ctx := context.Background()
+ ref := esv1beta1.ExternalSecretDataRemoteRef{Key: "secret1"}
+ returnedSecret, err := previderProvider.GetSecret(ctx, ref)
+ if err != nil {
+ t.Errorf("Secret not found")
+ }
+ if string(returnedSecret) != "secret1content" {
+ t.Errorf("Wrong secret returned")
+ }
+}
+
+func TestSecretManagerGetSecretNotExisting(t *testing.T) {
+ previderProvider := &SecretManager{VaultClient: &PreviderVaultFakeClient{}}
+ ctx := context.Background()
+ ref := esv1beta1.ExternalSecretDataRemoteRef{Key: "secret3"}
+ _, err := previderProvider.GetSecret(ctx, ref)
+ if err == nil {
+ t.Errorf("Secret found while non were expected")
+ }
+}
+
+func TestSecretManagerGetSecretMap(t *testing.T) {
+ previderProvider := &SecretManager{VaultClient: &PreviderVaultFakeClient{}}
+ ctx := context.Background()
+ key := "secret1"
+
+ ref := esv1beta1.ExternalSecretDataRemoteRef{Key: key}
+ returnedSecret, err := previderProvider.GetSecretMap(ctx, ref)
+ if err != nil {
+ t.Errorf("Secret not found")
+ }
+ if value, ok := returnedSecret[key]; !ok || string(value) != "secret1content" {
+ t.Errorf("Key not found or wrong secret returned")
+ }
+}
+
+func TestSecretManagerValidate(t *testing.T) {
+ previderProvider := &SecretManager{VaultClient: &PreviderVaultFakeClient{}}
+ validate, err := previderProvider.Validate()
+ if err != nil || validate != esv1beta1.ValidationResultReady {
+ t.Errorf("Could not validate")
+ }
+}
+
+func TestSecretManagerValidateStore(t *testing.T) {
+ previderProvider := &SecretManager{}
+ store := &esv1beta1.SecretStore{
+ Spec: esv1beta1.SecretStoreSpec{
+ Provider: &esv1beta1.SecretStoreProvider{
+ Previder: &esv1beta1.PreviderProvider{
+ Auth: esv1beta1.PreviderAuth{
+ SecretRef: &esv1beta1.PreviderAuthSecretRef{
+ AccessToken: v1.SecretKeySelector{
+ Name: "token",
+ Key: "key",
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ result, err := previderProvider.ValidateStore(store)
+ if result != nil || err != nil {
+ t.Errorf("Store Validation acts different than expected")
+ }
+
+ store = &esv1beta1.SecretStore{
+ Spec: esv1beta1.SecretStoreSpec{
+ Provider: &esv1beta1.SecretStoreProvider{
+ Previder: &esv1beta1.PreviderProvider{
+ Auth: esv1beta1.PreviderAuth{
+ SecretRef: &esv1beta1.PreviderAuthSecretRef{
+ AccessToken: v1.SecretKeySelector{
+ Name: "token",
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ result, err = previderProvider.ValidateStore(store)
+ if result != nil || err == nil {
+ t.Errorf("Store Validation key is not checked")
+ }
+
+ store = &esv1beta1.SecretStore{
+ Spec: esv1beta1.SecretStoreSpec{
+ Provider: &esv1beta1.SecretStoreProvider{
+ Previder: &esv1beta1.PreviderProvider{
+ Auth: esv1beta1.PreviderAuth{
+ SecretRef: &esv1beta1.PreviderAuthSecretRef{
+ AccessToken: v1.SecretKeySelector{
+ Key: "token",
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+
+ result, err = previderProvider.ValidateStore(store)
+ if result != nil || err == nil {
+ t.Errorf("Store Validation name is not checked")
+ }
+}
diff --git a/pkg/provider/pulumi/provider.go b/pkg/provider/pulumi/provider.go
index 32b098a09ef..bf75fb6ec18 100644
--- a/pkg/provider/pulumi/provider.go
+++ b/pkg/provider/pulumi/provider.go
@@ -19,7 +19,7 @@ import (
"errors"
"fmt"
- esc "github.com/pulumi/esc/cmd/esc/cli/client"
+ esc "github.com/pulumi/esc-sdk/sdk/go"
kclient "sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
@@ -39,6 +39,7 @@ const (
errNoStoreTypeOrWrongStoreType = "no store type or wrong store type"
errOrganizationIsRequired = "organization is required"
errEnvironmentIsRequired = "environment is required"
+ errProjectIsRequired = "project is required"
errSecretRefNameIsRequired = "secretRef.name is required"
errSecretRefKeyIsRequired = "secretRef.key is required"
)
@@ -52,14 +53,23 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore,
if storeKind == esv1beta1.ClusterSecretStoreKind && doesConfigDependOnNamespace(cfg) {
return nil, errors.New(errClusterStoreRequiresNamespace)
}
-
accessToken, err := loadAccessTokenSecret(ctx, cfg.AccessToken, kube, storeKind, namespace)
if err != nil {
return nil, err
}
- escClient := esc.New("external-secrets-operator", cfg.APIURL, accessToken, false)
+ configuration := esc.NewConfiguration()
+ configuration.UserAgent = "external-secrets-operator"
+ configuration.Servers = esc.ServerConfigurations{
+ esc.ServerConfiguration{
+ URL: cfg.APIURL,
+ },
+ }
+ authCtx := esc.NewAuthContext(accessToken)
+ escClient := esc.NewClient(configuration)
return &client{
- escClient: escClient,
+ escClient: *escClient,
+ authCtx: authCtx,
+ project: cfg.Project,
environment: cfg.Environment,
organization: cfg.Organization,
}, nil
@@ -91,7 +101,7 @@ func getConfig(store esv1beta1.GenericStore) (*esv1beta1.PulumiProvider, error)
cfg := spec.Provider.Pulumi
if cfg.APIURL == "" {
- cfg.APIURL = "https://api.pulumi.com"
+ cfg.APIURL = "https://api.pulumi.com/api/esc"
}
if cfg.Organization == "" {
@@ -100,6 +110,9 @@ func getConfig(store esv1beta1.GenericStore) (*esv1beta1.PulumiProvider, error)
if cfg.Environment == "" {
return nil, errors.New(errEnvironmentIsRequired)
}
+ if cfg.Project == "" {
+ return nil, errors.New(errProjectIsRequired)
+ }
err := validateStoreSecretRef(store, cfg.AccessToken)
if err != nil {
return nil, err
diff --git a/pkg/provider/pulumi/pulumi.go b/pkg/provider/pulumi/pulumi.go
index 33ec018d07c..15d9a3e709c 100644
--- a/pkg/provider/pulumi/pulumi.go
+++ b/pkg/provider/pulumi/pulumi.go
@@ -16,12 +16,12 @@ package pulumi
import (
"context"
- "encoding/json"
"errors"
"fmt"
- "time"
+ "strings"
- esc "github.com/pulumi/esc/cmd/esc/cli/client"
+ "dario.cat/mergo"
+ esc "github.com/pulumi/esc-sdk/sdk/go"
corev1 "k8s.io/api/core/v1"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
@@ -29,7 +29,9 @@ import (
)
type client struct {
- escClient esc.Client
+ escClient esc.EscClient
+ authCtx context.Context
+ project string
environment string
organization string
}
@@ -37,28 +39,78 @@ type client struct {
const (
errPushSecretsNotSupported = "pushing secrets is currently not supported by Pulumi"
errDeleteSecretsNotSupported = "deleting secrets is currently not supported by Pulumi"
- errGettingSecrets = "error getting secret %s: %w"
- errUnmarshalSecret = "unable to unmarshal secret: %w"
errUnableToGetValues = "unable to get value for key %s: %w"
errGettingAllSecretsNotSupported = "getting all secrets is currently not supported by Pulumi"
+ errReadEnvironment = "error reading environment : %w"
+ errPushSecrets = "error pushing secret: %w"
+ errInterfaceType = "interface{} is not of type map[string]interface{}"
+ errPushWholeSecret = "pushing the whole secret is not yet implemented"
)
var _ esv1beta1.SecretsClient = &client{}
-func (c *client) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
- x, _, err := c.escClient.OpenEnvironment(ctx, c.organization, c.environment, 5*time.Minute)
+func (c *client) GetSecret(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
+ env, err := c.escClient.OpenEnvironment(c.authCtx, c.organization, c.project, c.environment)
if err != nil {
return nil, err
}
- value, err := c.escClient.GetOpenProperty(ctx, c.organization, c.environment, x, ref.Key)
+ value, _, err := c.escClient.ReadEnvironmentProperty(c.authCtx, c.organization, c.project, c.environment, env.GetId(), ref.Key)
if err != nil {
return nil, err
}
- return utils.GetByteValue(value.ToJSON(false))
+ return utils.GetByteValue(value.GetValue())
}
-func (c *client) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
- return errors.New(errPushSecretsNotSupported)
+func createSubmaps(input map[string]interface{}) map[string]interface{} {
+ result := make(map[string]interface{})
+
+ for key, value := range input {
+ keys := strings.Split(key, ".")
+ current := result
+
+ for i, k := range keys {
+ if i == len(keys)-1 {
+ current[k] = value
+ } else {
+ if _, exists := current[k]; !exists {
+ current[k] = make(map[string]interface{})
+ }
+ current = current[k].(map[string]interface{})
+ }
+ }
+ }
+
+ return result
+}
+
+func (c *client) PushSecret(_ context.Context, secret *corev1.Secret, data esv1beta1.PushSecretData) error {
+ secretKey := data.GetSecretKey()
+ if secretKey == "" {
+ return errors.New(errPushWholeSecret)
+ }
+ value := secret.Data[secretKey]
+
+ updatePayload := &esc.EnvironmentDefinition{
+ Values: &esc.EnvironmentDefinitionValues{
+ AdditionalProperties: map[string]interface{}{
+ data.GetRemoteKey(): string(value),
+ },
+ },
+ }
+ _, oldValues, err := c.escClient.OpenAndReadEnvironment(c.authCtx, c.organization, c.project, c.environment)
+ if err != nil {
+ return fmt.Errorf(errReadEnvironment, err)
+ }
+ updatePayload.Values.AdditionalProperties = createSubmaps(updatePayload.Values.AdditionalProperties)
+ if err := mergo.Merge(&updatePayload.Values.AdditionalProperties, oldValues); err != nil {
+ return fmt.Errorf(errPushSecrets, err)
+ }
+ _, err = c.escClient.UpdateEnvironment(c.authCtx, c.organization, c.project, c.environment, updatePayload)
+ if err != nil {
+ return fmt.Errorf(errPushSecrets, err)
+ }
+
+ return nil
}
func (c *client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
@@ -73,21 +125,46 @@ func (c *client) Validate() (esv1beta1.ValidationResult, error) {
return esv1beta1.ValidationResultReady, nil
}
-func (c *client) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
- data, err := c.GetSecret(ctx, ref)
- if err != nil {
- return nil, fmt.Errorf(errGettingSecrets, ref.Key, err)
+func GetMapFromInterface(i interface{}) (map[string][]byte, error) {
+ // Assert the interface{} to map[string]interface{}
+ m, ok := i.(map[string]interface{})
+ if !ok {
+ return nil, errors.New(errInterfaceType)
}
- kv := make(map[string]any)
- err = json.Unmarshal(data, &kv)
- if err != nil {
- return nil, fmt.Errorf(errUnmarshalSecret, err)
+ // Create a new map to hold the result
+ result := make(map[string][]byte)
+
+ // Iterate over the map and convert each value to []byte
+ for key, value := range m {
+ result[key], _ = utils.GetByteValue(value)
}
+ return result, nil
+}
+
+func (c *client) GetSecretMap(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
+ env, err := c.escClient.OpenEnvironment(c.authCtx, c.organization, c.project, c.environment)
+ if err != nil {
+ return nil, err
+ }
+ value, _, err := c.escClient.ReadEnvironmentProperty(c.authCtx, c.organization, c.project, c.environment, env.GetId(), ref.Key)
+ if err != nil {
+ return nil, err
+ }
+ kv, _ := GetMapFromInterface(value.GetValue())
secretData := make(map[string][]byte)
for k, v := range kv {
- secretData[k], err = utils.GetByteValue(v)
+ byteValue, err := utils.GetByteValue(v)
+ if err != nil {
+ return nil, err
+ }
+ val := esc.Value{}
+ err = val.UnmarshalJSON(byteValue)
+ if err != nil {
+ return nil, err
+ }
+ secretData[k], err = utils.GetByteValue(val.Value)
if err != nil {
return nil, fmt.Errorf(errUnableToGetValues, k, err)
}
diff --git a/pkg/provider/pulumi/pulumi_test.go b/pkg/provider/pulumi/pulumi_test.go
index e1cbfd9de5f..e0a8b4fffc0 100644
--- a/pkg/provider/pulumi/pulumi_test.go
+++ b/pkg/provider/pulumi/pulumi_test.go
@@ -16,42 +16,69 @@ package pulumi
import (
"context"
"encoding/json"
+ "fmt"
"net/http"
"net/http/httptest"
"reflect"
"testing"
- esc2 "github.com/pulumi/esc"
- esc "github.com/pulumi/esc/cmd/esc/cli/client"
+ esc "github.com/pulumi/esc-sdk/sdk/go"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
)
-func newTestClient(t *testing.T, _, _ string, handler func(w http.ResponseWriter, r *http.Request)) *client {
- const userAgent = "test-user-agent"
+// Constants for content type and value.
+const contentTypeValue = "application/json"
+const contentType = "Content-Type"
+
+func newTestClient(t *testing.T, _, pattern string, handler func(w http.ResponseWriter, r *http.Request)) *client {
const token = "test-token"
- server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- require.Equal(t, "token "+token, r.Header.Get("Authorization"))
- handler(w, r)
- }))
- t.Cleanup(server.Close)
+ mux := http.NewServeMux()
+
+ mux.HandleFunc(pattern, handler)
+ mux.HandleFunc("/environments/foo/default/bar/open/", func(w http.ResponseWriter, r *http.Request) {
+ r.Header.Add(contentType, contentTypeValue)
+ w.Header().Add(contentType, contentTypeValue)
+ w.WriteHeader(http.StatusOK)
+ err := json.NewEncoder(w).Encode(map[string]interface{}{
+ "id": "session-id",
+ })
+ require.NoError(t, err)
+ })
+ server := httptest.NewServer(mux)
+ t.Cleanup(server.Close)
+ configuration := esc.NewConfiguration()
+ configuration.AddDefaultHeader("Authorization", "token "+token)
+ configuration.UserAgent = "external-secrets-operator"
+ configuration.Servers = esc.ServerConfigurations{
+ esc.ServerConfiguration{
+ URL: server.URL,
+ },
+ }
+ ctx := esc.NewAuthContext(token)
+ escClient := esc.NewClient(configuration)
return &client{
- escClient: esc.New(userAgent, server.URL, token, true),
+ escClient: *escClient,
+ authCtx: ctx,
organization: "foo",
environment: "bar",
+ project: "default",
}
}
func TestGetSecret(t *testing.T) {
- ctx := context.Background()
- expected := esc2.NewValue("world")
+ testmap := map[string]interface{}{
+ "b": "world",
+ }
- client := newTestClient(t, http.MethodGet, "/api/preview/environments/foo/bar/open/session", func(w http.ResponseWriter, r *http.Request) {
- err := json.NewEncoder(w).Encode(expected)
+ client := newTestClient(t, http.MethodGet, "/environments/foo/default/bar/open/session-id", func(w http.ResponseWriter, r *http.Request) {
+ r.Header.Add(contentType, contentTypeValue)
+ w.Header().Add(contentType, contentTypeValue)
+ err := json.NewEncoder(w).Encode(esc.NewValue(testmap, esc.Trace{}))
require.NoError(t, err)
})
@@ -64,12 +91,12 @@ func TestGetSecret(t *testing.T) {
ref: esv1beta1.ExternalSecretDataRemoteRef{
Key: "b",
},
- want: []byte(`world`),
+ want: []byte(`{"b":"world"}`),
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
- got, err := client.GetSecret(ctx, tc.ref)
+ got, err := client.GetSecret(context.TODO(), tc.ref)
if tc.err == nil {
assert.NoError(t, err)
assert.Equal(t, tc.want, got)
@@ -86,7 +113,7 @@ func TestGetSecretMap(t *testing.T) {
tests := []struct {
name string
ref esv1beta1.ExternalSecretDataRemoteRef
- input string
+ input map[string]interface{}
want map[string][]byte
wantErr bool
@@ -96,7 +123,62 @@ func TestGetSecretMap(t *testing.T) {
ref: esv1beta1.ExternalSecretDataRemoteRef{
Key: "mysec",
},
- input: `{"foo": "bar", "foobar": 42, "bar": true}`,
+ input: map[string]interface{}{
+ "foo": map[string]interface{}{
+ "value": "bar",
+ "trace": map[string]interface{}{
+ "def": map[string]interface{}{
+ "environment": "bar",
+ "begin": map[string]interface{}{
+ "line": 3,
+ "column": 9,
+ "byte": 29,
+ },
+ "end": map[string]interface{}{
+ "line": 3,
+ "column": 13,
+ "byte": 33,
+ },
+ },
+ },
+ },
+ "foobar": map[string]interface{}{
+ "value": "42",
+ "trace": map[string]interface{}{
+ "def": map[string]interface{}{
+ "environment": "bar",
+ "begin": map[string]interface{}{
+ "line": 4,
+ "column": 9,
+ "byte": 38,
+ },
+ "end": map[string]interface{}{
+ "line": 4,
+ "column": 13,
+ "byte": 42,
+ },
+ },
+ },
+ },
+ "bar": map[string]interface{}{
+ "value": true,
+ "trace": map[string]interface{}{
+ "def": map[string]interface{}{
+ "environment": "bar",
+ "begin": map[string]interface{}{
+ "line": 5,
+ "column": 9,
+ "byte": 47,
+ },
+ "end": map[string]interface{}{
+ "line": 5,
+ "column": 13,
+ "byte": 51,
+ },
+ },
+ },
+ },
+ },
want: map[string][]byte{
"foo": []byte("bar"),
"foobar": []byte("42"),
@@ -109,10 +191,85 @@ func TestGetSecretMap(t *testing.T) {
ref: esv1beta1.ExternalSecretDataRemoteRef{
Key: "mysec",
},
- input: `{"foo": {"foobar": 42}, "bar": {"foo": "bar"}}`,
+ input: map[string]interface{}{
+ "test22": map[string]interface{}{
+ "value": map[string]interface{}{
+ "my": map[string]interface{}{
+ "value": "hello",
+ "trace": map[string]interface{}{
+ "def": map[string]interface{}{
+ "environment": "bar",
+ "begin": map[string]interface{}{
+ "line": 6,
+ "column": 11,
+ "byte": 72,
+ },
+ "end": map[string]interface{}{
+ "line": 6,
+ "column": 16,
+ "byte": 77,
+ },
+ },
+ },
+ },
+ },
+ "trace": map[string]interface{}{
+ "def": map[string]interface{}{
+ "environment": "bar",
+ "begin": map[string]interface{}{
+ "line": 6,
+ "column": 7,
+ "byte": 68,
+ },
+ "end": map[string]interface{}{
+ "line": 6,
+ "column": 16,
+ "byte": 77,
+ },
+ },
+ },
+ },
+ "test33": map[string]interface{}{
+ "value": map[string]interface{}{
+ "world": map[string]interface{}{
+ "value": "hello",
+ "trace": map[string]interface{}{
+ "def": map[string]interface{}{
+ "environment": "bar",
+ "begin": map[string]interface{}{
+ "line": 8,
+ "column": 14,
+ "byte": 103,
+ },
+ "end": map[string]interface{}{
+ "line": 8,
+ "column": 19,
+ "byte": 108,
+ },
+ },
+ },
+ },
+ },
+ "trace": map[string]interface{}{
+ "def": map[string]interface{}{
+ "environment": "bar",
+ "begin": map[string]interface{}{
+ "line": 8,
+ "column": 7,
+ "byte": 96,
+ },
+ "end": map[string]interface{}{
+ "line": 8,
+ "column": 19,
+ "byte": 108,
+ },
+ },
+ },
+ },
+ },
want: map[string][]byte{
- "foo": []byte(`{"foobar":42}`),
- "bar": []byte(`{"foo":"bar"}`),
+ "test22": []byte(`{"my":{"trace":{"def":{"begin":{"byte":72,"column":11,"line":6},"end":{"byte":77,"column":16,"line":6},"environment":"bar"}},"value":"hello"}}`),
+ "test33": []byte(`{"world":{"trace":{"def":{"begin":{"byte":103,"column":14,"line":8},"end":{"byte":108,"column":19,"line":8},"environment":"bar"}},"value":"hello"}}`),
},
wantErr: false,
},
@@ -121,23 +278,80 @@ func TestGetSecretMap(t *testing.T) {
ref: esv1beta1.ExternalSecretDataRemoteRef{
Key: "mysec",
},
- input: `{"foo": "bar", "bar": {"foo": {"bar": false}}}`,
+ input: map[string]interface{}{
+ "foo": map[string]interface{}{
+ "value": "bar",
+ "trace": map[string]interface{}{
+ "def": map[string]interface{}{
+ "environment": "bar",
+ "begin": map[string]interface{}{
+ "line": 3,
+ "column": 9,
+ "byte": 29,
+ },
+ "end": map[string]interface{}{
+ "line": 3,
+ "column": 13,
+ "byte": 33,
+ },
+ },
+ },
+ },
+ "test22": map[string]interface{}{
+ "value": map[string]interface{}{
+ "my": map[string]interface{}{
+ "value": "hello",
+ "trace": map[string]interface{}{
+ "def": map[string]interface{}{
+ "environment": "bar",
+ "begin": map[string]interface{}{
+ "line": 6,
+ "column": 11,
+ "byte": 72,
+ },
+ "end": map[string]interface{}{
+ "line": 6,
+ "column": 16,
+ "byte": 77,
+ },
+ },
+ },
+ },
+ },
+ "trace": map[string]interface{}{
+ "def": map[string]interface{}{
+ "environment": "bar",
+ "begin": map[string]interface{}{
+ "line": 6,
+ "column": 7,
+ "byte": 68,
+ },
+ "end": map[string]interface{}{
+ "line": 6,
+ "column": 16,
+ "byte": 77,
+ },
+ },
+ },
+ },
+ },
want: map[string][]byte{
- "foo": []byte(`bar`),
- "bar": []byte(`{"foo":{"bar":false}}`),
+ "foo": []byte("bar"),
+ "test22": []byte(`{"my":{"trace":{"def":{"begin":{"byte":72,"column":11,"line":6},"end":{"byte":77,"column":16,"line":6},"environment":"bar"}},"value":"hello"}}`),
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
- p := newTestClient(t, http.MethodGet, "/api/preview/environments/foo/bar/open/session", func(w http.ResponseWriter, r *http.Request) {
- esc2Input, err1 := esc2.FromJSON(tt.input, false)
- require.NoError(t, err1)
- err2 := json.NewEncoder(w).Encode(esc2Input)
+ p := newTestClient(t, http.MethodGet, "/environments/foo/default/bar/open/session-id", func(w http.ResponseWriter, r *http.Request) {
+ r.Header.Add(contentType, contentTypeValue)
+ w.Header().Add(contentType, contentTypeValue)
+ err2 := json.NewEncoder(w).Encode(esc.NewValue(tt.input, esc.Trace{}))
require.NoError(t, err2)
})
- got, err := p.GetSecretMap(context.Background(), tt.ref)
+ got, err := p.GetSecretMap(context.TODO(), tt.ref)
+ fmt.Print(got)
if (err != nil) != tt.wantErr {
t.Errorf("ProviderPulumi.GetSecretMap() error = %v, wantErr %v", err, tt.wantErr)
return
@@ -148,3 +362,51 @@ func TestGetSecretMap(t *testing.T) {
})
}
}
+
+func TestCreateSubmaps(t *testing.T) {
+ input := map[string]interface{}{
+ "a.b.c": 1,
+ "a.b.d": 2,
+ "a.e": 3,
+ "f": 4,
+ }
+
+ expected := map[string]interface{}{
+ "a": map[string]interface{}{
+ "b": map[string]interface{}{
+ "c": 1,
+ "d": 2,
+ },
+ "e": 3,
+ },
+ "f": 4,
+ }
+
+ result := createSubmaps(input)
+
+ if !reflect.DeepEqual(result, expected) {
+ t.Errorf("createSubmaps() = %v, want %v", result, expected)
+ }
+
+ // Test nested access
+ a, ok := result["a"].(map[string]interface{})
+ if !ok {
+ t.Errorf("Expected 'a' to be a map")
+ }
+
+ b, ok := a["b"].(map[string]interface{})
+ if !ok {
+ t.Errorf("Expected 'a.b' to be a map")
+ }
+
+ c, ok := b["c"].(int)
+ if !ok || c != 1 {
+ t.Errorf("Expected 'a.b.c' to be 1, got %v", b["c"])
+ }
+
+ // Test non-nested key
+ f, ok := result["f"].(int)
+ if !ok || f != 4 {
+ t.Errorf("Expected 'f' to be 4, got %v", result["f"])
+ }
+}
diff --git a/pkg/provider/register/register.go b/pkg/provider/register/register.go
index 80e0e4019df..34a4eeb134d 100644
--- a/pkg/provider/register/register.go
+++ b/pkg/provider/register/register.go
@@ -21,15 +21,19 @@ import (
_ "github.com/external-secrets/external-secrets/pkg/provider/alibaba"
_ "github.com/external-secrets/external-secrets/pkg/provider/aws"
_ "github.com/external-secrets/external-secrets/pkg/provider/azure/keyvault"
+ _ "github.com/external-secrets/external-secrets/pkg/provider/beyondtrust"
+ _ "github.com/external-secrets/external-secrets/pkg/provider/bitwarden"
_ "github.com/external-secrets/external-secrets/pkg/provider/chef"
_ "github.com/external-secrets/external-secrets/pkg/provider/conjur"
_ "github.com/external-secrets/external-secrets/pkg/provider/delinea"
+ _ "github.com/external-secrets/external-secrets/pkg/provider/device42"
_ "github.com/external-secrets/external-secrets/pkg/provider/doppler"
_ "github.com/external-secrets/external-secrets/pkg/provider/fake"
_ "github.com/external-secrets/external-secrets/pkg/provider/fortanix"
_ "github.com/external-secrets/external-secrets/pkg/provider/gcp/secretmanager"
_ "github.com/external-secrets/external-secrets/pkg/provider/gitlab"
_ "github.com/external-secrets/external-secrets/pkg/provider/ibm"
+ _ "github.com/external-secrets/external-secrets/pkg/provider/infisical"
_ "github.com/external-secrets/external-secrets/pkg/provider/keepersecurity"
_ "github.com/external-secrets/external-secrets/pkg/provider/kubernetes"
_ "github.com/external-secrets/external-secrets/pkg/provider/onboardbase"
@@ -37,8 +41,10 @@ import (
_ "github.com/external-secrets/external-secrets/pkg/provider/oracle"
_ "github.com/external-secrets/external-secrets/pkg/provider/passbolt"
_ "github.com/external-secrets/external-secrets/pkg/provider/passworddepot"
+ _ "github.com/external-secrets/external-secrets/pkg/provider/previder"
_ "github.com/external-secrets/external-secrets/pkg/provider/pulumi"
_ "github.com/external-secrets/external-secrets/pkg/provider/scaleway"
+ _ "github.com/external-secrets/external-secrets/pkg/provider/secretserver"
_ "github.com/external-secrets/external-secrets/pkg/provider/senhasegura"
_ "github.com/external-secrets/external-secrets/pkg/provider/vault"
_ "github.com/external-secrets/external-secrets/pkg/provider/webhook"
diff --git a/pkg/provider/scaleway/client.go b/pkg/provider/scaleway/client.go
index e8a8b52b346..312949bb726 100644
--- a/pkg/provider/scaleway/client.go
+++ b/pkg/provider/scaleway/client.go
@@ -59,7 +59,7 @@ func (r scwSecretRef) String() string {
func decodeScwSecretRef(key string) (*scwSecretRef, error) {
sepIndex := strings.IndexRune(key, ':')
if sepIndex < 0 {
- return nil, fmt.Errorf("invalid secret reference: missing colon ':'")
+ return nil, errors.New("invalid secret reference: missing colon ':'")
}
return &scwSecretRef{
@@ -104,7 +104,7 @@ func (c *client) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretData
func (c *client) PushSecret(ctx context.Context, secret *corev1.Secret, data esv1beta1.PushSecretData) error {
if data.GetSecretKey() == "" {
- return fmt.Errorf("pushing the whole secret is not yet implemented")
+ return errors.New("pushing the whole secret is not yet implemented")
}
value := secret.Data[data.GetSecretKey()]
@@ -128,14 +128,14 @@ func (c *client) PushSecret(ctx context.Context, secret *corev1.Secret, data esv
case refTypePath:
name, path, ok := splitNameAndPath(scwRef.Value)
if !ok {
- return fmt.Errorf("ref is not a path")
+ return errors.New("ref is not a path")
}
listSecretReq.Name = &name
listSecretReq.Path = &path
secretName = name
secretPath = path
default:
- return fmt.Errorf("secrets can only be pushed by name or path")
+ return errors.New("secrets can only be pushed by name or path")
}
var secretID string
@@ -234,13 +234,13 @@ func (c *client) DeleteSecret(ctx context.Context, remoteRef esv1beta1.PushSecre
case refTypePath:
name, path, ok := splitNameAndPath(scwRef.Value)
if !ok {
- return fmt.Errorf("ref is not a path")
+ return errors.New("ref is not a path")
}
listSecretReq.Name = &name
listSecretReq.Path = &path
default:
- return fmt.Errorf("secrets can only be deleted by name or path")
+ return errors.New("secrets can only be deleted by name or path")
}
listSecrets, err := c.api.ListSecrets(listSecretReq, scw.WithContext(ctx))
@@ -265,7 +265,7 @@ func (c *client) DeleteSecret(ctx context.Context, remoteRef esv1beta1.PushSecre
}
func (c *client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf("not implemented")
+ return false, errors.New("not implemented")
}
func (c *client) Validate() (esv1beta1.ValidationResult, error) {
@@ -339,7 +339,7 @@ func (c *client) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecret
return nil, err
}
- totalFetched := uint64(*request.Page-1)*uint64(*request.PageSize) + uint64(len(response.Secrets))
+ totalFetched := c.safeConvertInt32(request.Page)*uint64(*request.PageSize) + uint64(len(response.Secrets))
done = totalFetched == response.TotalCount
*request.Page++
@@ -368,6 +368,14 @@ func (c *client) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecret
return results, nil
}
+func (c *client) safeConvertInt32(page *int32) uint64 {
+ if *page-1 < 0 {
+ return 0
+ }
+
+ return uint64(*page - 1) //nolint:gosec // already checked above
+}
+
func (c *client) Close(context.Context) error {
return nil
}
@@ -408,7 +416,7 @@ func (c *client) accessSecretVersion(ctx context.Context, secretRef *scwSecretRe
case refTypePath:
name, path, ok := splitNameAndPath(secretRef.Value)
if !ok {
- return nil, fmt.Errorf("ref is not a path")
+ return nil, errors.New("ref is not a path")
}
request.Name = &name
diff --git a/pkg/provider/scaleway/provider.go b/pkg/provider/scaleway/provider.go
index f57df3a088f..5f8ef8bba3f 100644
--- a/pkg/provider/scaleway/provider.go
+++ b/pkg/provider/scaleway/provider.go
@@ -16,6 +16,7 @@ package scaleway
import (
"context"
+ "errors"
"fmt"
smapi "github.com/scaleway/scaleway-sdk-go/api/secret/v1beta1"
@@ -50,7 +51,7 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore,
if store.GetKind() == esv1beta1.ClusterSecretStoreKind && doesConfigDependOnNamespace(cfg) {
// we are not attached to a specific namespace, but some config values are dependent on it
- return nil, fmt.Errorf("when using a ClusterSecretStore, namespaces must be explicitly set")
+ return nil, errors.New("when using a ClusterSecretStore, namespaces must be explicitly set")
}
accessKey, err := loadConfigSecret(ctx, cfg.AccessKey, kube, namespace, store.GetKind())
@@ -97,14 +98,14 @@ func loadConfigSecret(ctx context.Context, ref *esv1beta1.ScalewayProviderSecret
func validateSecretRef(store esv1beta1.GenericStore, ref *esv1beta1.ScalewayProviderSecretRef) error {
if ref.SecretRef != nil {
if ref.Value != "" {
- return fmt.Errorf("cannot specify both secret reference and value")
+ return errors.New("cannot specify both secret reference and value")
}
err := utils.ValidateReferentSecretSelector(store, *ref.SecretRef)
if err != nil {
return err
}
} else if ref.Value == "" {
- return fmt.Errorf("must specify either secret reference or direct value")
+ return errors.New("must specify either secret reference or direct value")
}
return nil
@@ -124,12 +125,12 @@ func doesConfigDependOnNamespace(cfg *esv1beta1.ScalewayProvider) bool {
func getConfig(store esv1beta1.GenericStore) (*esv1beta1.ScalewayProvider, error) {
if store == nil {
- return nil, fmt.Errorf("missing store specification")
+ return nil, errors.New("missing store specification")
}
storeSpec := store.GetSpec()
if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.Scaleway == nil {
- return nil, fmt.Errorf("invalid specification for scaleway provider")
+ return nil, errors.New("invalid specification for scaleway provider")
}
cfg := storeSpec.Provider.Scaleway
diff --git a/pkg/provider/secretserver/client.go b/pkg/provider/secretserver/client.go
new file mode 100644
index 00000000000..49398675ec9
--- /dev/null
+++ b/pkg/provider/secretserver/client.go
@@ -0,0 +1,166 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package secretserver
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "strconv"
+
+ "github.com/DelineaXPM/tss-sdk-go/v2/server"
+ "github.com/tidwall/gjson"
+ corev1 "k8s.io/api/core/v1"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ "github.com/external-secrets/external-secrets/pkg/utils"
+)
+
+type client struct {
+ api secretAPI
+}
+
+var _ esv1beta1.SecretsClient = &client{}
+
+// GetSecret supports two types:
+// 1. Get the secrets using the secret ID in ref.key i.e. key: 53974
+// 2. Get the secret using the secret "name" i.e. key: "secretNameHere"
+// - Secret names must not contain spaces.
+// - If using the secret "name" and multiple secrets are found ...
+// the first secret in the array will be the secret returned.
+// 3. get the full secret as json-encoded value
+// by leaving the ref.Property empty.
+// 4. get a specific value by using a key from the json formatted secret in Items.0.ItemValue.
+// Nested values are supported by specifying a gjson expression
+func (c *client) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
+ secret, err := c.getSecret(ctx, ref)
+ if err != nil {
+ return nil, err
+ }
+ // Return nil if secret contains no fields
+ if secret.Fields == nil {
+ return nil, nil
+ }
+ jsonStr, err := json.Marshal(secret)
+ if err != nil {
+ return nil, err
+ }
+ // If no property is defined return the full secret as raw json
+ if ref.Property == "" {
+ return jsonStr, nil
+ }
+
+ // Keep original behavior of decoding first Item into gjson
+ if len(secret.Fields) == 1 {
+ // extract first "field" i.e. Items.0.ItemValue, data from secret using gjson
+ val := gjson.Get(string(jsonStr), "Items.0.ItemValue")
+ if !val.Exists() {
+ return nil, esv1beta1.NoSecretError{}
+ }
+ // extract specific value from data directly above using gjson
+ out := gjson.Get(val.String(), ref.Property)
+ if !out.Exists() {
+ return nil, esv1beta1.NoSecretError{}
+ }
+ return []byte(out.String()), nil
+ } else {
+ // More general case Fields is an array in DelineaXPM/tss-sdk-go/v2/server
+ // https://github.com/DelineaXPM/tss-sdk-go/blob/571e5674a8103031ad6f873453db27959ec1ca67/server/secret.go#L23
+ secretMap := make(map[string]string)
+
+ for index := range secret.Fields {
+ secretMap[secret.Fields[index].FieldName] = secret.Fields[index].ItemValue
+ secretMap[secret.Fields[index].Slug] = secret.Fields[index].ItemValue
+ }
+
+ out, ok := secretMap[ref.Property]
+ if !ok {
+ return nil, esv1beta1.NoSecretError{}
+ }
+
+ return []byte(out), nil
+ }
+}
+
+// Not supported at this time.
+func (c *client) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
+ return errors.New("pushing secrets is not supported by Secret Server at this time")
+}
+
+// Not supported at this time.
+func (c *client) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
+ return errors.New("deleting secrets is not supported by Secret Server at this time")
+}
+
+// Not supported at this time.
+func (c *client) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
+ return false, errors.New("not implemented")
+}
+
+// Not supported at this time.
+func (c *client) Validate() (esv1beta1.ValidationResult, error) {
+ return esv1beta1.ValidationResultReady, nil
+}
+
+func (c *client) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
+ secret, err := c.getSecret(ctx, ref)
+ if err != nil {
+ return nil, err
+ }
+ secretData := make(map[string]any)
+
+ err = json.Unmarshal([]byte(secret.Fields[0].ItemValue), &secretData)
+ if err != nil {
+ return nil, err
+ }
+
+ data := make(map[string][]byte)
+ for k, v := range secretData {
+ data[k], err = utils.GetByteValue(v)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return data, nil
+}
+
+// Not supported at this time.
+func (c *client) GetAllSecrets(_ context.Context, _ esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
+ return nil, errors.New("getting all secrets is not supported by Delinea Secret Server at this time")
+}
+
+func (c *client) Close(context.Context) error {
+ return nil
+}
+
+// getSecret retrieves the secret referenced by ref from the Vault API.
+func (c *client) getSecret(_ context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (*server.Secret, error) {
+ if ref.Version != "" {
+ return nil, errors.New("specifying a version is not supported")
+ }
+ id, err := strconv.Atoi(ref.Key)
+ if err != nil {
+ s, err := c.api.Secrets(ref.Key, "Name")
+ if err != nil {
+ return nil, err
+ }
+ if len(s) == 0 {
+ return nil, errors.New("unable to retrieve secret at this time")
+ }
+
+ return &s[0], nil
+ }
+ return c.api.Secret(id)
+}
diff --git a/pkg/provider/secretserver/client_test.go b/pkg/provider/secretserver/client_test.go
new file mode 100644
index 00000000000..026136c36d1
--- /dev/null
+++ b/pkg/provider/secretserver/client_test.go
@@ -0,0 +1,214 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package secretserver
+
+import (
+ "context"
+ "encoding/json"
+ "errors"
+ "io"
+ "os"
+ "testing"
+
+ "github.com/DelineaXPM/tss-sdk-go/v2/server"
+ "github.com/stretchr/testify/assert"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+)
+
+var (
+ errNotFound = errors.New("not found")
+)
+
+type fakeAPI struct {
+ secrets []*server.Secret
+}
+
+func (f *fakeAPI) Secret(id int) (*server.Secret, error) {
+ for _, s := range f.secrets {
+ if s.ID == id {
+ return s, nil
+ }
+ }
+ return nil, errNotFound
+}
+
+func (f *fakeAPI) Secrets(searchText, _ string) ([]server.Secret, error) {
+ secret := make([]server.Secret, 1)
+ for _, s := range f.secrets {
+ if s.Name == searchText {
+ secret[0] = *s
+ return secret, nil
+ }
+ }
+ return nil, errNotFound
+}
+
+// createSecret assembles a server.Secret from file test_data.json.
+func createSecret(id int, itemValue string) *server.Secret {
+ s, _ := getJSONData()
+ s.ID = id
+ s.Fields[0].ItemValue = itemValue
+ return s
+}
+
+func getJSONData() (*server.Secret, error) {
+ var s = &server.Secret{}
+ jsonFile, err := os.Open("test_data.json")
+ if err != nil {
+ return nil, err
+ }
+ defer jsonFile.Close()
+
+ byteValue, _ := io.ReadAll(jsonFile)
+ err = json.Unmarshal(byteValue, &s)
+ if err != nil {
+ return nil, err
+ }
+ return s, nil
+}
+
+func createTestSecretFromCode(id int) *server.Secret {
+ s := new(server.Secret)
+ s.ID = id
+ s.Name = "Secretname"
+ s.Fields = make([]server.SecretField, 2)
+ s.Fields[0].ItemValue = "usernamevalue"
+ s.Fields[0].FieldName = "Username"
+ s.Fields[0].Slug = "username"
+ s.Fields[1].FieldName = "Password"
+ s.Fields[1].Slug = "password"
+ s.Fields[1].ItemValue = "passwordvalue"
+ return s
+}
+
+func newTestClient() esv1beta1.SecretsClient {
+ return &client{
+ api: &fakeAPI{
+ secrets: []*server.Secret{
+ createSecret(1000, "{ \"user\": \"robertOppenheimer\", \"password\": \"badPassword\",\"server\":\"192.168.1.50\"}"),
+ createSecret(2000, "{ \"user\": \"helloWorld\", \"password\": \"badPassword\",\"server\":[ \"192.168.1.50\",\"192.168.1.51\"] }"),
+ createSecret(3000, "{ \"user\": \"chuckTesta\", \"password\": \"badPassword\",\"server\":\"192.168.1.50\"}"),
+ createTestSecretFromCode(4000),
+ },
+ },
+ }
+}
+
+func TestGetSecretSecretServer(t *testing.T) {
+ ctx := context.Background()
+ c := newTestClient()
+ s, _ := getJSONData()
+ jsonStr, _ := json.Marshal(s)
+ jsonStr2, _ := json.Marshal(createTestSecretFromCode(4000))
+
+ testCases := map[string]struct {
+ ref esv1beta1.ExternalSecretDataRemoteRef
+ want []byte
+ err error
+ }{
+ "incorrect key returns nil and error": {
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: "0",
+ },
+ want: []byte(nil),
+ err: errNotFound,
+ },
+ "key = 'secret name' and user property returns a single value": {
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: "ESO-test-secret",
+ Property: "user",
+ },
+ want: []byte(`robertOppenheimer`),
+ },
+ "Secret from JSON: key and password property returns a single value": {
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: "1000",
+ Property: "password",
+ },
+ want: []byte(`badPassword`),
+ },
+ "Secret from JSON: key and nested property returns a single value": {
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: "2000",
+ Property: "server.1",
+ },
+ want: []byte(`192.168.1.51`),
+ },
+ "Secret from JSON: existent key with non-existing propery": {
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: "3000",
+ Property: "foo.bar",
+ },
+ err: esv1beta1.NoSecretError{},
+ },
+ "Secret from JSON: existent 'name' key with no propery": {
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: "1000",
+ },
+ want: jsonStr,
+ },
+ "Secret from code: existent key with no property": {
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: "4000",
+ },
+ want: jsonStr2,
+ },
+ "Secret from code: key and username fieldnamereturns a single value": {
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: "4000",
+ Property: "Username",
+ },
+ want: []byte(`usernamevalue`),
+ },
+ "Secret from code: 'name' and password slug returns a single value": {
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: "Secretname",
+ Property: "password",
+ },
+ want: []byte(`passwordvalue`),
+ },
+ "Secret from code: 'name' not found and password slug returns error": {
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: "Secretnameerror",
+ Property: "password",
+ },
+ want: []byte(nil),
+ err: errNotFound,
+ },
+ "Secret from code: 'name' found and non-existent attribute slug returns noSecretError": {
+ ref: esv1beta1.ExternalSecretDataRemoteRef{
+ Key: "Secretname",
+ Property: "passwordkey",
+ },
+ want: []byte(nil),
+ err: esv1beta1.NoSecretError{},
+ },
+ }
+
+ for name, tc := range testCases {
+ t.Run(name, func(t *testing.T) {
+ got, err := c.GetSecret(ctx, tc.ref)
+
+ if tc.err == nil {
+ assert.NoError(t, err)
+ assert.Equal(t, tc.want, got)
+ } else {
+ assert.Nil(t, got)
+ assert.ErrorIs(t, err, tc.err)
+ assert.Equal(t, tc.err, err)
+ }
+ })
+ }
+}
diff --git a/pkg/provider/secretserver/provider.go b/pkg/provider/secretserver/provider.go
new file mode 100644
index 00000000000..e4470131606
--- /dev/null
+++ b/pkg/provider/secretserver/provider.go
@@ -0,0 +1,179 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package secretserver
+
+import (
+ "context"
+ "errors"
+
+ "github.com/DelineaXPM/tss-sdk-go/v2/server"
+ kubeClient "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ "github.com/external-secrets/external-secrets/pkg/utils"
+ "github.com/external-secrets/external-secrets/pkg/utils/resolvers"
+)
+
+var (
+ errEmptyUserName = errors.New("username must not be empty")
+ errEmptyPassword = errors.New("password must be set")
+ errEmptyServerURL = errors.New("serverURL must be set")
+ errSecretRefAndValueConflict = errors.New("cannot specify both secret reference and value")
+ errSecretRefAndValueMissing = errors.New("must specify either secret reference or direct value")
+ errMissingStore = errors.New("missing store specification")
+ errInvalidSpec = errors.New("invalid specification for secret server provider")
+ errClusterStoreRequiresNamespace = errors.New("when using a ClusterSecretStore, namespaces must be explicitly set")
+ errMissingSecretName = errors.New("must specify a secret name")
+
+ errMissingSecretKey = errors.New("must specify a secret key")
+)
+
+type Provider struct{}
+
+var _ esv1beta1.Provider = &Provider{}
+
+// Capabilities return the provider supported capabilities (ReadOnly, WriteOnly, ReadWrite).
+func (p *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
+ return esv1beta1.SecretStoreReadOnly
+}
+
+func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kubeClient.Client, namespace string) (esv1beta1.SecretsClient, error) {
+ cfg, err := getConfig(store)
+ if err != nil {
+ return nil, err
+ }
+ if store.GetKind() == esv1beta1.ClusterSecretStoreKind && doesConfigDependOnNamespace(cfg) {
+ // we are not attached to a specific namespace, but some config values are dependent on it
+ return nil, errClusterStoreRequiresNamespace
+ }
+ username, err := loadConfigSecret(ctx, store.GetKind(), cfg.Username, kube, namespace)
+ if err != nil {
+ return nil, err
+ }
+ password, err := loadConfigSecret(ctx, store.GetKind(), cfg.Password, kube, namespace)
+ if err != nil {
+ return nil, err
+ }
+
+ secretServer, err := server.New(server.Configuration{
+ Credentials: server.UserCredential{
+ Username: username,
+ Password: password,
+ },
+ ServerURL: cfg.ServerURL,
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ return &client{
+ api: secretServer,
+ }, nil
+}
+
+func loadConfigSecret(
+ ctx context.Context,
+ storeKind string,
+ ref *esv1beta1.SecretServerProviderRef,
+ kube kubeClient.Client,
+ namespace string) (string, error) {
+ if ref.SecretRef == nil {
+ return ref.Value, nil
+ }
+ if err := validateSecretRef(ref); err != nil {
+ return "", err
+ }
+ return resolvers.SecretKeyRef(ctx, kube, storeKind, namespace, ref.SecretRef)
+}
+
+func validateStoreSecretRef(store esv1beta1.GenericStore, ref *esv1beta1.SecretServerProviderRef) error {
+ if ref.SecretRef != nil {
+ if err := utils.ValidateReferentSecretSelector(store, *ref.SecretRef); err != nil {
+ return err
+ }
+ }
+ return validateSecretRef(ref)
+}
+
+func validateSecretRef(ref *esv1beta1.SecretServerProviderRef) error {
+ if ref.SecretRef != nil {
+ if ref.Value != "" {
+ return errSecretRefAndValueConflict
+ }
+ if ref.SecretRef.Name == "" {
+ return errMissingSecretName
+ }
+ if ref.SecretRef.Key == "" {
+ return errMissingSecretKey
+ }
+ } else if ref.Value == "" {
+ return errSecretRefAndValueMissing
+ }
+ return nil
+}
+
+func doesConfigDependOnNamespace(cfg *esv1beta1.SecretServerProvider) bool {
+ if cfg.Username.SecretRef != nil && cfg.Username.SecretRef.Namespace == nil {
+ return true
+ }
+ if cfg.Password.SecretRef != nil && cfg.Password.SecretRef.Namespace == nil {
+ return true
+ }
+ return false
+}
+
+func getConfig(store esv1beta1.GenericStore) (*esv1beta1.SecretServerProvider, error) {
+ if store == nil {
+ return nil, errMissingStore
+ }
+ storeSpec := store.GetSpec()
+
+ if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.SecretServer == nil {
+ return nil, errInvalidSpec
+ }
+ cfg := storeSpec.Provider.SecretServer
+
+ if cfg.Username == nil {
+ return nil, errEmptyUserName
+ }
+ if cfg.Password == nil {
+ return nil, errEmptyPassword
+ }
+ if cfg.ServerURL == "" {
+ return nil, errEmptyServerURL
+ }
+
+ err := validateStoreSecretRef(store, cfg.Username)
+ if err != nil {
+ return nil, err
+ }
+ err = validateStoreSecretRef(store, cfg.Password)
+ if err != nil {
+ return nil, err
+ }
+ return cfg, nil
+}
+
+func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnings, error) {
+ _, err := getConfig(store)
+ return nil, err
+}
+
+func init() {
+ esv1beta1.Register(&Provider{}, &esv1beta1.SecretStoreProvider{
+ SecretServer: &esv1beta1.SecretServerProvider{},
+ })
+}
diff --git a/pkg/provider/secretserver/provider_test.go b/pkg/provider/secretserver/provider_test.go
new file mode 100644
index 00000000000..53a14fc4f4d
--- /dev/null
+++ b/pkg/provider/secretserver/provider_test.go
@@ -0,0 +1,351 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package secretserver
+
+import (
+ "context"
+ "math/rand"
+ "testing"
+
+ "github.com/DelineaXPM/tss-sdk-go/v2/server"
+ "github.com/stretchr/testify/assert"
+ corev1 "k8s.io/api/core/v1"
+ kubeErrors "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ kubeClient "sigs.k8s.io/controller-runtime/pkg/client"
+ clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ v1 "github.com/external-secrets/external-secrets/apis/meta/v1"
+ "github.com/external-secrets/external-secrets/pkg/utils"
+)
+
+func TestDoesConfigDependOnNamespace(t *testing.T) {
+ tests := map[string]struct {
+ cfg esv1beta1.SecretServerProvider
+ want bool
+ }{
+ "true when Username references a secret without explicit namespace": {
+ cfg: esv1beta1.SecretServerProvider{
+ Username: &esv1beta1.SecretServerProviderRef{
+ SecretRef: &v1.SecretKeySelector{Name: "foo"},
+ },
+ Password: &esv1beta1.SecretServerProviderRef{SecretRef: nil},
+ },
+ want: true,
+ },
+ "true when password references a secret without explicit namespace": {
+ cfg: esv1beta1.SecretServerProvider{
+ Username: &esv1beta1.SecretServerProviderRef{SecretRef: nil},
+ Password: &esv1beta1.SecretServerProviderRef{
+ SecretRef: &v1.SecretKeySelector{Name: "foo"},
+ },
+ },
+ want: true,
+ },
+ "false when neither Username or Password reference a secret": {
+ cfg: esv1beta1.SecretServerProvider{
+ Username: &esv1beta1.SecretServerProviderRef{SecretRef: nil},
+ Password: &esv1beta1.SecretServerProviderRef{SecretRef: nil},
+ },
+ want: false,
+ },
+ }
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ got := doesConfigDependOnNamespace(&tc.cfg)
+ assert.Equal(t, tc.want, got)
+ })
+ }
+}
+
+func TestValidateStore(t *testing.T) {
+ validSecretRefUsingValue := makeSecretRefUsingValue("foo")
+ ambiguousSecretRef := &esv1beta1.SecretServerProviderRef{
+ SecretRef: &v1.SecretKeySelector{Name: "foo"}, Value: "foo",
+ }
+ testURL := "https://example.com"
+
+ tests := map[string]struct {
+ cfg esv1beta1.SecretServerProvider
+ want error
+ }{
+ "invalid without username": {
+ cfg: esv1beta1.SecretServerProvider{
+ Username: nil,
+ Password: validSecretRefUsingValue,
+ ServerURL: testURL,
+ },
+ want: errEmptyUserName,
+ },
+ "invalid without password": {
+ cfg: esv1beta1.SecretServerProvider{
+ Username: validSecretRefUsingValue,
+ Password: nil,
+ ServerURL: testURL,
+ },
+ want: errEmptyPassword,
+ },
+ "invalid without serverURL": {
+ cfg: esv1beta1.SecretServerProvider{
+ Username: validSecretRefUsingValue,
+ Password: validSecretRefUsingValue,
+ /*ServerURL: testURL,*/
+ },
+ want: errEmptyServerURL,
+ },
+ "invalid with ambiguous Username": {
+ cfg: esv1beta1.SecretServerProvider{
+ Username: ambiguousSecretRef,
+ Password: validSecretRefUsingValue,
+ ServerURL: testURL,
+ },
+ want: errSecretRefAndValueConflict,
+ },
+ "invalid with ambiguous Password": {
+ cfg: esv1beta1.SecretServerProvider{
+ Username: validSecretRefUsingValue,
+ Password: ambiguousSecretRef,
+ ServerURL: testURL,
+ },
+ want: errSecretRefAndValueConflict,
+ },
+ "invalid with invalid Username": {
+ cfg: esv1beta1.SecretServerProvider{
+ Username: makeSecretRefUsingValue(""),
+ Password: validSecretRefUsingValue,
+ ServerURL: testURL,
+ },
+ want: errSecretRefAndValueMissing,
+ },
+ "invalid with invalid Password": {
+ cfg: esv1beta1.SecretServerProvider{
+ Username: validSecretRefUsingValue,
+ Password: makeSecretRefUsingValue(""),
+ ServerURL: testURL,
+ },
+ want: errSecretRefAndValueMissing,
+ },
+ "valid with tenant/clientID/clientSecret": {
+ cfg: esv1beta1.SecretServerProvider{
+ Username: validSecretRefUsingValue,
+ Password: validSecretRefUsingValue,
+ ServerURL: testURL,
+ },
+ want: nil,
+ },
+ }
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ s := esv1beta1.SecretStore{
+ Spec: esv1beta1.SecretStoreSpec{
+ Provider: &esv1beta1.SecretStoreProvider{
+ SecretServer: &tc.cfg,
+ },
+ },
+ }
+ p := &Provider{}
+ _, got := p.ValidateStore(&s)
+ assert.Equal(t, tc.want, got)
+ })
+ }
+}
+
+func TestNewClient(t *testing.T) {
+ userNameKey := "username"
+ userNameValue := "foo"
+ passwordKey := "password"
+ passwordValue := generateRandomString()
+
+ clientSecret := &corev1.Secret{
+ ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "default"},
+ Data: map[string][]byte{
+ userNameKey: []byte(userNameValue),
+ passwordKey: []byte(passwordValue),
+ },
+ }
+
+ validProvider := &esv1beta1.SecretServerProvider{
+ Username: makeSecretRefUsingRef(clientSecret.Name, userNameKey),
+ Password: makeSecretRefUsingRef(clientSecret.Name, passwordKey),
+ ServerURL: "https://example.com",
+ }
+
+ tests := map[string]struct {
+ store esv1beta1.GenericStore // leave nil for namespaced store
+ provider *esv1beta1.SecretServerProvider // discarded when store is set
+ kube kubeClient.Client
+ errCheck func(t *testing.T, err error)
+ }{
+ "missing provider config": {
+ provider: nil,
+ errCheck: func(t *testing.T, err error) {
+ assert.ErrorIs(t, err, errInvalidSpec)
+ },
+ },
+ "namespace-dependent cluster secret store": {
+ store: &esv1beta1.ClusterSecretStore{
+ TypeMeta: metav1.TypeMeta{Kind: esv1beta1.ClusterSecretStoreKind},
+ Spec: esv1beta1.SecretStoreSpec{
+ Provider: &esv1beta1.SecretStoreProvider{
+ SecretServer: validProvider,
+ },
+ },
+ },
+ errCheck: func(t *testing.T, err error) {
+ assert.ErrorIs(t, err, errClusterStoreRequiresNamespace)
+ },
+ },
+ "dangling password ref": {
+ provider: &esv1beta1.SecretServerProvider{
+ Username: validProvider.Username,
+ Password: makeSecretRefUsingRef("typo", passwordKey),
+ ServerURL: validProvider.ServerURL,
+ },
+ kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
+ errCheck: func(t *testing.T, err error) {
+ assert.True(t, kubeErrors.IsNotFound(err))
+ },
+ },
+ "dangling username ref": {
+ provider: &esv1beta1.SecretServerProvider{
+ Username: makeSecretRefUsingRef("typo", userNameKey),
+ Password: validProvider.Password,
+ ServerURL: validProvider.ServerURL,
+ },
+ kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
+ errCheck: func(t *testing.T, err error) {
+ assert.True(t, kubeErrors.IsNotFound(err))
+ },
+ },
+ "secret ref without name": {
+ provider: &esv1beta1.SecretServerProvider{
+ Username: makeSecretRefUsingRef("", userNameKey),
+ Password: validProvider.Password,
+ ServerURL: validProvider.ServerURL,
+ },
+ kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
+ errCheck: func(t *testing.T, err error) {
+ assert.ErrorIs(t, err, errMissingSecretName)
+ },
+ },
+ "secret ref without key": {
+ provider: &esv1beta1.SecretServerProvider{
+ Username: validProvider.Password,
+ Password: makeSecretRefUsingRef(clientSecret.Name, ""),
+ ServerURL: validProvider.ServerURL,
+ },
+ kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
+ errCheck: func(t *testing.T, err error) {
+ assert.ErrorIs(t, err, errMissingSecretKey)
+ },
+ },
+ "secret ref with non-existent keys": {
+ provider: &esv1beta1.SecretServerProvider{
+ Username: makeSecretRefUsingRef(clientSecret.Name, "typo"),
+ Password: makeSecretRefUsingRef(clientSecret.Name, passwordKey),
+ ServerURL: validProvider.ServerURL,
+ },
+ kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
+ errCheck: func(t *testing.T, err error) {
+ assert.EqualError(t, err, "cannot find secret data for key: \"typo\"")
+ },
+ },
+ "valid secret refs": {
+ provider: validProvider,
+ kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
+ },
+ "secret values": {
+ provider: &esv1beta1.SecretServerProvider{
+ Username: makeSecretRefUsingValue(userNameValue),
+ Password: makeSecretRefUsingValue(passwordValue),
+ ServerURL: validProvider.ServerURL,
+ },
+ kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
+ },
+ "cluster secret store": {
+ store: &esv1beta1.ClusterSecretStore{
+ TypeMeta: metav1.TypeMeta{Kind: esv1beta1.ClusterSecretStoreKind},
+ Spec: esv1beta1.SecretStoreSpec{
+ Provider: &esv1beta1.SecretStoreProvider{
+ SecretServer: &esv1beta1.SecretServerProvider{
+ Username: makeSecretRefUsingNamespacedRef(clientSecret.Namespace, clientSecret.Name, userNameKey),
+ Password: makeSecretRefUsingNamespacedRef(clientSecret.Namespace, clientSecret.Name, passwordKey),
+ ServerURL: validProvider.ServerURL,
+ },
+ },
+ },
+ },
+ kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(),
+ },
+ }
+ for name, tc := range tests {
+ t.Run(name, func(t *testing.T) {
+ p := &Provider{}
+ store := tc.store
+ if store == nil {
+ store = &esv1beta1.SecretStore{
+ TypeMeta: metav1.TypeMeta{Kind: esv1beta1.SecretStoreKind},
+ Spec: esv1beta1.SecretStoreSpec{
+ Provider: &esv1beta1.SecretStoreProvider{
+ SecretServer: tc.provider,
+ },
+ },
+ }
+ }
+ sc, err := p.NewClient(context.Background(), store, tc.kube, clientSecret.Namespace)
+ if tc.errCheck == nil {
+ assert.NoError(t, err)
+ delineaClient, ok := sc.(*client)
+ assert.True(t, ok)
+ secretServerClient, ok := delineaClient.api.(*server.Server)
+ assert.True(t, ok)
+ assert.Equal(t, server.UserCredential{
+ Username: userNameValue,
+ Password: passwordValue,
+ }, secretServerClient.Configuration.Credentials)
+ } else {
+ assert.Nil(t, sc)
+ tc.errCheck(t, err)
+ }
+ })
+ }
+}
+
+func makeSecretRefUsingNamespacedRef(namespace, name, key string) *esv1beta1.SecretServerProviderRef {
+ return &esv1beta1.SecretServerProviderRef{
+ SecretRef: &v1.SecretKeySelector{Namespace: utils.Ptr(namespace), Name: name, Key: key},
+ }
+}
+
+func makeSecretRefUsingValue(val string) *esv1beta1.SecretServerProviderRef {
+ return &esv1beta1.SecretServerProviderRef{Value: val}
+}
+
+func makeSecretRefUsingRef(name, key string) *esv1beta1.SecretServerProviderRef {
+ return &esv1beta1.SecretServerProviderRef{
+ SecretRef: &v1.SecretKeySelector{Name: name, Key: key},
+ }
+}
+
+func generateRandomString() string {
+ var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
+ b := make([]rune, 10)
+ for i := range b {
+ b[i] = letters[rand.Intn(len(letters))]
+ }
+
+ return string(b)
+}
diff --git a/pkg/provider/secretserver/secret_api.go b/pkg/provider/secretserver/secret_api.go
new file mode 100644
index 00000000000..f8beacff6a3
--- /dev/null
+++ b/pkg/provider/secretserver/secret_api.go
@@ -0,0 +1,26 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package secretserver
+
+import (
+ "github.com/DelineaXPM/tss-sdk-go/v2/server"
+)
+
+// secretAPI represents the subset of the Secret Server API
+// which is supported by tss-sdk-go/v2.
+type secretAPI interface {
+ Secret(id int) (*server.Secret, error)
+ Secrets(searchText, field string) ([]server.Secret, error)
+}
diff --git a/pkg/provider/secretserver/test_data.json b/pkg/provider/secretserver/test_data.json
new file mode 100644
index 00000000000..611e5906d48
--- /dev/null
+++ b/pkg/provider/secretserver/test_data.json
@@ -0,0 +1,38 @@
+{
+"Name": "ESO-test-secret",
+"FolderID": 73,
+"ID": 1000,
+"SiteID": 1,
+"SecretTemplateID": 6098,
+"SecretPolicyID": -1,
+"PasswordTypeWebScriptID": -1,
+"LauncherConnectAsSecretID": -1,
+"CheckOutIntervalMinutes": -1,
+"Active": true,
+"CheckedOut": false,
+"CheckOutEnabled": false,
+"AutoChangeEnabled": false,
+"CheckOutChangePasswordEnabled": false,
+"DelayIndexing": false,
+"EnableInheritPermissions": false,
+"EnableInheritSecretPolicy": false,
+"ProxyEnabled": false,
+"RequiresComment": false,
+"SessionRecordingEnabled": false,
+"WebLauncherRequiresIncognitoMode": false,
+"Items": [
+ {
+ "ItemID": 286259,
+ "FieldID": 439,
+ "FileAttachmentID": 0,
+ "FieldName": "Data",
+ "Slug": "data",
+ "FieldDescription": "json text field",
+ "Filename": "",
+ "ItemValue": "{ \"user\": \"robertOppenheimer\", \"password\": \"badPassword\",\"server\":\"192.168.1.50\"}",
+ "IsFile": false,
+ "IsNotes": false,
+ "IsPassword": false
+ }
+]
+}
diff --git a/pkg/provider/senhasegura/provider.go b/pkg/provider/senhasegura/provider.go
index 67dd0a829e0..64d2a62e01c 100644
--- a/pkg/provider/senhasegura/provider.go
+++ b/pkg/provider/senhasegura/provider.go
@@ -16,6 +16,7 @@ package senhasegura
import (
"context"
+ "errors"
"fmt"
"net/url"
@@ -77,16 +78,16 @@ func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnin
func validateStore(store esv1beta1.GenericStore) error {
if store == nil {
- return fmt.Errorf(errNilStore)
+ return errors.New(errNilStore)
}
spec := store.GetSpec()
if spec == nil {
- return fmt.Errorf(errMissingStoreSpec)
+ return errors.New(errMissingStoreSpec)
}
if spec.Provider == nil {
- return fmt.Errorf(errMissingProvider)
+ return errors.New(errMissingProvider)
}
provider := spec.Provider.Senhasegura
@@ -96,21 +97,21 @@ func validateStore(store esv1beta1.GenericStore) error {
url, err := url.Parse(provider.URL)
if err != nil {
- return fmt.Errorf(errInvalidSenhaseguraURL)
+ return errors.New(errInvalidSenhaseguraURL)
}
// senhasegura doesn't accept requests without SSL/TLS layer for security reasons
// DSM doesn't provides gRPC schema, only HTTPS
if url.Scheme != "https" {
- return fmt.Errorf(errInvalidSenhaseguraURLHTTPS)
+ return errors.New(errInvalidSenhaseguraURLHTTPS)
}
if url.Host == "" {
- return fmt.Errorf(errInvalidSenhaseguraURL)
+ return errors.New(errInvalidSenhaseguraURL)
}
if provider.Auth.ClientID == "" {
- return fmt.Errorf(errMissingClientID)
+ return errors.New(errMissingClientID)
}
return nil
diff --git a/pkg/provider/vault/auth.go b/pkg/provider/vault/auth.go
index 6f06e17fa3a..e076bcca844 100644
--- a/pkg/provider/vault/auth.go
+++ b/pkg/provider/vault/auth.go
@@ -16,6 +16,7 @@ package vault
import (
"context"
+ "encoding/json"
"errors"
"fmt"
@@ -42,6 +43,10 @@ const (
// setAuth gets a new token using the configured mechanism.
// If there's already a valid token, does nothing.
func (c *client) setAuth(ctx context.Context, cfg *vault.Config) error {
+ if c.store.Namespace != nil { // set namespace before checking the need for AuthNamespace
+ c.client.SetNamespace(*c.store.Namespace)
+ }
+
// Switch to auth namespace if different from the provider namespace
restoreNamespace := c.useAuthNamespace(ctx)
defer restoreNamespace()
@@ -147,14 +152,37 @@ func checkToken(ctx context.Context, token util.Token) (bool, error) {
if err != nil {
return false, err
}
+ // LookupSelfWithContext() calls ParseSecret(), which has several places
+ // that return no data and no error, including when a token is expired.
+ if resp == nil {
+ return false, errors.New("no response nor error for token lookup")
+ }
t, ok := resp.Data["type"]
if !ok {
- return false, fmt.Errorf("could not assert token type")
+ return false, errors.New("could not assert token type")
}
tokenType := t.(string)
if tokenType == "batch" {
return false, nil
}
+ ttl, ok := resp.Data["ttl"]
+ if !ok {
+ return false, errors.New("no TTL found in response")
+ }
+ ttlInt, err := ttl.(json.Number).Int64()
+ if err != nil {
+ return false, fmt.Errorf("invalid token TTL: %v: %w", ttl, err)
+ }
+ expireTime, ok := resp.Data["expire_time"]
+ if !ok {
+ return false, errors.New("no expiration time found in response")
+ }
+ if ttlInt < 60 && expireTime != nil {
+ // Treat expirable tokens that are about to expire as already expired.
+ // This ensures that the token won't expire in between this check and
+ // performing the actual operation.
+ return false, nil
+ }
return true, nil
}
@@ -176,7 +204,7 @@ func revokeTokenIfValid(ctx context.Context, client util.Client) error {
func (c *client) useAuthNamespace(_ context.Context) func() {
ns := ""
- if c.store.Namespace != nil {
+ if c.store != nil && c.store.Namespace != nil {
ns = *c.store.Namespace
}
@@ -194,6 +222,7 @@ func (c *client) useAuthNamespace(_ context.Context) func() {
}
}
- // no-op
- return func() {}
+ return func() {
+ // no-op
+ }
}
diff --git a/pkg/provider/vault/auth_approle.go b/pkg/provider/vault/auth_approle.go
index 25b21e39e86..3047dd6b329 100644
--- a/pkg/provider/vault/auth_approle.go
+++ b/pkg/provider/vault/auth_approle.go
@@ -16,10 +16,10 @@ package vault
import (
"context"
- "fmt"
+ "errors"
"strings"
- approle "github.com/hashicorp/vault/api/auth/approle"
+ "github.com/hashicorp/vault/api/auth/approle"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
"github.com/external-secrets/external-secrets/pkg/constants"
@@ -56,7 +56,7 @@ func (c *client) requestTokenWithAppRoleRef(ctx context.Context, appRole *esv1be
return err
}
} else { // we ran out of ways to get RoleID. return an appropriate error
- return fmt.Errorf(errInvalidAppRoleID)
+ return errors.New(errInvalidAppRoleID)
}
secretID, err := resolvers.SecretKeyRef(ctx, c.kube, c.storeKind, c.namespace, &appRole.SecretRef)
diff --git a/pkg/provider/vault/auth_jwt.go b/pkg/provider/vault/auth_jwt.go
index bb531f6e809..aa5906c6764 100644
--- a/pkg/provider/vault/auth_jwt.go
+++ b/pkg/provider/vault/auth_jwt.go
@@ -16,6 +16,7 @@ package vault
import (
"context"
+ "errors"
"fmt"
"strings"
@@ -66,7 +67,7 @@ func (c *client) requestTokenWithJwtAuth(ctx context.Context, jwtAuth *esv1beta1
*audiences,
*expirationSeconds)
} else {
- err = fmt.Errorf(errJwtNoTokenSource)
+ err = errors.New(errJwtNoTokenSource)
}
if err != nil {
return err
diff --git a/pkg/provider/vault/auth_test.go b/pkg/provider/vault/auth_test.go
index 823d260df17..1432806edaa 100644
--- a/pkg/provider/vault/auth_test.go
+++ b/pkg/provider/vault/auth_test.go
@@ -16,10 +16,13 @@ package vault
import (
"context"
+ "encoding/json"
+ "errors"
"testing"
"github.com/google/go-cmp/cmp"
"github.com/google/go-cmp/cmp/cmpopts"
+ vault "github.com/hashicorp/vault/api"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/utils/ptr"
@@ -131,7 +134,7 @@ func TestSetAuthNamespace(t *testing.T) {
c, cfg, err := prov.prepareConfig(context.Background(), kube, nil, tc.args.store.Spec.Provider.Vault, nil, "default", store.GetObjectKind().GroupVersionKind().Kind)
if err != nil {
- t.Errorf(err.Error())
+ t.Error(err.Error())
}
client, err := getVaultClient(prov, tc.args.store, cfg)
@@ -165,3 +168,112 @@ func TestSetAuthNamespace(t *testing.T) {
})
}
}
+
+func TestCheckTokenErrors(t *testing.T) {
+ cases := map[string]struct {
+ message string
+ secret *vault.Secret
+ err error
+ }{
+ "SuccessWithNoData": {
+ message: "should not cache if token lookup returned no data",
+ secret: &vault.Secret{},
+ err: nil,
+ },
+ "Error": {
+ message: "should not cache if token lookup errored",
+ secret: nil,
+ err: errors.New(""),
+ },
+ // This happens when a token is expired and the Vault server returns:
+ // {"errors":["permission denied"]}
+ "NoDataNorError": {
+ message: "should not cache if token lookup returned no data nor error",
+ secret: nil,
+ err: nil,
+ },
+ }
+
+ for name, tc := range cases {
+ t.Run(name, func(t *testing.T) {
+ token := fake.Token{
+ LookupSelfWithContextFn: func(ctx context.Context) (*vault.Secret, error) {
+ return tc.secret, tc.err
+ },
+ }
+
+ cached, _ := checkToken(context.Background(), token)
+ if cached {
+ t.Errorf("%v", tc.message)
+ }
+ })
+ }
+}
+
+func TestCheckTokenTtl(t *testing.T) {
+ cases := map[string]struct {
+ message string
+ secret *vault.Secret
+ cache bool
+ }{
+ "LongTTLExpirable": {
+ message: "should cache if expirable token expires far into the future",
+ secret: &vault.Secret{
+ Data: map[string]interface{}{
+ "expire_time": "2024-01-01T00:00:00.000000000Z",
+ "ttl": json.Number("3600"),
+ "type": "service",
+ },
+ },
+ cache: true,
+ },
+ "ShortTTLExpirable": {
+ message: "should not cache if expirable token is about to expire",
+ secret: &vault.Secret{
+ Data: map[string]interface{}{
+ "expire_time": "2024-01-01T00:00:00.000000000Z",
+ "ttl": json.Number("5"),
+ "type": "service",
+ },
+ },
+ cache: false,
+ },
+ "ZeroTTLExpirable": {
+ message: "should not cache if expirable token has TTL of 0",
+ secret: &vault.Secret{
+ Data: map[string]interface{}{
+ "expire_time": "2024-01-01T00:00:00.000000000Z",
+ "ttl": json.Number("0"),
+ "type": "service",
+ },
+ },
+ cache: false,
+ },
+ "NonExpirable": {
+ message: "should cache if token is non-expirable",
+ secret: &vault.Secret{
+ Data: map[string]interface{}{
+ "expire_time": nil,
+ "ttl": json.Number("0"),
+ "type": "service",
+ },
+ },
+ cache: true,
+ },
+ }
+
+ for name, tc := range cases {
+ t.Run(name, func(t *testing.T) {
+ token := fake.Token{
+ LookupSelfWithContextFn: func(ctx context.Context) (*vault.Secret, error) {
+ return tc.secret, nil
+ },
+ }
+
+ cached, err := checkToken(context.Background(), token)
+ if cached != tc.cache || err != nil {
+ t.Errorf("%v: err = %v", tc.message, err)
+ }
+ })
+ }
+}
diff --git a/pkg/provider/vault/client.go b/pkg/provider/vault/client.go
index e6cb3131991..8de3f2bf5c1 100644
--- a/pkg/provider/vault/client.go
+++ b/pkg/provider/vault/client.go
@@ -25,13 +25,12 @@ import (
"github.com/go-logr/logr"
vault "github.com/hashicorp/vault/api"
corev1 "k8s.io/api/core/v1"
- "k8s.io/apimachinery/pkg/types"
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
kclient "sigs.k8s.io/controller-runtime/pkg/client"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
- esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
"github.com/external-secrets/external-secrets/pkg/provider/vault/util"
+ "github.com/external-secrets/external-secrets/pkg/utils"
"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
)
@@ -56,39 +55,19 @@ func (c *client) newConfig(ctx context.Context) (*vault.Config, error) {
if len(c.store.CABundle) != 0 || c.store.CAProvider != nil {
caCertPool := x509.NewCertPool()
-
- if len(c.store.CABundle) > 0 {
- ok := caCertPool.AppendCertsFromPEM(c.store.CABundle)
- if !ok {
- return nil, fmt.Errorf(errVaultCert, errors.New("failed to parse certificates from CertPool"))
- }
- }
-
- if c.store.CAProvider != nil && c.storeKind == esv1beta1.ClusterSecretStoreKind && c.store.CAProvider.Namespace == nil {
- return nil, errors.New(errCANamespace)
+ ca, err := utils.FetchCACertFromSource(ctx, utils.CreateCertOpts{
+ CABundle: c.store.CABundle,
+ CAProvider: c.store.CAProvider,
+ StoreKind: c.storeKind,
+ Namespace: c.namespace,
+ Client: c.kube,
+ })
+ if err != nil {
+ return nil, err
}
-
- if c.store.CAProvider != nil {
- var cert []byte
- var err error
-
- switch c.store.CAProvider.Type {
- case esv1beta1.CAProviderTypeSecret:
- cert, err = getCertFromSecret(c)
- case esv1beta1.CAProviderTypeConfigMap:
- cert, err = getCertFromConfigMap(c)
- default:
- return nil, errors.New(errUnknownCAProvider)
- }
-
- if err != nil {
- return nil, err
- }
-
- ok := caCertPool.AppendCertsFromPEM(cert)
- if !ok {
- return nil, fmt.Errorf(errVaultCert, errors.New("failed to parse certificates from CertPool"))
- }
+ ok := caCertPool.AppendCertsFromPEM(ca)
+ if !ok {
+ return nil, fmt.Errorf(errVaultCert, errors.New("failed to parse certificates from CertPool"))
}
if transport, ok := cfg.HttpClient.Transport.(*http.Transport); ok {
@@ -138,50 +117,6 @@ func (c *client) configureClientTLS(ctx context.Context, cfg *vault.Config) erro
return nil
}
-func getCertFromSecret(v *client) ([]byte, error) {
- secretRef := esmeta.SecretKeySelector{
- Name: v.store.CAProvider.Name,
- Namespace: &v.namespace,
- Key: v.store.CAProvider.Key,
- }
-
- if v.store.CAProvider.Namespace != nil {
- secretRef.Namespace = v.store.CAProvider.Namespace
- }
-
- ctx := context.Background()
- res, err := resolvers.SecretKeyRef(ctx, v.kube, v.storeKind, v.namespace, &secretRef)
- if err != nil {
- return nil, fmt.Errorf(errVaultCert, err)
- }
-
- return []byte(res), nil
-}
-
-func getCertFromConfigMap(v *client) ([]byte, error) {
- objKey := types.NamespacedName{
- Name: v.store.CAProvider.Name,
- Namespace: v.namespace,
- }
-
- if v.store.CAProvider.Namespace != nil {
- objKey.Namespace = *v.store.CAProvider.Namespace
- }
-
- configMapRef := &corev1.ConfigMap{}
- ctx := context.Background()
- err := v.kube.Get(ctx, objKey, configMapRef)
- if err != nil {
- return nil, fmt.Errorf(errVaultCert, err)
- }
-
- val, ok := configMapRef.Data[v.store.CAProvider.Key]
- if !ok {
- return nil, fmt.Errorf(errConfigMapFmt, v.store.CAProvider.Key)
- }
- return []byte(val), nil
-}
-
func (c *client) Close(ctx context.Context) error {
// Revoke the token if we have one set, it wasn't sourced from a TokenSecretRef,
// and token caching isn't enabled
diff --git a/pkg/provider/vault/client_get.go b/pkg/provider/vault/client_get.go
index 8a8f307aa00..30e72c88a70 100644
--- a/pkg/provider/vault/client_get.go
+++ b/pkg/provider/vault/client_get.go
@@ -218,10 +218,10 @@ func (c *client) buildMetadataPath(path string) (string, error) {
url = fmt.Sprintf("%s/%s", *c.store.Path, path)
} else { // KV v2 is used
if c.store.Path == nil && !strings.Contains(path, "data") {
- return "", fmt.Errorf(errPathInvalid)
+ return "", errors.New(errPathInvalid)
}
if c.store.Path == nil {
- path = strings.Replace(path, "data", "metadata", 1)
+ path = strings.Replace(path, "/data/", "/metadata/", 1)
url = path
} else {
url = fmt.Sprintf("%s/metadata/%s", *c.store.Path, path)
diff --git a/pkg/provider/vault/client_get_all_secrets.go b/pkg/provider/vault/client_get_all_secrets.go
index b28d09754e3..b8575414120 100644
--- a/pkg/provider/vault/client_get_all_secrets.go
+++ b/pkg/provider/vault/client_get_all_secrets.go
@@ -27,14 +27,14 @@ import (
)
const (
- errUnsupportedKvVersion = "cannot perform find operations with kv version v1"
+ errUnsupportedKvVersion = "cannot perform find by tag operations with kv version v1"
)
// GetAllSecrets gets multiple secrets from the provider and loads into a kubernetes secret.
// First load all secrets from secretStore path configuration
// Then, gets secrets from a matching name or matching custom_metadata.
func (c *client) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
- if c.store.Version == esv1beta1.VaultKVStoreV1 {
+ if c.store.Version == esv1beta1.VaultKVStoreV1 && ref.Tags != nil {
return nil, errors.New(errUnsupportedKvVersion)
}
searchPath := ""
@@ -118,7 +118,7 @@ func (c *client) listSecrets(ctx context.Context, path string) ([]string, error)
return nil, fmt.Errorf(errReadSecret, err)
}
if secret == nil {
- return nil, fmt.Errorf("provided path %v does not contain any secrets", url)
+ return nil, esv1beta1.NoSecretError{}
}
t, ok := secret.Data["keys"]
if !ok {
diff --git a/pkg/provider/vault/client_get_all_secrets_test.go b/pkg/provider/vault/client_get_all_secrets_test.go
index b4b5afe8f76..1ae4ad854f8 100644
--- a/pkg/provider/vault/client_get_all_secrets_test.go
+++ b/pkg/provider/vault/client_get_all_secrets_test.go
@@ -35,7 +35,7 @@ func TestGetAllSecrets(t *testing.T) {
path2Bytes := []byte("{\"access_key\":\"path2\",\"access_secret\":\"path2\"}")
tagBytes := []byte("{\"access_key\":\"unfetched\",\"access_secret\":\"unfetched\"}")
path := "path"
- secret := map[string]any{
+ kv2secret := map[string]any{
"secret1": map[string]any{
"metadata": map[string]any{
"custom_metadata": map[string]any{
@@ -116,6 +116,28 @@ func TestGetAllSecrets(t *testing.T) {
},
},
}
+ kv1secret := map[string]any{
+ "secret1": map[string]any{
+ "access_key": "access_key",
+ "access_secret": "access_secret",
+ },
+ "secret2": map[string]any{
+ "access_key": "access_key2",
+ "access_secret": "access_secret2",
+ },
+ "tag": map[string]any{
+ "access_key": "unfetched",
+ "access_secret": "unfetched",
+ },
+ "path/1": map[string]any{
+ "access_key": "path1",
+ "access_secret": "path1",
+ },
+ "path/2": map[string]any{
+ "access_key": "path2",
+ "access_secret": "path2",
+ },
+ }
type args struct {
store *esv1beta1.VaultProvider
kube kclient.Client
@@ -134,13 +156,35 @@ func TestGetAllSecrets(t *testing.T) {
args args
want want
}{
- "FindByName": {
- reason: "should map multiple secrets matching name",
+ "FindByNameKv2": {
+ reason: "should map multiple secrets matching name for kv2",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vLogical: &fake.Logical{
- ListWithContextFn: newListWithContextFn(secret),
- ReadWithDataWithContextFn: newReadtWithContextFn(secret),
+ ListWithContextFn: newListWithContextFn(kv2secret),
+ ReadWithDataWithContextFn: newReadtWithContextFn(kv2secret),
+ },
+ data: esv1beta1.ExternalSecretFind{
+ Name: &esv1beta1.FindName{
+ RegExp: "secret.*",
+ },
+ },
+ },
+ want: want{
+ err: nil,
+ val: map[string][]byte{
+ "secret1": secret1Bytes,
+ "secret2": secret2Bytes,
+ },
+ },
+ },
+ "FindByNameKv1": {
+ reason: "should map multiple secrets matching name for kv1",
+ args: args{
+ store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
+ vLogical: &fake.Logical{
+ ListWithContextFn: newListWithContextKvv1Fn(kv1secret),
+ ReadWithDataWithContextFn: newReadtWithContextKvv1Fn(kv1secret),
},
data: esv1beta1.ExternalSecretFind{
Name: &esv1beta1.FindName{
@@ -156,13 +200,13 @@ func TestGetAllSecrets(t *testing.T) {
},
},
},
- "FindByTag": {
+ "FindByTagKv2": {
reason: "should map multiple secrets matching tags",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vLogical: &fake.Logical{
- ListWithContextFn: newListWithContextFn(secret),
- ReadWithDataWithContextFn: newReadtWithContextFn(secret),
+ ListWithContextFn: newListWithContextFn(kv2secret),
+ ReadWithDataWithContextFn: newReadtWithContextFn(kv2secret),
},
data: esv1beta1.ExternalSecretFind{
Tags: map[string]string{
@@ -178,13 +222,31 @@ func TestGetAllSecrets(t *testing.T) {
},
},
},
- "FilterByPath": {
- reason: "should filter secrets based on path",
+ "FindByTagKv1": {
+ reason: "find by tag should not work if using kv1 store",
+ args: args{
+ store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
+ vLogical: &fake.Logical{
+ ListWithContextFn: newListWithContextKvv1Fn(kv1secret),
+ ReadWithDataWithContextFn: newReadtWithContextKvv1Fn(kv1secret),
+ },
+ data: esv1beta1.ExternalSecretFind{
+ Tags: map[string]string{
+ "foo": "baz",
+ },
+ },
+ },
+ want: want{
+ err: errors.New(errUnsupportedKvVersion),
+ },
+ },
+ "FilterByPathKv2WithTags": {
+ reason: "should filter secrets based on path for kv2 with tags",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vLogical: &fake.Logical{
- ListWithContextFn: newListWithContextFn(secret),
- ReadWithDataWithContextFn: newReadtWithContextFn(secret),
+ ListWithContextFn: newListWithContextFn(kv2secret),
+ ReadWithDataWithContextFn: newReadtWithContextFn(kv2secret),
},
data: esv1beta1.ExternalSecretFind{
Path: &path,
@@ -201,22 +263,62 @@ func TestGetAllSecrets(t *testing.T) {
},
},
},
- "FailIfKv1": {
- reason: "should not work if using kv1 store",
+ "FilterByPathKv2WithoutTags": {
+ reason: "should filter secrets based on path for kv2 without tags",
args: args{
- store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
+ store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vLogical: &fake.Logical{
- ListWithContextFn: newListWithContextFn(secret),
- ReadWithDataWithContextFn: newReadtWithContextFn(secret),
+ ListWithContextFn: newListWithContextFn(kv2secret),
+ ReadWithDataWithContextFn: newReadtWithContextFn(kv2secret),
},
data: esv1beta1.ExternalSecretFind{
- Tags: map[string]string{
- "foo": "baz",
+ Path: &path,
+ },
+ },
+ want: want{
+ err: nil,
+ val: map[string][]byte{
+ "path/1": path1Bytes,
+ "path/2": path2Bytes,
+ },
+ },
+ },
+ "FilterByPathReturnsNotFound": {
+ reason: "should return a not found error if there are no more secrets on the path",
+ args: args{
+ store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
+ vLogical: &fake.Logical{
+ ListWithContextFn: func(ctx context.Context, path string) (*vault.Secret, error) {
+ return nil, nil
},
+ ReadWithDataWithContextFn: newReadtWithContextFn(map[string]any{}),
+ },
+ data: esv1beta1.ExternalSecretFind{
+ Path: &path,
},
},
want: want{
- err: errors.New(errUnsupportedKvVersion),
+ err: esv1beta1.NoSecretError{},
+ },
+ },
+ "FilterByPathKv1": {
+ reason: "should filter secrets based on path for kv1",
+ args: args{
+ store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
+ vLogical: &fake.Logical{
+ ListWithContextFn: newListWithContextKvv1Fn(kv1secret),
+ ReadWithDataWithContextFn: newReadtWithContextKvv1Fn(kv1secret),
+ },
+ data: esv1beta1.ExternalSecretFind{
+ Path: &path,
+ },
+ },
+ want: want{
+ err: nil,
+ val: map[string][]byte{
+ "path/1": path1Bytes,
+ "path/2": path2Bytes,
+ },
},
},
"MetadataNotFound": {
@@ -224,7 +326,7 @@ func TestGetAllSecrets(t *testing.T) {
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vLogical: &fake.Logical{
- ListWithContextFn: newListWithContextFn(secret),
+ ListWithContextFn: newListWithContextFn(kv2secret),
ReadWithDataWithContextFn: func(ctx context.Context, path string, d map[string][]string) (*vault.Secret, error) {
return nil, nil
},
@@ -251,10 +353,10 @@ func TestGetAllSecrets(t *testing.T) {
}
val, err := vStore.GetAllSecrets(context.Background(), tc.args.data)
if diff := cmp.Diff(tc.want.err, err, EquateErrors()); diff != "" {
- t.Errorf("\n%s\nvault.GetSecretMap(...): -want error, +got error:\n%s", tc.reason, diff)
+ t.Errorf("\n%s\nvault.GetAllSecrets(...): -want error, +got error:\n%s", tc.reason, diff)
}
if diff := cmp.Diff(tc.want.val, val); diff != "" {
- t.Errorf("\n%s\nvault.GetSecretMap(...): -want val, +got val:\n%s", tc.reason, diff)
+ t.Errorf("\n%s\nvault.GetAllSecrets(...): -want val, +got val:\n%s", tc.reason, diff)
}
})
}
@@ -262,10 +364,11 @@ func TestGetAllSecrets(t *testing.T) {
func newListWithContextFn(secrets map[string]any) func(ctx context.Context, path string) (*vault.Secret, error) {
return func(ctx context.Context, path string) (*vault.Secret, error) {
- path = strings.TrimPrefix(path, "secret/metadata/")
+ path = strings.TrimPrefix(path, "secret/metadata/") // kvv2
if path == "" {
path = "default"
}
+
data, ok := secrets[path]
if !ok {
return nil, errors.New("Secret not found")
@@ -281,13 +384,35 @@ func newListWithContextFn(secrets map[string]any) func(ctx context.Context, path
}
}
+func newListWithContextKvv1Fn(secrets map[string]any) func(ctx context.Context, path string) (*vault.Secret, error) {
+ return func(ctx context.Context, path string) (*vault.Secret, error) {
+ path = strings.TrimPrefix(path, "secret/")
+
+ keys := make([]any, 0, len(secrets))
+ for k := range secrets {
+ if strings.HasPrefix(k, path) {
+ uniqueSuffix := strings.TrimPrefix(k, path)
+ keys = append(keys, uniqueSuffix)
+ }
+ }
+ if len(keys) == 0 {
+ return nil, errors.New("Secret not found")
+ }
+
+ secret := &vault.Secret{
+ Data: map[string]any{
+ "keys": keys,
+ },
+ }
+ return secret, nil
+ }
+}
+
func newReadtWithContextFn(secrets map[string]any) func(ctx context.Context, path string, data map[string][]string) (*vault.Secret, error) {
return func(ctx context.Context, path string, d map[string][]string) (*vault.Secret, error) {
path = strings.TrimPrefix(path, "secret/data/")
path = strings.TrimPrefix(path, "secret/metadata/")
- if path == "" {
- path = "default"
- }
+
data, ok := secrets[path]
if !ok {
return nil, errors.New("Secret not found")
@@ -304,3 +429,20 @@ func newReadtWithContextFn(secrets map[string]any) func(ctx context.Context, pat
return secret, nil
}
}
+
+func newReadtWithContextKvv1Fn(secrets map[string]any) func(ctx context.Context, path string, data map[string][]string) (*vault.Secret, error) {
+ return func(ctx context.Context, path string, d map[string][]string) (*vault.Secret, error) {
+ path = strings.TrimPrefix(path, "secret/")
+
+ data, ok := secrets[path]
+ if !ok {
+ return nil, errors.New("Secret not found")
+ }
+
+ dataAsMap := data.(map[string]any)
+ secret := &vault.Secret{
+ Data: dataAsMap,
+ }
+ return secret, nil
+ }
+}
diff --git a/pkg/provider/vault/client_get_test.go b/pkg/provider/vault/client_get_test.go
index b5b21ff6534..d197089e684 100644
--- a/pkg/provider/vault/client_get_test.go
+++ b/pkg/provider/vault/client_get_test.go
@@ -309,7 +309,7 @@ func TestGetSecret(t *testing.T) {
},
},
want: want{
- err: fmt.Errorf(errNotFound),
+ err: errors.New(errNotFound),
},
},
"FailReadSecretMetadataWrongVersion": {
@@ -324,7 +324,7 @@ func TestGetSecret(t *testing.T) {
},
},
want: want{
- err: fmt.Errorf(errUnsupportedMetadataKvVersion),
+ err: errors.New(errUnsupportedMetadataKvVersion),
},
},
}
@@ -696,6 +696,67 @@ func TestGetSecretPath(t *testing.T) {
}
}
+func TestGetSecretMetadataPath(t *testing.T) {
+ storeV2 := makeValidSecretStore()
+ storeV2NoPath := storeV2.DeepCopy()
+ multiPath := "secret/path"
+ storeV2.Spec.Provider.Vault.Path = &multiPath
+ storeV2NoPath.Spec.Provider.Vault.Path = nil
+
+ storeV1 := makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1)
+ storeV1NoPath := storeV1.DeepCopy()
+ storeV1.Spec.Provider.Vault.Path = &multiPath
+ storeV1NoPath.Spec.Provider.Vault.Path = nil
+
+ type args struct {
+ store *esv1beta1.VaultProvider
+ path string
+ expected string
+ }
+ cases := map[string]struct {
+ reason string
+ args args
+ }{
+ "PathForV1": {
+ reason: "path should compose with mount point if set",
+ args: args{
+ store: storeV1.Spec.Provider.Vault,
+ path: "data/test",
+ expected: "secret/path/data/test",
+ },
+ },
+ "PathForV2": {
+ reason: "path should compose with mount point if set without data",
+ args: args{
+ store: storeV2.Spec.Provider.Vault,
+ path: "secret/path/data/test",
+ expected: "secret/path/metadata/secret/path/data/test",
+ },
+ },
+ "PathForV2WithData": {
+ reason: "if data is in the path it shouldn't be changed",
+ args: args{
+ store: storeV2NoPath.Spec.Provider.Vault,
+ path: "my_data/data/path",
+ expected: "my_data/metadata/path",
+ },
+ },
+ }
+
+ for name, tc := range cases {
+ t.Run(name, func(t *testing.T) {
+ vStore := &client{
+ store: tc.args.store,
+ }
+
+ want, _ := vStore.buildMetadataPath(tc.args.path)
+ if diff := cmp.Diff(want, tc.args.expected); diff != "" {
+ t.Errorf("\n%s\nvault.buildPath(...): -want expected, +got error:\n%s", tc.reason, diff)
+ }
+ })
+ }
+}
+
func TestSecretExists(t *testing.T) {
secret := map[string]any{
"foo": "bar",
diff --git a/pkg/provider/vault/client_push.go b/pkg/provider/vault/client_push.go
index 42830ad4375..d36b7204baf 100644
--- a/pkg/provider/vault/client_push.go
+++ b/pkg/provider/vault/client_push.go
@@ -20,6 +20,7 @@ import (
"encoding/json"
"errors"
"fmt"
+ "maps"
corev1 "k8s.io/api/core/v1"
@@ -74,7 +75,7 @@ func (c *client) PushSecret(ctx context.Context, secret *corev1.Secret, data esv
}
manager, ok := metadata["managed-by"]
if !ok || manager != "external-secrets" {
- return fmt.Errorf("secret not managed by external-secrets")
+ return errors.New("secret not managed by external-secrets")
}
}
// Remove the metadata map to check the reconcile difference
@@ -107,9 +108,7 @@ func (c *client) PushSecret(ctx context.Context, secret *corev1.Secret, data esv
return nil
}
}
- for k, v := range vaultSecret {
- secretVal[k] = v
- }
+ maps.Insert(secretVal, maps.All(vaultSecret))
// Secret got from vault is already on map[string]string format
secretVal[data.GetProperty()] = string(value)
} else {
diff --git a/pkg/provider/vault/client_push_test.go b/pkg/provider/vault/client_push_test.go
index 771ed6a1c80..420ed0e0ff4 100644
--- a/pkg/provider/vault/client_push_test.go
+++ b/pkg/provider/vault/client_push_test.go
@@ -85,13 +85,13 @@ func TestDeleteSecret(t *testing.T) {
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
vLogical: &fake.Logical{
- ReadWithDataWithContextFn: fake.NewReadWithContextFn(nil, fmt.Errorf("failed to read")),
+ ReadWithDataWithContextFn: fake.NewReadWithContextFn(nil, errors.New("failed to read")),
WriteWithContextFn: fake.ExpectWriteWithContextNoCall(),
DeleteWithContextFn: fake.ExpectDeleteWithContextNoCall(),
},
},
want: want{
- err: fmt.Errorf("failed to read"),
+ err: errors.New("failed to read"),
},
},
"DeleteSecretFailIfErrorKV2": {
@@ -99,13 +99,13 @@ func TestDeleteSecret(t *testing.T) {
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vLogical: &fake.Logical{
- ReadWithDataWithContextFn: fake.NewReadWithContextFn(nil, fmt.Errorf("failed to read")),
+ ReadWithDataWithContextFn: fake.NewReadWithContextFn(nil, errors.New("failed to read")),
WriteWithContextFn: fake.ExpectWriteWithContextNoCall(),
DeleteWithContextFn: fake.ExpectDeleteWithContextNoCall(),
},
},
want: want{
- err: fmt.Errorf("failed to read"),
+ err: errors.New("failed to read"),
},
},
"DeleteSecretNotManagedKV1": {
@@ -200,11 +200,11 @@ func TestDeleteSecret(t *testing.T) {
},
}, nil),
WriteWithContextFn: fake.ExpectWriteWithContextNoCall(),
- DeleteWithContextFn: fake.NewDeleteWithContextFn(nil, fmt.Errorf("failed to delete")),
+ DeleteWithContextFn: fake.NewDeleteWithContextFn(nil, errors.New("failed to delete")),
},
},
want: want{
- err: fmt.Errorf("failed to delete"),
+ err: errors.New("failed to delete"),
},
},
"DeleteSecretErrorKV2": {
@@ -221,11 +221,11 @@ func TestDeleteSecret(t *testing.T) {
},
}, nil),
WriteWithContextFn: fake.ExpectWriteWithContextNoCall(),
- DeleteWithContextFn: fake.NewDeleteWithContextFn(nil, fmt.Errorf("failed to delete")),
+ DeleteWithContextFn: fake.NewDeleteWithContextFn(nil, errors.New("failed to delete")),
},
},
want: want{
- err: fmt.Errorf("failed to delete"),
+ err: errors.New("failed to delete"),
},
},
"DeleteSecretUpdatePropertyKV1": {
diff --git a/pkg/provider/vault/fake/vault.go b/pkg/provider/vault/fake/vault.go
index d11fcce6b0c..1910c432172 100644
--- a/pkg/provider/vault/fake/vault.go
+++ b/pkg/provider/vault/fake/vault.go
@@ -16,6 +16,7 @@ package fake
import (
"context"
+ "errors"
"fmt"
"reflect"
"strings"
@@ -23,7 +24,7 @@ import (
vault "github.com/hashicorp/vault/api"
- util "github.com/external-secrets/external-secrets/pkg/provider/vault/util"
+ "github.com/external-secrets/external-secrets/pkg/provider/vault/util"
)
type LoginFn func(ctx context.Context, authMethod vault.AuthMethod) (*vault.Secret, error)
@@ -104,13 +105,13 @@ func ExpectWriteWithContextValue(expected map[string]any) WriteWithContextFn {
func ExpectWriteWithContextNoCall() WriteWithContextFn {
return func(_ context.Context, path string, data map[string]any) (*vault.Secret, error) {
- return nil, fmt.Errorf("fail")
+ return nil, errors.New("fail")
}
}
func ExpectDeleteWithContextNoCall() DeleteWithContextFn {
return func(ctx context.Context, path string) (*vault.Secret, error) {
- return nil, fmt.Errorf("fail")
+ return nil, errors.New("fail")
}
}
func WriteChangingReadContext(secret map[string]any, l Logical) WriteWithContextFn {
@@ -188,11 +189,15 @@ func NewTokenFn(v string) MockTokenFn {
}
func NewClearTokenFn() MockClearTokenFn {
- return func() {}
+ return func() {
+ // no-op
+ }
}
func NewAddHeaderFn() MockAddHeaderFn {
- return func(key, value string) {}
+ return func(key, value string) {
+ // no header
+ }
}
type VaultClient struct {
diff --git a/pkg/provider/vault/provider.go b/pkg/provider/vault/provider.go
index df97f6e0636..169199344a1 100644
--- a/pkg/provider/vault/provider.go
+++ b/pkg/provider/vault/provider.go
@@ -43,13 +43,11 @@ var (
)
const (
- errVaultStore = "received invalid Vault SecretStore resource: %w"
- errVaultClient = "cannot setup new vault client: %w"
- errVaultCert = "cannot set Vault CA certificate: %w"
- errConfigMapFmt = "cannot find config map data for key: %q"
- errClientTLSAuth = "error from Client TLS Auth: %q"
- errUnknownCAProvider = "unknown caProvider type given"
- errCANamespace = "cannot read secret for CAProvider due to missing namespace on kind ClusterSecretStore"
+ errVaultStore = "received invalid Vault SecretStore resource: %w"
+ errVaultClient = "cannot setup new vault client: %w"
+ errVaultCert = "cannot set Vault CA certificate: %w"
+ errClientTLSAuth = "error from Client TLS Auth: %q"
+ errCANamespace = "missing namespace on caProvider secret"
)
type Provider struct {
@@ -98,8 +96,8 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore,
return p.newClient(ctx, store, kube, clientset.CoreV1(), namespace)
}
-func (p *Provider) NewGeneratorClient(ctx context.Context, kube kclient.Client, corev1 typedcorev1.CoreV1Interface, vaultSpec *esv1beta1.VaultProvider, namespace string) (util.Client, error) {
- vStore, cfg, err := p.prepareConfig(ctx, kube, corev1, vaultSpec, nil, namespace, resolvers.EmptyStoreKind)
+func (p *Provider) NewGeneratorClient(ctx context.Context, kube kclient.Client, corev1 typedcorev1.CoreV1Interface, vaultSpec *esv1beta1.VaultProvider, namespace string, retrySettings *esv1beta1.SecretStoreRetrySettings) (util.Client, error) {
+ vStore, cfg, err := p.prepareConfig(ctx, kube, corev1, vaultSpec, retrySettings, namespace, resolvers.EmptyStoreKind)
if err != nil {
return nil, err
}
@@ -149,9 +147,16 @@ func (p *Provider) initClient(ctx context.Context, c *client, client util.Client
client.SetNamespace(*vaultSpec.Namespace)
}
+ if vaultSpec.Headers != nil {
+ for hKey, hValue := range vaultSpec.Headers {
+ client.AddHeader(hKey, hValue)
+ }
+ }
+
if vaultSpec.ReadYourWrites && vaultSpec.ForwardInconsistent {
client.AddHeader("X-Vault-Inconsistent", "forward-active-node")
}
+
c.client = client
c.auth = client.Auth()
c.logical = client.Logical()
diff --git a/pkg/provider/vault/provider_test.go b/pkg/provider/vault/provider_test.go
index 709516780fe..b6061beeab7 100644
--- a/pkg/provider/vault/provider_test.go
+++ b/pkg/provider/vault/provider_test.go
@@ -306,7 +306,7 @@ MIIFkTCCA3mgAwIBAgIUBEUg3m/WqAsWHG4Q/II3IePFfuowDQYJKoZIhvcNAQELBQAwWDELMAkGA1UE
}),
},
want: want{
- err: fmt.Errorf(errVaultCert, errors.New("failed to parse certificates from CertPool")),
+ err: fmt.Errorf("failed to decode ca bundle: %w", errors.New("failed to parse the new certificate, not valid pem data")),
},
},
"VaultAuthFormatError": {
@@ -419,7 +419,7 @@ MIIFkTCCA3mgAwIBAgIUBEUg3m/WqAsWHG4Q/II3IePFfuowDQYJKoZIhvcNAQELBQAwWDELMAkGA1UE
newClientFunc: fake.ClientWithLoginMock,
},
want: want{
- err: fmt.Errorf(errVaultCert, errors.New(`cannot find secret data for key: "cert"`)),
+ err: fmt.Errorf("failed to get cert from secret: %w", fmt.Errorf("failed to resolve secret key ref: %w", errors.New("cannot find secret data for key: \"cert\""))),
},
},
"SuccessfulVaultStoreWithIamAuthSecret": {
@@ -491,7 +491,7 @@ MIIFkTCCA3mgAwIBAgIUBEUg3m/WqAsWHG4Q/II3IePFfuowDQYJKoZIhvcNAQELBQAwWDELMAkGA1UE
newClientFunc: fake.ClientWithLoginMock,
},
want: want{
- err: fmt.Errorf(errConfigMapFmt, "cert"),
+ err: fmt.Errorf("failed to get cert from configmap: %w", errors.New("failed to get caProvider configMap vault-cert -> cert")),
},
},
"GetCertificateFormatError": {
@@ -506,7 +506,7 @@ MIIFkTCCA3mgAwIBAgIUBEUg3m/WqAsWHG4Q/II3IePFfuowDQYJKoZIhvcNAQELBQAwWDELMAkGA1UE
},
Data: map[string][]byte{
tlsKey: secretClientKey,
- tlsCrt: []byte("cert with mistak"),
+ tlsCrt: []byte("cert with mistake"),
},
}).Build(),
newClientFunc: fake.ClientWithLoginMock,
diff --git a/pkg/provider/vault/validate.go b/pkg/provider/vault/validate.go
index b21fee5aac2..8411fccfcd8 100644
--- a/pkg/provider/vault/validate.go
+++ b/pkg/provider/vault/validate.go
@@ -49,18 +49,18 @@ const (
func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnings, error) {
if store == nil {
- return nil, fmt.Errorf(errInvalidStore)
+ return nil, errors.New(errInvalidStore)
}
spc := store.GetSpec()
if spc == nil {
- return nil, fmt.Errorf(errInvalidStoreSpec)
+ return nil, errors.New(errInvalidStoreSpec)
}
if spc.Provider == nil {
- return nil, fmt.Errorf(errInvalidStoreProv)
+ return nil, errors.New(errInvalidStoreProv)
}
vaultProvider := spc.Provider.Vault
if vaultProvider == nil {
- return nil, fmt.Errorf(errInvalidVaultProv)
+ return nil, errors.New(errInvalidVaultProv)
}
if vaultProvider.Auth.AppRole != nil {
// check SecretRef for valid configuration
@@ -75,7 +75,7 @@ func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnin
return nil, fmt.Errorf(errInvalidAppRoleRef, err)
}
} else { // we ran out of ways to get RoleID. return an appropriate error
- return nil, fmt.Errorf(errInvalidAppRoleID)
+ return nil, errors.New(errInvalidAppRoleID)
}
}
}
@@ -97,7 +97,7 @@ func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnin
return nil, fmt.Errorf(errInvalidJwtK8sSA, err)
}
} else {
- return nil, fmt.Errorf(errJwtNoTokenSource)
+ return nil, errors.New(errJwtNoTokenSource)
}
}
if vaultProvider.Auth.Kubernetes != nil {
diff --git a/pkg/provider/webhook/webhook.go b/pkg/provider/webhook/webhook.go
index 510f229b06b..5f7f5257255 100644
--- a/pkg/provider/webhook/webhook.go
+++ b/pkg/provider/webhook/webhook.go
@@ -17,6 +17,7 @@ package webhook
import (
"context"
"encoding/json"
+ "errors"
"fmt"
"strconv"
"time"
@@ -60,16 +61,18 @@ func (p *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
return esv1beta1.SecretStoreReadOnly
}
-func (p *Provider) NewClient(_ context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
+func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
wh := webhook.Webhook{
Kube: kube,
Namespace: namespace,
+ StoreKind: store.GetObjectKind().GroupVersionKind().Kind,
}
whClient := &WebHook{
store: store,
wh: wh,
storeKind: store.GetObjectKind().GroupVersionKind().Kind,
}
+ whClient.wh.EnforceLabels = true
if whClient.storeKind == esv1beta1.ClusterSecretStoreKind {
whClient.wh.ClusterScoped = true
}
@@ -79,7 +82,7 @@ func (p *Provider) NewClient(_ context.Context, store esv1beta1.GenericStore, ku
}
whClient.url = provider.URL
- whClient.wh.HTTP, err = whClient.wh.GetHTTPClient(provider)
+ whClient.wh.HTTP, err = whClient.wh.GetHTTPClient(ctx, provider)
if err != nil {
return nil, err
}
@@ -93,7 +96,7 @@ func (p *Provider) ValidateStore(_ esv1beta1.GenericStore) (admission.Warnings,
func getProvider(store esv1beta1.GenericStore) (*webhook.Spec, error) {
spc := store.GetSpec()
if spc == nil || spc.Provider == nil || spc.Provider.Webhook == nil {
- return nil, fmt.Errorf("missing store provider webhook")
+ return nil, errors.New("missing store provider webhook")
}
out := webhook.Spec{}
d, err := json.Marshal(spc.Provider.Webhook)
@@ -105,22 +108,22 @@ func getProvider(store esv1beta1.GenericStore) (*webhook.Spec, error) {
}
func (w *WebHook) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
- return fmt.Errorf(errNotImplemented)
+ return errors.New(errNotImplemented)
}
func (w *WebHook) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf(errNotImplemented)
+ return false, errors.New(errNotImplemented)
}
-// Not Implemented PushSecret.
+// PushSecret not implement.
func (w *WebHook) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
- return fmt.Errorf(errNotImplemented)
+ return errors.New(errNotImplemented)
}
-// Empty GetAllSecrets.
+// GetAllSecrets Empty .
func (w *WebHook) GetAllSecrets(_ context.Context, _ esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
// TO be implemented
- return nil, fmt.Errorf(errNotImplemented)
+ return nil, errors.New(errNotImplemented)
}
func (w *WebHook) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
@@ -133,7 +136,7 @@ func (w *WebHook) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDat
return nil, err
}
// Only parse as json if we have a jsonpath set
- data, err := w.wh.GetTemplateData(ctx, &ref, provider.Secrets)
+ data, err := w.wh.GetTemplateData(ctx, &ref, provider.Secrets, false)
if err != nil {
return nil, err
}
@@ -177,7 +180,7 @@ func extractSecretData(jsondata any) ([]byte, error) {
// in case we see a []something we pick the first element and return it
case []any:
if len(val) == 0 {
- return nil, fmt.Errorf("filter worked but didn't get any result")
+ return nil, errors.New("filter worked but didn't get any result")
}
return extractSecretData(val[0])
diff --git a/pkg/provider/webhook/webhook_test.go b/pkg/provider/webhook/webhook_test.go
index d6a5cd678fd..2cd8c5be4a0 100644
--- a/pkg/provider/webhook/webhook_test.go
+++ b/pkg/provider/webhook/webhook_test.go
@@ -51,6 +51,7 @@ type args struct {
type want struct {
Path string `json:"path,omitempty"`
+ Body string `json:"body,omitempty"`
Err string `json:"err,omitempty"`
Result string `json:"result,omitempty"`
ResultMap map[string]string `json:"resultmap,omitempty"`
@@ -299,6 +300,43 @@ want:
path: /api/getsecret?id=testkey&version=1
err: ''
result: "RE/DACTED=="
+---
+case: good json with mixed fields and jsonpath filter
+args:
+ url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
+ key: testkey
+ version: 1
+ jsonpath: $.result.thesecret
+ response: '{"result":{"thesecret":"secret-value","alsosecret":"another-value", "id": 1234, "weight": 1.5}}'
+want:
+ path: /api/getsecret?id=testkey&version=1
+ err: ''
+ result: secret-value
+---
+case: good json with mixed fields to map
+args:
+ url: /api/getsecret?id={{ .remoteRef.key }}&version={{ .remoteRef.version }}
+ key: testkey
+ version: 1
+ jsonpath: $.result
+ response: '{"result":{"thesecret":"secret-value","alsosecret":"another-value", "id": 1234, "weight": 1.5}}'
+want:
+ path: /api/getsecret?id=testkey&version=1
+ err: ''
+ resultmap:
+ thesecret: secret-value
+ alsosecret: another-value
+ id: 1234
+ weight: 1.5
+---
+case: only url encoding for url templates
+args:
+ url: /api/getsecrets?folder={{ .remoteRef.key }}
+ body: '{"folder": "{{ .remoteRef.key }}"}'
+ key: /myapp/secrets
+want:
+ path: /api/getsecrets?folder=%2Fmyapp%2Fsecrets
+ body: '{"folder": "/myapp/secrets"}'
`
func TestWebhookGetSecret(t *testing.T) {
@@ -321,6 +359,12 @@ func testCaseServer(tc testCase, t *testing.T) *httptest.Server {
if tc.Want.Path != "" && req.URL.String() != tc.Want.Path {
t.Errorf("%s: unexpected api path: %s, expected %s", tc.Case, req.URL.String(), tc.Want.Path)
}
+ if tc.Want.Body != "" {
+ b, _ := io.ReadAll(req.Body)
+ if string(b) != tc.Want.Body {
+ t.Errorf("%s: unexpected body: %s, expected %s", tc.Case, string(b), tc.Want.Body)
+ }
+ }
if tc.Args.StatusCode != 0 {
rw.WriteHeader(tc.Args.StatusCode)
}
diff --git a/pkg/provider/yandex/certificatemanager/certificatemanager.go b/pkg/provider/yandex/certificatemanager/certificatemanager.go
index bd9856a48cd..274564456f0 100644
--- a/pkg/provider/yandex/certificatemanager/certificatemanager.go
+++ b/pkg/provider/yandex/certificatemanager/certificatemanager.go
@@ -16,7 +16,7 @@ package certificatemanager
import (
"context"
- "fmt"
+ "errors"
"time"
"github.com/yandex-cloud/go-sdk/iamkey"
@@ -34,12 +34,12 @@ var log = ctrl.Log.WithName("provider").WithName("yandex").WithName("certificate
func adaptInput(store esv1beta1.GenericStore) (*common.SecretsClientInput, error) {
storeSpec := store.GetSpec()
if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.YandexCertificateManager == nil {
- return nil, fmt.Errorf("received invalid Yandex Certificate Manager SecretStore resource")
+ return nil, errors.New("received invalid Yandex Certificate Manager SecretStore resource")
}
storeSpecYandexCertificateManager := storeSpec.Provider.YandexCertificateManager
if storeSpecYandexCertificateManager.Auth.AuthorizedKey.Name == "" {
- return nil, fmt.Errorf("invalid Yandex Certificate Manager SecretStore resource: missing AuthorizedKey Name")
+ return nil, errors.New("invalid Yandex Certificate Manager SecretStore resource: missing AuthorizedKey Name")
}
var caCertificate *esmeta.SecretKeySelector
diff --git a/pkg/provider/yandex/certificatemanager/client/fakeclient.go b/pkg/provider/yandex/certificatemanager/client/fakeclient.go
index c73fb6139b1..230cca97c6c 100644
--- a/pkg/provider/yandex/certificatemanager/client/fakeclient.go
+++ b/pkg/provider/yandex/certificatemanager/client/fakeclient.go
@@ -16,7 +16,7 @@ package client
import (
"context"
- "fmt"
+ "errors"
"time"
"github.com/google/go-cmp/cmp"
@@ -117,20 +117,20 @@ func (s *FakeCertificateManagerServer) NewIamToken(authorizedKey *iamkey.Key) *c
func (s *FakeCertificateManagerServer) getCertificateContent(iamToken, certificateID, versionID string) (*api.GetCertificateContentResponse, error) {
if _, ok := s.certificateMap[certificateKey{certificateID}]; !ok {
- return nil, fmt.Errorf("certificate not found")
+ return nil, errors.New("certificate not found")
}
if _, ok := s.versionMap[versionKey{certificateID, versionID}]; !ok {
- return nil, fmt.Errorf("version not found")
+ return nil, errors.New("version not found")
}
if _, ok := s.tokenMap[tokenKey{iamToken}]; !ok {
- return nil, fmt.Errorf("unauthenticated")
+ return nil, errors.New("unauthenticated")
}
if s.tokenMap[tokenKey{iamToken}].expiresAt.Before(s.clock.CurrentTime()) {
- return nil, fmt.Errorf("iam token expired")
+ return nil, errors.New("iam token expired")
}
if !cmp.Equal(s.tokenMap[tokenKey{iamToken}].authorizedKey, s.certificateMap[certificateKey{certificateID}].expectedAuthorizedKey, cmpopts.IgnoreUnexported(iamkey.Key{})) {
- return nil, fmt.Errorf("permission denied")
+ return nil, errors.New("permission denied")
}
return s.versionMap[versionKey{certificateID, versionID}].content, nil
diff --git a/pkg/provider/yandex/common/sdk.go b/pkg/provider/yandex/common/sdk.go
index 6720a6d0863..2d0b1532ba7 100644
--- a/pkg/provider/yandex/common/sdk.go
+++ b/pkg/provider/yandex/common/sdk.go
@@ -57,7 +57,16 @@ func NewGrpcConnection(
return nil, err
}
- return grpc.NewClient(serviceAPIEndpoint.Address,
+ // Until gRPC proposal A61 is implemented in grpc-go, default gRPC name resolver (dns)
+ // is incompatible with dualstack backends, and YC API backends are dualstack.
+ // However, if passthrough resolver is used instead, grpc-go won't do any name resolution
+ // and will pass the endpoint to net.Dial as-is, which would utilize happy-eyeballs
+ // support in Go's net package.
+ // So we explicitly set gRPC resolver to `passthrough` to match `ycsdk`s behavior,
+ // which uses `passthrough` resolver implicitly by using deprecated grpc.DialContext
+ // instead of grpc.NewClient used here
+ target := "passthrough:///" + serviceAPIEndpoint.Address
+ return grpc.NewClient(target,
grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)),
grpc.WithKeepaliveParams(keepalive.ClientParameters{
Time: time.Second * 30,
diff --git a/pkg/provider/yandex/common/secretsclient.go b/pkg/provider/yandex/common/secretsclient.go
index 95f454735ad..f21cf9c1f5b 100644
--- a/pkg/provider/yandex/common/secretsclient.go
+++ b/pkg/provider/yandex/common/secretsclient.go
@@ -16,7 +16,7 @@ package common
import (
"context"
- "fmt"
+ "errors"
corev1 "k8s.io/api/core/v1"
@@ -42,15 +42,15 @@ func (c *yandexCloudSecretsClient) GetSecret(ctx context.Context, ref esv1beta1.
}
func (c *yandexCloudSecretsClient) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error {
- return fmt.Errorf(errNotImplemented)
+ return errors.New(errNotImplemented)
}
func (c *yandexCloudSecretsClient) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRef) (bool, error) {
- return false, fmt.Errorf(errNotImplemented)
+ return false, errors.New(errNotImplemented)
}
func (c *yandexCloudSecretsClient) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
- return fmt.Errorf(errNotImplemented)
+ return errors.New(errNotImplemented)
}
func (c *yandexCloudSecretsClient) Validate() (esv1beta1.ValidationResult, error) {
@@ -63,7 +63,7 @@ func (c *yandexCloudSecretsClient) GetSecretMap(ctx context.Context, ref esv1bet
func (c *yandexCloudSecretsClient) GetAllSecrets(_ context.Context, _ esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
// TO be implemented
- return nil, fmt.Errorf(errNotImplemented)
+ return nil, errors.New(errNotImplemented)
}
func (c *yandexCloudSecretsClient) Close(_ context.Context) error {
diff --git a/pkg/provider/yandex/lockbox/client/fakeclient.go b/pkg/provider/yandex/lockbox/client/fakeclient.go
index 37e21ca77b1..7e1512f35ee 100644
--- a/pkg/provider/yandex/lockbox/client/fakeclient.go
+++ b/pkg/provider/yandex/lockbox/client/fakeclient.go
@@ -16,7 +16,7 @@ package client
import (
"context"
- "fmt"
+ "errors"
"time"
"github.com/google/go-cmp/cmp"
@@ -117,20 +117,20 @@ func (s *FakeLockboxServer) NewIamToken(authorizedKey *iamkey.Key) *common.IamTo
func (s *FakeLockboxServer) getEntries(iamToken, secretID, versionID string) ([]*api.Payload_Entry, error) {
if _, ok := s.secretMap[secretKey{secretID}]; !ok {
- return nil, fmt.Errorf("secret not found")
+ return nil, errors.New("secret not found")
}
if _, ok := s.versionMap[versionKey{secretID, versionID}]; !ok {
- return nil, fmt.Errorf("version not found")
+ return nil, errors.New("version not found")
}
if _, ok := s.tokenMap[tokenKey{iamToken}]; !ok {
- return nil, fmt.Errorf("unauthenticated")
+ return nil, errors.New("unauthenticated")
}
if s.tokenMap[tokenKey{iamToken}].expiresAt.Before(s.clock.CurrentTime()) {
- return nil, fmt.Errorf("iam token expired")
+ return nil, errors.New("iam token expired")
}
if !cmp.Equal(s.tokenMap[tokenKey{iamToken}].authorizedKey, s.secretMap[secretKey{secretID}].expectedAuthorizedKey, cmpopts.IgnoreUnexported(iamkey.Key{})) {
- return nil, fmt.Errorf("permission denied")
+ return nil, errors.New("permission denied")
}
return s.versionMap[versionKey{secretID, versionID}].entries, nil
diff --git a/pkg/provider/yandex/lockbox/lockbox.go b/pkg/provider/yandex/lockbox/lockbox.go
index b5ad2f71e98..48b548250e9 100644
--- a/pkg/provider/yandex/lockbox/lockbox.go
+++ b/pkg/provider/yandex/lockbox/lockbox.go
@@ -16,7 +16,7 @@ package lockbox
import (
"context"
- "fmt"
+ "errors"
"time"
"github.com/yandex-cloud/go-sdk/iamkey"
@@ -34,12 +34,12 @@ var log = ctrl.Log.WithName("provider").WithName("yandex").WithName("lockbox")
func adaptInput(store esv1beta1.GenericStore) (*common.SecretsClientInput, error) {
storeSpec := store.GetSpec()
if storeSpec == nil || storeSpec.Provider == nil || storeSpec.Provider.YandexLockbox == nil {
- return nil, fmt.Errorf("received invalid Yandex Lockbox SecretStore resource")
+ return nil, errors.New("received invalid Yandex Lockbox SecretStore resource")
}
storeSpecYandexLockbox := storeSpec.Provider.YandexLockbox
if storeSpecYandexLockbox.Auth.AuthorizedKey.Name == "" {
- return nil, fmt.Errorf("invalid Yandex Lockbox SecretStore resource: missing AuthorizedKey Name")
+ return nil, errors.New("invalid Yandex Lockbox SecretStore resource: missing AuthorizedKey Name")
}
var caCertificate *esmeta.SecretKeySelector
diff --git a/pkg/template/engine.go b/pkg/template/engine.go
index b406a962b9a..3f1c22f3108 100644
--- a/pkg/template/engine.go
+++ b/pkg/template/engine.go
@@ -14,6 +14,8 @@ limitations under the License.
package template
import (
+ "fmt"
+
corev1 "k8s.io/api/core/v1"
esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
@@ -25,13 +27,13 @@ type ExecFunc func(tpl, data map[string][]byte, scope esapi.TemplateScope, targe
func EngineForVersion(version esapi.TemplateEngineVersion) (ExecFunc, error) {
switch version {
- case esapi.TemplateEngineV1:
+ // NOTE: the version can be empty if the ExternalSecret was created with version 0.4.3 or earlier,
+ // all versions after this will default to "v1" (for v1alpha1 ES) or "v2" (for v1beta1 ES).
+ // so if we encounter an empty version, we must default to the v1 engine.
+ case esapi.TemplateEngineV1, "":
return v1.Execute, nil
case esapi.TemplateEngineV2:
return v2.Execute, nil
}
-
- // in case we run with a old v1alpha1 CRD
- // we must return v1 as default
- return v1.Execute, nil
+ return nil, fmt.Errorf("unsupported template engine version: %s", version)
}
diff --git a/pkg/template/v1/template.go b/pkg/template/v1/template.go
index 28b90d9305a..f703147b9a6 100644
--- a/pkg/template/v1/template.go
+++ b/pkg/template/v1/template.go
@@ -26,8 +26,8 @@ import (
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/youmark/pkcs8"
- "golang.org/x/crypto/pkcs12"
corev1 "k8s.io/api/core/v1"
+ "software.sslmate.com/src/go-pkcs12"
esapi "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
)
@@ -73,22 +73,35 @@ const (
)
// Execute renders the secret data as template. If an error occurs processing is stopped immediately.
-func Execute(tpl, data map[string][]byte, _ esapi.TemplateScope, _ esapi.TemplateTarget, secret *corev1.Secret) error {
- if tpl == nil {
+func Execute(tpl, data map[string][]byte, scope esapi.TemplateScope, target esapi.TemplateTarget, secret *corev1.Secret) error {
+ if len(tpl) == 0 {
return nil
}
- for k, v := range tpl {
- val, err := execute(k, string(v), data)
- if err != nil {
- return fmt.Errorf(errExecute, k, err)
+
+ if scope != "" && scope != esapi.TemplateScopeValues {
+ return fmt.Errorf("template scope %s is not supported in v1 templates, please only use Values", scope)
+ }
+
+ switch target {
+ case esapi.TemplateTargetAnnotations:
+ // Annotations are not supported in v1 templates
+ case esapi.TemplateTargetLabels:
+ // Labels are not supported in v1 templates
+ case esapi.TemplateTargetData, "":
+ for k, v := range tpl {
+ val, err := execute(k, string(v), data)
+ if err != nil {
+ return fmt.Errorf(errExecute, k, err)
+ }
+ secret.Data[k] = val
}
- secret.Data[k] = val
}
return nil
}
func execute(k, val string, data map[string][]byte) ([]byte, error) {
t, err := tpl.New(k).
+ Option("missingkey=error").
Funcs(tplFuncs).
Parse(val)
if err != nil {
diff --git a/pkg/template/v1/template_test.go b/pkg/template/v1/template_test.go
index 2d5c645c853..0a1aa41a171 100644
--- a/pkg/template/v1/template_test.go
+++ b/pkg/template/v1/template_test.go
@@ -293,6 +293,14 @@ func TestExecute(t *testing.T) {
data: map[string][]byte{},
expErr: "unable to parse template",
},
+ {
+ name: "unknown key error",
+ tpl: map[string][]byte{
+ "key": []byte(`{{ .unknown }}`),
+ },
+ data: map[string][]byte{},
+ expErr: "unable to execute template at key key",
+ },
{
name: "jwk rsa pub pem",
tpl: map[string][]byte{
diff --git a/pkg/template/v2/jwk.go b/pkg/template/v2/jwk.go
index d0ba453c917..6a64f2a4d8d 100644
--- a/pkg/template/v2/jwk.go
+++ b/pkg/template/v2/jwk.go
@@ -34,7 +34,7 @@ func jwkPublicKeyPem(jwkjson string) (string, error) {
if err != nil {
return "", err
}
- return pemEncode(string(mpk), "PUBLIC KEY")
+ return pemEncode(mpk, "PUBLIC KEY")
}
func jwkPrivateKeyPem(jwkjson string) (string, error) {
@@ -52,5 +52,5 @@ func jwkPrivateKeyPem(jwkjson string) (string, error) {
if err != nil {
return "", err
}
- return pemEncode(string(mpk), "PRIVATE KEY")
+ return pemEncode(mpk, "PRIVATE KEY")
}
diff --git a/pkg/template/v2/pem.go b/pkg/template/v2/pem.go
index 0490bc1698b..8f44ba55fbc 100644
--- a/pkg/template/v2/pem.go
+++ b/pkg/template/v2/pem.go
@@ -16,6 +16,7 @@ package template
import (
"bytes"
+ "crypto/x509"
"encoding/pem"
"errors"
"strings"
@@ -23,6 +24,10 @@ import (
const (
errJunk = "error filtering pem: found junk"
+
+ certTypeLeaf = "leaf"
+ certTypeIntermediate = "intermediate"
+ certTypeRoot = "root"
)
func filterPEM(pemType, input string) (string, error) {
@@ -56,8 +61,50 @@ func filterPEM(pemType, input string) (string, error) {
return string(blocks), nil
}
-func pemEncode(thing, kind string) (string, error) {
+func filterCertChain(certType, input string) (string, error) {
+ ordered, err := fetchX509CertChains([]byte(input))
+ if err != nil {
+ return "", err
+ }
+
+ switch certType {
+ case certTypeLeaf:
+ cert := ordered[0]
+ if cert.AuthorityKeyId != nil && !bytes.Equal(cert.AuthorityKeyId, cert.SubjectKeyId) {
+ return pemEncode(ordered[0].Raw, pemTypeCertificate)
+ }
+ case certTypeIntermediate:
+ if len(ordered) < 2 {
+ return "", nil
+ }
+ var pemData []byte
+ for _, cert := range ordered[1:] {
+ if isRootCertificate(cert) {
+ break
+ }
+ b := &pem.Block{
+ Type: pemTypeCertificate,
+ Bytes: cert.Raw,
+ }
+ pemData = append(pemData, pem.EncodeToMemory(b)...)
+ }
+ return string(pemData), nil
+ case certTypeRoot:
+ cert := ordered[len(ordered)-1]
+ if isRootCertificate(cert) {
+ return pemEncode(cert.Raw, pemTypeCertificate)
+ }
+ }
+
+ return "", nil
+}
+
+func isRootCertificate(cert *x509.Certificate) bool {
+ return cert.AuthorityKeyId == nil || bytes.Equal(cert.AuthorityKeyId, cert.SubjectKeyId)
+}
+
+func pemEncode(thing []byte, kind string) (string, error) {
buf := bytes.NewBuffer(nil)
- err := pem.Encode(buf, &pem.Block{Type: kind, Bytes: []byte(thing)})
+ err := pem.Encode(buf, &pem.Block{Type: kind, Bytes: thing})
return buf.String(), err
}
diff --git a/pkg/template/v2/pem_chain.go b/pkg/template/v2/pem_chain.go
index b831c7367db..708374d19b1 100644
--- a/pkg/template/v2/pem_chain.go
+++ b/pkg/template/v2/pem_chain.go
@@ -31,7 +31,7 @@ import (
"bytes"
"crypto/x509"
"encoding/pem"
- "fmt"
+ "errors"
)
const (
@@ -47,9 +47,8 @@ type node struct {
isParent bool
}
-func fetchCertChains(data []byte) ([]byte, error) {
+func fetchX509CertChains(data []byte) ([]*x509.Certificate, error) {
var newCertChain []*x509.Certificate
- var pemData []byte
nodes, err := pemToNodes(data)
if err != nil {
return nil, err
@@ -80,7 +79,7 @@ func fetchCertChains(data []byte) ([]byte, error) {
for i := range nodes {
if !nodes[i].isParent {
if foundLeaf {
- return nil, fmt.Errorf(errFoundDisjunctCert)
+ return nil, errors.New(errFoundDisjunctCert)
}
// this is the leaf node as it's not a parent for any other node
leaf = nodes[i]
@@ -89,7 +88,7 @@ func fetchCertChains(data []byte) ([]byte, error) {
}
if leaf == nil {
- return nil, fmt.Errorf(errNoLeafFound)
+ return nil, errors.New(errNoLeafFound)
}
processedNodes := 0
@@ -98,12 +97,20 @@ func fetchCertChains(data []byte) ([]byte, error) {
processedNodes++
// ensure we aren't stuck in a cyclic loop
if processedNodes > len(nodes) {
- return pemData, fmt.Errorf(errChainCycle)
+ return nil, errors.New(errChainCycle)
}
newCertChain = append(newCertChain, leaf.cert)
leaf = leaf.parent
}
+ return newCertChain, nil
+}
+func fetchCertChains(data []byte) ([]byte, error) {
+ var pemData []byte
+ newCertChain, err := fetchX509CertChains(data)
+ if err != nil {
+ return nil, err
+ }
for _, cert := range newCertChain {
b := &pem.Block{
Type: pemTypeCertificate,
@@ -131,7 +138,7 @@ func pemToNodes(data []byte) ([]*node, error) {
// this should not be the case because ParseCertificate should return a non nil
// certificate when there is no error.
if cert == nil {
- return nil, fmt.Errorf(errNilCert)
+ return nil, errors.New(errNilCert)
}
nodes = append(nodes, &node{
cert: cert,
diff --git a/pkg/template/v2/pem_test.go b/pkg/template/v2/pem_test.go
index 3963395426a..ce04efa44c0 100644
--- a/pkg/template/v2/pem_test.go
+++ b/pkg/template/v2/pem_test.go
@@ -14,7 +14,10 @@ limitations under the License.
package template
-import "testing"
+import (
+ "os"
+ "testing"
+)
const (
certData = `-----BEGIN CERTIFICATE-----
@@ -179,3 +182,193 @@ func TestFilterPEM(t *testing.T) {
})
}
}
+
+type filterCertChainTestArgs struct {
+ input []string
+ certType string
+}
+
+type filterCertChainTest struct {
+ name string
+ args filterCertChainTestArgs
+ want string
+ wantErr bool
+}
+
+func TestFilterCertChain(t *testing.T) {
+ const (
+ leafCertPath = "_testdata/foo.crt"
+ intermediateCertPath = "_testdata/intermediate-ca.crt"
+ rootCertPath = "_testdata/root-ca.crt"
+ rootKeyPath = "_testdata/root-ca.key"
+ )
+ tests := []filterCertChainTest{
+ {
+ name: "extract leaf cert / empty cert chain",
+ args: filterCertChainTestArgs{
+ input: []string{},
+ certType: certTypeLeaf,
+ },
+ wantErr: true,
+ },
+ {
+ name: "extract leaf cert / cert chain with pkey",
+ args: filterCertChainTestArgs{
+ input: []string{
+ leafCertPath,
+ rootKeyPath,
+ },
+ certType: certTypeLeaf,
+ },
+ wantErr: true,
+ },
+ {
+ name: "extract leaf cert / leaf cert only",
+ args: filterCertChainTestArgs{
+ input: []string{
+ leafCertPath,
+ },
+ certType: certTypeLeaf,
+ },
+ want: leafCertPath,
+ },
+ {
+ name: "extract leaf cert / cert chain without root",
+ args: filterCertChainTestArgs{
+ input: []string{
+ leafCertPath,
+ intermediateCertPath,
+ },
+ certType: certTypeLeaf,
+ },
+ want: leafCertPath,
+ },
+ {
+ name: "extract leaf cert / root cert only",
+ args: filterCertChainTestArgs{
+ input: []string{
+ rootCertPath,
+ },
+ certType: certTypeLeaf,
+ },
+ want: "",
+ },
+ {
+ name: "extract leaf cert / full cert chain",
+ args: filterCertChainTestArgs{
+ input: []string{
+ leafCertPath,
+ intermediateCertPath,
+ rootCertPath,
+ },
+ certType: certTypeLeaf,
+ },
+ want: leafCertPath,
+ },
+ {
+ name: "extract intermediate cert / leaf cert only",
+ args: filterCertChainTestArgs{
+ input: []string{
+ leafCertPath,
+ },
+ certType: certTypeIntermediate,
+ },
+ want: "",
+ },
+ {
+ name: "extract intermediate cert / cert chain without root",
+ args: filterCertChainTestArgs{
+ input: []string{
+ leafCertPath,
+ intermediateCertPath,
+ },
+ certType: certTypeIntermediate,
+ },
+ want: intermediateCertPath,
+ },
+ {
+ name: "extract intermediate cert / full cert chain",
+ args: filterCertChainTestArgs{
+ input: []string{
+ leafCertPath,
+ intermediateCertPath,
+ rootCertPath,
+ },
+ certType: certTypeIntermediate,
+ },
+ want: intermediateCertPath,
+ },
+ {
+ name: "extract root cert / leaf cert only",
+ args: filterCertChainTestArgs{
+ input: []string{
+ leafCertPath,
+ },
+ certType: certTypeRoot,
+ },
+ want: "",
+ },
+ {
+ name: "extract root cert / root cert only",
+ args: filterCertChainTestArgs{
+ input: []string{
+ rootCertPath,
+ },
+ certType: certTypeRoot,
+ },
+ want: rootCertPath,
+ },
+ {
+ name: "extract root cert / full cert chain",
+ args: filterCertChainTestArgs{
+ input: []string{
+ leafCertPath,
+ intermediateCertPath,
+ rootCertPath,
+ },
+ certType: certTypeRoot,
+ },
+ want: rootCertPath,
+ },
+ }
+ for _, tt := range tests {
+ runFilterCertChainTest(t, tt)
+ }
+}
+
+func runFilterCertChainTest(t *testing.T, tt filterCertChainTest) {
+ t.Run(tt.name, func(t *testing.T) {
+ chainIn, err := readCertificates(tt.args.input)
+ if err != nil {
+ t.Error(err)
+ }
+ var expOut []byte
+ if tt.want != "" {
+ var err error
+ expOut, err = os.ReadFile(tt.want)
+ if err != nil {
+ t.Error(err)
+ }
+ }
+ got, err := filterCertChain(tt.args.certType, string(chainIn))
+ if (err != nil) != tt.wantErr {
+ t.Errorf("filterCertChain() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if got != string(expOut) {
+ t.Errorf("filterCertChain() = %v, want %v", got, string(expOut))
+ }
+ })
+}
+
+func readCertificates(certFiles []string) ([]byte, error) {
+ var certificates []byte
+ for _, f := range certFiles {
+ c, err := os.ReadFile(f)
+ if err != nil {
+ return nil, err
+ }
+ certificates = append(certificates, c...)
+ }
+ return certificates, nil
+}
diff --git a/pkg/template/v2/pkcs12.go b/pkg/template/v2/pkcs12.go
index 89a8d3f9e75..689f9a9c4aa 100644
--- a/pkg/template/v2/pkcs12.go
+++ b/pkg/template/v2/pkcs12.go
@@ -19,43 +19,31 @@ import (
"crypto/x509"
"encoding/base64"
"encoding/pem"
+ "errors"
"fmt"
- "golang.org/x/crypto/pkcs12"
gopkcs12 "software.sslmate.com/src/go-pkcs12"
)
func pkcs12keyPass(pass, input string) (string, error) {
- blocks, err := pkcs12.ToPEM([]byte(input), pass)
+ privateKey, _, _, err := gopkcs12.DecodeChain([]byte(input), pass)
if err != nil {
return "", fmt.Errorf(errDecodePKCS12WithPass, err)
}
- var pemData []byte
- for _, block := range blocks {
- // remove bag attributes like localKeyID, friendlyName
- block.Headers = nil
- if block.Type == pemTypeCertificate {
- continue
- }
- key, err := parsePrivateKey(block.Bytes)
- if err != nil {
- return "", err
- }
- // we use pkcs8 because it supports more key types (ecdsa, ed25519), not just RSA
- block.Bytes, err = x509.MarshalPKCS8PrivateKey(key)
- if err != nil {
- return "", err
- }
- // report error if encode fails
- var buf bytes.Buffer
- if err := pem.Encode(&buf, block); err != nil {
- return "", err
- }
- pemData = append(pemData, buf.Bytes()...)
+ marshalPrivateKey, err := x509.MarshalPKCS8PrivateKey(privateKey)
+ if err != nil {
+ return "", err
}
- return string(pemData), nil
+ var buf bytes.Buffer
+ if err := pem.Encode(&buf, &pem.Block{
+ Type: pemTypeKey,
+ Bytes: marshalPrivateKey,
+ }); err != nil {
+ return "", err
+ }
+ return buf.String(), nil
}
func parsePrivateKey(block []byte) (any, error) {
@@ -68,7 +56,7 @@ func parsePrivateKey(block []byte) (any, error) {
if k, err := x509.ParseECPrivateKey(block); err == nil {
return k, nil
}
- return nil, fmt.Errorf(errParsePrivKey)
+ return nil, errors.New(errParsePrivKey)
}
func pkcs12key(input string) (string, error) {
@@ -76,21 +64,28 @@ func pkcs12key(input string) (string, error) {
}
func pkcs12certPass(pass, input string) (string, error) {
- blocks, err := pkcs12.ToPEM([]byte(input), pass)
+ _, certificate, caCerts, err := gopkcs12.DecodeChain([]byte(input), pass)
if err != nil {
return "", fmt.Errorf(errDecodeCertWithPass, err)
}
var pemData []byte
- for _, block := range blocks {
- if block.Type != pemTypeCertificate {
- continue
- }
- // remove bag attributes like localKeyID, friendlyName
- block.Headers = nil
- // report error if encode fails
+ var buf bytes.Buffer
+ if err := pem.Encode(&buf, &pem.Block{
+ Type: pemTypeCertificate,
+ Bytes: certificate.Raw,
+ }); err != nil {
+ return "", err
+ }
+
+ pemData = append(pemData, buf.Bytes()...)
+
+ for _, ca := range caCerts {
var buf bytes.Buffer
- if err := pem.Encode(&buf, block); err != nil {
+ if err := pem.Encode(&buf, &pem.Block{
+ Type: pemTypeCertificate,
+ Bytes: ca.Raw,
+ }); err != nil {
return "", err
}
pemData = append(pemData, buf.Bytes()...)
@@ -117,19 +112,51 @@ func pemToPkcs12(cert, key string) (string, error) {
func pemToPkcs12Pass(cert, key, pass string) (string, error) {
certPem, _ := pem.Decode([]byte(cert))
- keyPem, _ := pem.Decode([]byte(key))
parsedCert, err := x509.ParseCertificate(certPem.Bytes)
if err != nil {
return "", err
}
+ return certsToPkcs12(parsedCert, key, nil, pass)
+}
+
+func fullPemToPkcs12(cert, key string) (string, error) {
+ return fullPemToPkcs12Pass(cert, key, "")
+}
+
+func fullPemToPkcs12Pass(cert, key, pass string) (string, error) {
+ certPem, rest := pem.Decode([]byte(cert))
+
+ parsedCert, err := x509.ParseCertificate(certPem.Bytes)
+ if err != nil {
+ return "", err
+ }
+
+ caCerts := make([]*x509.Certificate, 0)
+ for len(rest) > 0 {
+ caPem, restBytes := pem.Decode(rest)
+ rest = restBytes
+
+ caCert, err := x509.ParseCertificate(caPem.Bytes)
+ if err != nil {
+ return "", err
+ }
+
+ caCerts = append(caCerts, caCert)
+ }
+
+ return certsToPkcs12(parsedCert, key, caCerts, pass)
+}
+
+func certsToPkcs12(cert *x509.Certificate, key string, caCerts []*x509.Certificate, password string) (string, error) {
+ keyPem, _ := pem.Decode([]byte(key))
parsedKey, err := parsePrivateKey(keyPem.Bytes)
if err != nil {
return "", err
}
- pfx, err := gopkcs12.Modern.Encode(parsedKey, parsedCert, nil, pass)
+ pfx, err := gopkcs12.Modern.Encode(parsedKey, cert, caCerts, password)
if err != nil {
return "", err
}
diff --git a/pkg/template/v2/pkcs12_test.go b/pkg/template/v2/pkcs12_test.go
new file mode 100644
index 00000000000..3d44816a291
--- /dev/null
+++ b/pkg/template/v2/pkcs12_test.go
@@ -0,0 +1,63 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package template
+
+import (
+ "testing"
+
+ gopkcs12 "software.sslmate.com/src/go-pkcs12"
+)
+
+const (
+ expectedPfx string = "MIIKAQIBAzCCCbEGCSqGSIb3DQEHAaCCCaIEggmeMIIJmjCCBAgGCSqGSIb3DQEHBqCCA/kwggP1AgEAMIID7gYJKoZIhvcNAQcBMF0GCSqGSIb3DQEFDTBQMC8GCSqGSIb3DQEFDDAiBBAiMDEyMzQ1Njc4OSIAAAAAAgIIADAKBggqhkiG9w0CCTAdBglghkgBZQMEASoEECIwMTIzNDU2Nzg5IgAAAACAggOA3PfruBHwXSLGGGcI6woqtR/A8eBKsYZt0r6kpLf9w3aC9Idic93fY4lPhf2v3d/tin7CZwboeiT6H4uM5s+TrYWWbZIcHh67femCfMjbwnNSkOuzZOFmprJUUEtdZhP8ifDh0NcqT//0KmwRIYaV4JPxmGFfblRFSaLg0daKfCFKIE7onJMa/uy0i4IREzserCNNSZezCNOdtSfWdvjKrIlCFGopjv6iO1oG1qT6JN7YWDZNzgDposmtkIqwP+tNn+40iyv56uVMXZPJHxWxQMhvo24Iq7Qtni+TE+rLmHbiC2l8/99m03YzqDRfA2iDtptqk+rmVf7bVm0nzPEUoWVC0LqQj4RDA/x5mi29OIlOYCfYNi8OJm4wlFlc49USg0URhz6fihNZdp3tcl/In2BKPyAPeLSE5DQb94WWSTlsRov1dw+G6bGD3ClsNZ8xfoBarr93auk1HP1Y3uDzvTAXjBHhMvKamm8pTn2b97qDqWvQDSk+39hh/Ey1hPqr8tdsqCZCpLrvvty9hmtREaosD5/1lNtoZ3AjdhCDlhWshMhh7HFDHTK9nV7xDpETXqv2GNW8xTI5GuPbuBZD6NHQdd1Yqff5N+bejGOD6UBVCnkgtkXOzy0frj8MQqVnCsMZQ1iwJh+nOkJD9S5quapB7q7+uOhnoP88m354uaNI7heAj3RWVKq55ohAbbCB7736bUmHJMoF83IV1fkOD7wYGZOtn3oRgYM7RLu1TPKhvk7VqoYz8MiAXZF7dwHXfO8qOsnKqqNl/yuoy//tZfLrHtktUUonZEsYpAb0FDFCSQJ3gktW6mib+P3/nK9qtu1cS+UEoaavXpvjFohD29GnMBr6AbRNWz1jjbKypWuznJZsUY/NMvyvTBxdqTN5x6h1cH1wU3nnd8MbRBZ5pAW1w63S51fRcfT8/b7SwEAO3F/6z3sfYKfNP4d3/m3HF3kxF8Qgu2fWp9SBOqXnwX7RnNWRfdyFUTjKyUWQucpiFyP3GqQgfPl5cYMgOEqpC8Am1HXGlX0jimSQGphKsQKHJNklQ+wfdltFZ4IjKukF3XVYoClosqYZGl3YjXqzCK60WMCN2Pmk09xw0XTN/8lFK17byWpNRaWLXhkgeqtmVd5jwztAq/hDczHuw+Hu5GbPoqBu6Dn+J61Cd05YCMiWcFrjff+KaBkuJeerMJEwggWKBgkqhkiG9w0BBwGgggV7BIIFdzCCBXMwggVvBgsqhkiG9w0BDAoBAqCCBTcwggUzMF0GCSqGSIb3DQEFDTBQMC8GCSqGSIb3DQEFDDAiBBAiMDEyMzQ1Njc4OSIAAAAAAgIIADAKBggqhkiG9w0CCTAdBglghkgBZQMEASoEECIwMTIzNDU2Nzg5IgAAAAAEggTQ2VJM4mr0FBkty7ied/w1aha3MoDllKVosUCocl8+tCXSBO+i5tQvybIex7gleTOnkmm2s/eFO1i3LKFzzK9/8SVmzJo2+juyUJSM3rIFmN5zKpj+q4Pto6YHpoXGoDszOpVetPPBbsPtJ8Q1tu9CgyL9Bm/GJwfzhJpc1SeywxztILwkJhF/zEkxngiKX8HGVSxVoTuKhi0Gpp2brfWBr9cRf31KHY6a4uQia5nxzr73EPy5DVH2EGcuHdXbsOIcSVGlfVk3+Fz/S6FrgzDPPk9L8DCe/lvzbou8R9TpV7dOOetAI8UT0+zu5I7YZV5gDlR0IxDRa8sgrCw6I0Bk8ABmSSC4tMersSQmVYcnAWkGDn6WQDjpEGJ6T1ScTPd0sFLysCdIo9nyh/T1//mGfNXylx8smlNtnokrlUr0x0ywGLiyRrZWD5ZVhYEmOLFJ0CkRGGR6lUpudrSAun7oQaZ9cSlmyApQ9dZAQmVjEYVcVwN0pTkqo92FJUbkmEGCgvBSIwIT/bB1w8TKjTz6iTYQmcuodAnnhzDuV+F0HqH6IolIKVZdREeRpm+GatNFGcN36FJlvze87XSsst9YiyLEgUy9Le1Pnt/wDlqrI1l9qqyIySIl3VLiY04rv/8PRJGkVD5S66xNqkY5gYuGD6DVzcrdO4RfqXiWXbiqSkicaDd80KApuRVtnvoqrvZRtRuMf18GA8FQ4yTRa2w8/CQav6QbcUecFZBglvrGl/KquHhVZrTwaSP0GEfPUs6X7fBU4BfgBFIISAzhcpHntgZ/ngZSVbJuOo7NHpCPJsLSDf+c4KsNSHQuYI9xU8t/yCFyR5alglT3CbMt976JGRevD6hedUXIr/ZI6jcrWC3JrdJJjr0pt+DiZ9sLNXhLbKb0afzBzgvOvPHDKx4Cwzf0eG8oOm0cGkIcqUBV6Uosnk+xYIQUcK9GR6HlB6kCL3wyp1uDQQEUcknkw3aLlYaOgc3O1uaDIDDD1f7SAcoY/e+3N8HYUZ2dcCVR9HV0Ycg3QwayFRJZPkkkk3GMxVgUhQaVt2SKIiHBlavzRs1Ur/oCPoUktzozyo+FEKUoVHEJRftGCATI3H8TqrtC79W/9OR7V3bk+5RgZgzK5k5l9F8nUeSiSzoByrFCtbHlZbiblZguNGVikqYG3hXe+0QHOTt1nFcmd2MeLvd3o65FGb5fTG4gNwxN2C659j92VfH6OpYFUqlQ+An2+tiJdN+JVdfsHUI5+DdXCDhQs8H9xFdNTOttuSRuwgXsuiY6zGwGErd3hY0hRNFXr2IEai+A/q7xdVdiy//vaS80wHoCSAvvSjpYNg0w0zNiFQ/2Ty+aQg4qHVazocMUlhj2XMWd1vfIpoQSWggTjhYcdIK5z1EsmTAiXK7xHvVOpLI6QVqKHrHBKhmd01Yf/QQyYpvB2/GxlmiXy7WTMFfY3IqP8w6pRQEApKmTUYpx9qVRp2l55R8hOBR9RW2OQGKm10dZRgg87qGbVwbzz3cPx/fPKoGhDu7o7qRwNrKXAbCCjV4eSxoRASFbQUQzJ/Bi6uvZgA8Z42xZXddkFcoGyJZCoi+550QSypEjoRvVy76ff1xxRF+aS9Au3DJKYHQw78018/TdT/62vFI6u92huEcxJTAjBgkqhkiG9w0BCRUxFgQUOEXV6IFYGpCSHi0MPHz4b3W0KOQwRzAvMAsGCWCGSAFlAwQCAQQgLXxF5f6gWA5AMRbc2Z8V+tAk9yGYpjMKeU3M7f+eWnUEECIwMTIzNDU2Nzg5IgAAAAACAggA"
+ expectedPfxCa string = "MIIL4QIBAzCCC5EGCSqGSIb3DQEHAaCCC4IEggt+MIILejCCBegGCSqGSIb3DQEHBqCCBdkwggXVAgEAMIIFzgYJKoZIhvcNAQcBMF0GCSqGSIb3DQEFDTBQMC8GCSqGSIb3DQEFDDAiBBAiMDEyMzQ1Njc4OSIAAAAAAgIIADAKBggqhkiG9w0CCTAdBglghkgBZQMEASoEECIwMTIzNDU2Nzg5IgAAAACAggVgsKYe+E6Ea1TTSH+knsTsQZcml6DgZWIAD8jpKgch9uYlLsYovIdglRYNkmVoYtvBJxfIOpgCb9yi7xRdftnteQEJUko+WGYuBWTRUBb+loOEdSj7fHohPv1OEBWOkCEs2DJqngWpbQy2zmMtkAzfZIL0Ed3cBGvBkWUhCGAPjVoYJNqLoPlx5VUM7Aj9ZdBhawMHBXuVUXOUgONuLRdHJB5MacXJ62eJobRZscoiuAH8s9tDpkWjo0axALundiR8U98O4a8E9gbXp5ESDw+Fif41NFy9gq/b0F41grgPK0d9UKoWddOe5HxbwhgXZO8mRew96ed8CulGzR9u3vSiU2dvRYDHfvSghCeFFuxDe9zSrQCiRJNKOL0UT3f5v44fUJBpDVAzpf+6Fg3EchmxxIA08PUc934GngOhf2eKKNiPjRveP25TiBDg/3ls2qSDpZOVRMP7H4H2WeCGdYKTvbmLC0XEGTh5wzCkUvL0Bf3V6lW4xHPmDW3vI6RXxFGvpziqO0+lZ+v/RefNZV+MDtTdQEU6UKa67uz1HlB4448+h8wrtnWDAI0e0qSRYIrFLLCl8zg9M2THQtvwZ/PdAQH5ljjHnnQb72LS7OdKuxkdBauiufqA1rtFr4yc3Gr5lHmXV9v5+Gl1yFQeR7oFxIOrwG52d81chG7aBvIgT43dZDGxSSmNOOuhCc2ALA3nQuf4B6ayZGNXIYtbc9j+MddvtDwPksQUqbwskBp927MryskJz0vIBgD89ybYArG4vqLt1uYLsgBnioaSp1Z8FNR2prdrrkGJ0eZsTPcWKk724eLd1wrGhOef4ezhxqu2cJ6aztGPNq8udsBpyK1pThGdDMALq6fg5qgmoZglpw/ZZj/Xdycrc6g299Q1Zzr5CdhU5WlQ0MTu0wM7nk1JRlmDEtHzOdRqM81Wc4qHK3lXtuI/zm1blpwW9FiXuuBTdOYB3YlotHp1GvDdl+NhFC8ele4HeHDhQ+FEdg4GSb5X4C2X/K+IzZvp26L+Dp6V3V/yoKTh+bcnG1Wx6JA99ZJyTZ13WEb5RWosy2KcvbmkY8qTq/cNti+WHwgQ/xqg5PY4RgZ1DgCgEyy5qvX6gFErN+MxcFa8PcPVOOnGFAu6oAOwKed93cB2/uycBnNJByuLUgFPj55Nvh55eDLIn+083YzeOJSfb26D2TCjRPP0FMZMCW4VbQiglA0d0j8Amrl472wqs70coAjKY44iwOUQXjGSoo/0TDyG8PyNBhTs2Jhi3xdn4B2RApWQ6B1QvNORl4+BRKUic0JUbMjRsh4muMtsx3srXEZmWjfpzINnD19UX0whOkRkaMzemaZ49RZJpn2AeIyoRo+laDdxx4pCqYAI0ro9pA+q60XGoVODhstERfsFrb5WGeVUkNP5flgvQmDy5atDMl1RQGea0ZXsWynUJ/BF1trNtrWuEe2TmsmfJzP9HRKF3mw07S5z9IrCQ2NDISEkseBScJSWDjo5/Rr08XgXBhYlgQKYtzOFB108YIkFJSWqe9PAJYxv/agp6RgFmigDLh2rhJZNQpl7EgKYW+g/jUNXMHaT/m3SRMlXbC7ceV6dm2LPE4gYct4b9IvOFEAZxWJixIKHdTf16wTDlGT1oeSv0avzo3RNyO70TTIACx5L+6pmPZi1U3iDJa+OloZO8z6VEb2bODzFMvjhiegw6JQbWpMNi46Y+urF0rQjWmKojlb8s5K46SmAayFFo/9Xl3U0uOnqgApub2RgwCXgE3tvBY6hwq8bZlZ1j5PeJo0375MxLuT0TrPPnqh27S3V4L69OVJ+UZCILl89UM4E7BBhGfTlQ3kwggWKBgkqhkiG9w0BBwGgggV7BIIFdzCCBXMwggVvBgsqhkiG9w0BDAoBAqCCBTcwggUzMF0GCSqGSIb3DQEFDTBQMC8GCSqGSIb3DQEFDDAiBBAiMDEyMzQ1Njc4OSIAAAAAAgIIADAKBggqhkiG9w0CCTAdBglghkgBZQMEASoEECIwMTIzNDU2Nzg5IgAAAAAEggTQ2VJM4mr0FBkty7ied/w1aha3MoDllKVosUCocl8+tCXSBO+i5tQvybIex7gleTOnkmm2s/eFO1i3LKFzzK9/8SVmzJo2+juyUJSM3rIFmN5zKpj+q4Pto6YHpoXGoDszOpVetPPBbsPtJ8Q1tu9CgyL9Bm/GJwfzhJpc1SeywxztILwkJhF/zEkxngiKX8HGVSxVoTuKhi0Gpp2brfWBr9cRf31KHY6a4uQia5nxzr73EPy5DVH2EGcuHdXbsOIcSVGlfVk3+Fz/S6FrgzDPPk9L8DCe/lvzbou8R9TpV7dOOetAI8UT0+zu5I7YZV5gDlR0IxDRa8sgrCw6I0Bk8ABmSSC4tMersSQmVYcnAWkGDn6WQDjpEGJ6T1ScTPd0sFLysCdIo9nyh/T1//mGfNXylx8smlNtnokrlUr0x0ywGLiyRrZWD5ZVhYEmOLFJ0CkRGGR6lUpudrSAun7oQaZ9cSlmyApQ9dZAQmVjEYVcVwN0pTkqo92FJUbkmEGCgvBSIwIT/bB1w8TKjTz6iTYQmcuodAnnhzDuV+F0HqH6IolIKVZdREeRpm+GatNFGcN36FJlvze87XSsst9YiyLEgUy9Le1Pnt/wDlqrI1l9qqyIySIl3VLiY04rv/8PRJGkVD5S66xNqkY5gYuGD6DVzcrdO4RfqXiWXbiqSkicaDd80KApuRVtnvoqrvZRtRuMf18GA8FQ4yTRa2w8/CQav6QbcUecFZBglvrGl/KquHhVZrTwaSP0GEfPUs6X7fBU4BfgBFIISAzhcpHntgZ/ngZSVbJuOo7NHpCPJsLSDf+c4KsNSHQuYI9xU8t/yCFyR5alglT3CbMt976JGRevD6hedUXIr/ZI6jcrWC3JrdJJjr0pt+DiZ9sLNXhLbKb0afzBzgvOvPHDKx4Cwzf0eG8oOm0cGkIcqUBV6Uosnk+xYIQUcK9GR6HlB6kCL3wyp1uDQQEUcknkw3aLlYaOgc3O1uaDIDDD1f7SAcoY/e+3N8HYUZ2dcCVR9HV0Ycg3QwayFRJZPkkkk3GMxVgUhQaVt2SKIiHBlavzRs1Ur/oCPoUktzozyo+FEKUoVHEJRftGCATI3H8TqrtC79W/9OR7V3bk+5RgZgzK5k5l9F8nUeSiSzoByrFCtbHlZbiblZguNGVikqYG3hXe+0QHOTt1nFcmd2MeLvd3o65FGb5fTG4gNwxN2C659j92VfH6OpYFUqlQ+An2+tiJdN+JVdfsHUI5+DdXCDhQs8H9xFdNTOttuSRuwgXsuiY6zGwGErd3hY0hRNFXr2IEai+A/q7xdVdiy//vaS80wHoCSAvvSjpYNg0w0zNiFQ/2Ty+aQg4qHVazocMUlhj2XMWd1vfIpoQSWggTjhYcdIK5z1EsmTAiXK7xHvVOpLI6QVqKHrHBKhmd01Yf/QQyYpvB2/GxlmiXy7WTMFfY3IqP8w6pRQEApKmTUYpx9qVRp2l55R8hOBR9RW2OQGKm10dZRgg87qGbVwbzz3cPx/fPKoGhDu7o7qRwNrKXAbCCjV4eSxoRASFbQUQzJ/Bi6uvZgA8Z42xZXddkFcoGyJZCoi+550QSypEjoRvVy76ff1xxRF+aS9Au3DJKYHQw78018/TdT/62vFI6u92huEcxJTAjBgkqhkiG9w0BCRUxFgQUOEXV6IFYGpCSHi0MPHz4b3W0KOQwRzAvMAsGCWCGSAFlAwQCAQQgcZGaBMgQmJC2O0UWNjaW0aQbipFFKCFVHNmLLObEbKwEECIwMTIzNDU2Nzg5IgAAAAACAggA"
+)
+
+func TestPemToPkcs12(t *testing.T) {
+ // Mock the random generator, to guarantee to always generate the same output
+ gopkcs12.Modern = gopkcs12.Modern.WithRand(MockRandomReader{})
+
+ out, err := pemToPkcs12Pass(certData, keyData, "password")
+ if err != nil {
+ t.Errorf("pemToPkcs12Pass() got error '%v', expected none", err)
+ return
+ }
+
+ if out != expectedPfx {
+ t.Errorf("pemToPkcs12Pass() got '%s', expected '%s'", out, expectedPfx)
+ }
+}
+
+func TestFullPemToPkcs12(t *testing.T) {
+ // Mock the random generator, to guarantee to always generate the same output
+ gopkcs12.Modern = gopkcs12.Modern.WithRand(MockRandomReader{})
+
+ out, err := fullPemToPkcs12Pass(certData+"\n"+otherCert, keyData, "password")
+ if err != nil {
+ t.Errorf("pemToPkcs12Pass() got error '%v', expected none", err)
+ return
+ }
+
+ if out != expectedPfxCa {
+ t.Errorf("pemToPkcs12Pass() got '%s', expected '%s'", out, expectedPfxCa)
+ }
+}
+
+type MockRandomReader struct{}
+
+func (r MockRandomReader) Read(p []byte) (int, error) {
+ copy(p, `"0123456789"`)
+ return len(p), nil
+}
diff --git a/pkg/template/v2/template.go b/pkg/template/v2/template.go
index e7684e61843..97a99ebd126 100644
--- a/pkg/template/v2/template.go
+++ b/pkg/template/v2/template.go
@@ -32,10 +32,13 @@ var tplFuncs = tpl.FuncMap{
"pkcs12cert": pkcs12cert,
"pkcs12certPass": pkcs12certPass,
- "pemToPkcs12": pemToPkcs12,
- "pemToPkcs12Pass": pemToPkcs12Pass,
+ "pemToPkcs12": pemToPkcs12,
+ "pemToPkcs12Pass": pemToPkcs12Pass,
+ "fullPemToPkcs12": fullPemToPkcs12,
+ "fullPemToPkcs12Pass": fullPemToPkcs12Pass,
- "filterPEM": filterPEM,
+ "filterPEM": filterPEM,
+ "filterCertChain": filterCertChain,
"jwkPublicKeyPem": jwkPublicKeyPem,
"jwkPrivateKeyPem": jwkPrivateKeyPem,
@@ -57,6 +60,7 @@ const (
errParsePrivKey = "unable to parse private key type"
pemTypeCertificate = "CERTIFICATE"
+ pemTypeKey = "PRIVATE KEY"
)
func init() {
@@ -72,10 +76,19 @@ func init() {
func applyToTarget(k, val string, target esapi.TemplateTarget, secret *corev1.Secret) {
switch target {
case esapi.TemplateTargetAnnotations:
+ if secret.Annotations == nil {
+ secret.Annotations = make(map[string]string)
+ }
secret.Annotations[k] = val
case esapi.TemplateTargetLabels:
+ if secret.Labels == nil {
+ secret.Labels = make(map[string]string)
+ }
secret.Labels[k] = val
case esapi.TemplateTargetData:
+ if secret.Data == nil {
+ secret.Data = make(map[string][]byte)
+ }
secret.Data[k] = []byte(val)
default:
}
@@ -139,6 +152,7 @@ func execute(k, val string, data map[string][]byte) ([]byte, error) {
}
t, err := tpl.New(k).
+ Option("missingkey=error").
Funcs(tplFuncs).
Parse(val)
if err != nil {
diff --git a/pkg/template/v2/template_test.go b/pkg/template/v2/template_test.go
index ac9fadd6296..1697d900e1a 100644
--- a/pkg/template/v2/template_test.go
+++ b/pkg/template/v2/template_test.go
@@ -369,6 +369,14 @@ func TestExecute(t *testing.T) {
data: map[string][]byte{},
expErr: "unable to parse template",
},
+ {
+ name: "unknown key error",
+ tpl: map[string][]byte{
+ "key": []byte(`{{ .unknown }}`),
+ },
+ data: map[string][]byte{},
+ expErr: "unable to execute template at key key",
+ },
{
name: "jwk rsa pub pem",
tpl: map[string][]byte{
@@ -515,6 +523,85 @@ func TestExecute(t *testing.T) {
}
}
+func TestScopeValuesWithSecretFieldsNil(t *testing.T) {
+ tbl := []struct {
+ name string
+ tpl map[string][]byte
+ target esapi.TemplateTarget
+ data map[string][]byte
+ expectedData map[string][]byte
+ expectedStringData map[string]string
+ expErr string
+ }{
+ {
+ name: "test empty",
+ tpl: map[string][]byte{},
+ target: esapi.TemplateTargetData,
+ data: nil,
+ },
+ {
+ name: "test byte",
+ tpl: map[string][]byte{"foo": []byte("bar")},
+ target: esapi.TemplateTargetData,
+ data: map[string][]byte{
+ "key": []byte("foo"),
+ "value": []byte("bar"),
+ },
+ expectedData: map[string][]byte{
+ "foo": []byte("bar"),
+ },
+ },
+ {
+ name: "test Annotations",
+ tpl: map[string][]byte{"foo": []byte("bar")},
+ target: esapi.TemplateTargetAnnotations,
+ data: map[string][]byte{
+ "key": []byte("foo"),
+ "value": []byte("bar"),
+ },
+ expectedStringData: map[string]string{
+ "foo": "bar",
+ },
+ },
+ {
+ name: "test Labels",
+ tpl: map[string][]byte{"foo": []byte("bar")},
+ target: esapi.TemplateTargetLabels,
+ data: map[string][]byte{
+ "key": []byte("foo"),
+ "value": []byte("bar"),
+ },
+ expectedStringData: map[string]string{
+ "foo": "bar",
+ },
+ },
+ }
+ for i := range tbl {
+ row := tbl[i]
+ t.Run(row.name, func(t *testing.T) {
+ sec := &corev1.Secret{}
+ err := Execute(row.tpl, row.data, esapi.TemplateScopeValues, row.target, sec)
+ if !ErrorContains(err, row.expErr) {
+ t.Errorf("unexpected error: %s, expected: %s", err, row.expErr)
+ }
+ switch row.target {
+ case esapi.TemplateTargetData:
+ if row.expectedData != nil {
+ assert.EqualValues(t, row.expectedData, sec.Data)
+ }
+ case esapi.TemplateTargetLabels:
+ if row.expectedStringData != nil {
+ assert.EqualValues(t, row.expectedStringData, sec.Labels)
+ }
+ case esapi.TemplateTargetAnnotations:
+ if row.expectedStringData != nil {
+ assert.EqualValues(t, row.expectedStringData, sec.Annotations)
+ }
+ }
+ })
+ }
+}
+
func TestExecuteInvalidTemplateScope(t *testing.T) {
sec := &corev1.Secret{}
err := Execute(map[string][]byte{"foo": []byte("bar")}, nil, "invalid", esapi.TemplateTargetData, sec)
diff --git a/pkg/utils/metadata/metadata.go b/pkg/utils/metadata/metadata.go
new file mode 100644
index 00000000000..dc6712e34f6
--- /dev/null
+++ b/pkg/utils/metadata/metadata.go
@@ -0,0 +1,55 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package metadata
+
+import (
+ "fmt"
+
+ apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ "sigs.k8s.io/yaml"
+)
+
+const (
+ APIVersion = "kubernetes.external-secrets.io/v1alpha1"
+ Kind = "PushSecretMetadata"
+)
+
+type PushSecretMetadata[T any] struct {
+ Kind string `json:"kind"`
+ APIVersion string `json:"apiVersion"`
+ Spec T `json:"spec,omitempty"`
+}
+
+// ParseMetadataParameters parses metadata with an arbitrary Spec.
+func ParseMetadataParameters[T any](data *apiextensionsv1.JSON) (*PushSecretMetadata[T], error) {
+ if data == nil {
+ return nil, nil
+ }
+ var metadata PushSecretMetadata[T]
+ err := yaml.Unmarshal(data.Raw, &metadata, yaml.DisallowUnknownFields)
+ if err != nil {
+ return nil, fmt.Errorf("failed to parse %s %s: %w", APIVersion, Kind, err)
+ }
+
+ if metadata.APIVersion != APIVersion {
+ return nil, fmt.Errorf("unexpected apiVersion %q, expected %q", metadata.APIVersion, APIVersion)
+ }
+
+ if metadata.Kind != Kind {
+ return nil, fmt.Errorf("unexpected kind %q, expected %q", metadata.Kind, Kind)
+ }
+
+ return &metadata, nil
+}
diff --git a/pkg/utils/resolvers/generator.go b/pkg/utils/resolvers/generator.go
new file mode 100644
index 00000000000..b647b8b8f6e
--- /dev/null
+++ b/pkg/utils/resolvers/generator.go
@@ -0,0 +1,221 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package resolvers
+
+import (
+ "context"
+ "fmt"
+ "reflect"
+
+ apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/types"
+ "sigs.k8s.io/controller-runtime/pkg/client"
+ "sigs.k8s.io/controller-runtime/pkg/reconcile"
+
+ esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1"
+)
+
+// these errors are explicitly defined so we can detect them with `errors.Is()`.
+var (
+ // ErrUnableToGetGenerator is returned when a generator reference cannot be resolved.
+ ErrUnableToGetGenerator = fmt.Errorf("unable to get generator")
+)
+
+// GeneratorRef resolves a generator reference to a generator implementation.
+func GeneratorRef(ctx context.Context, cl client.Client, scheme *runtime.Scheme, namespace string, generatorRef *esv1beta1.GeneratorRef) (genv1alpha1.Generator, *apiextensions.JSON, error) {
+ generator, jsonObj, err := getGenerator(ctx, cl, scheme, namespace, generatorRef)
+ if err != nil {
+ return nil, nil, fmt.Errorf("%w: %w", ErrUnableToGetGenerator, err)
+ }
+ return generator, jsonObj, nil
+}
+
+func getGenerator(ctx context.Context, cl client.Client, scheme *runtime.Scheme, namespace string, generatorRef *esv1beta1.GeneratorRef) (genv1alpha1.Generator, *apiextensions.JSON, error) {
+ // get a GVK from the generatorRef
+ gv, err := schema.ParseGroupVersion(generatorRef.APIVersion)
+ if err != nil {
+ return nil, nil, reconcile.TerminalError(fmt.Errorf("generatorRef has invalid APIVersion: %w", err))
+ }
+ gvk := schema.GroupVersionKind{
+ Group: gv.Group,
+ Version: gv.Version,
+ Kind: generatorRef.Kind,
+ }
+
+ // fail if the GVK does not use the generator group
+ if gvk.Group != genv1alpha1.Group {
+ return nil, nil, reconcile.TerminalError(fmt.Errorf("generatorRef may only reference the generators group, but got %s", gvk.Group))
+ }
+
+ // get a client Object from the GVK
+ t, exists := scheme.AllKnownTypes()[gvk]
+ if !exists {
+ return nil, nil, reconcile.TerminalError(fmt.Errorf("generatorRef references unknown GVK %s", gvk))
+ }
+ obj := reflect.New(t).Interface().(client.Object)
+
+ // this interface provides the Generate() method used by the controller
+ // NOTE: all instances of a generator kind use the same instance of this interface
+ var generator genv1alpha1.Generator
+
+ // ClusterGenerator is a special case because it's a cluster-scoped resource
+ // to use it, we create a "virtual" namespaced generator for the current namespace, as if one existed in the API
+ if gvk.Kind == genv1alpha1.ClusterGeneratorKind {
+ clusterGenerator := obj.(*genv1alpha1.ClusterGenerator)
+
+ // get the cluster generator resource from the API
+ // NOTE: it's important that we use the structured client so we use the cache
+ err = cl.Get(ctx, client.ObjectKey{Name: generatorRef.Name}, clusterGenerator)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // convert the cluster generator to a virtual namespaced generator object
+ obj, err = clusterGeneratorToVirtual(clusterGenerator)
+ if err != nil {
+ return nil, nil, reconcile.TerminalError(fmt.Errorf("invalid ClusterGenerator: %w", err))
+ }
+
+ // get the generator interface
+ var ok bool
+ generator, ok = genv1alpha1.GetGeneratorByName(string(clusterGenerator.Spec.Kind))
+ if !ok {
+ return nil, nil, reconcile.TerminalError(fmt.Errorf("ClusterGenerator has unknown kind %s", clusterGenerator.Spec.Kind))
+ }
+ } else {
+ // get the generator resource from the API
+ // NOTE: it's important that we use the structured client so we use the cache
+ err = cl.Get(ctx, types.NamespacedName{
+ Name: generatorRef.Name,
+ Namespace: namespace,
+ }, obj)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // get the generator interface
+ var ok bool
+ generator, ok = genv1alpha1.GetGeneratorByName(gvk.Kind)
+ if !ok {
+ return nil, nil, reconcile.TerminalError(fmt.Errorf("generatorRef has unknown kind %s", gvk.Kind))
+ }
+ }
+
+ // convert the generator to unstructured object
+ u := &unstructured.Unstructured{}
+ u.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // convert the unstructured object to JSON
+ // NOTE: we do this for backwards compatibility with how this API works, not because it's a good idea
+ // we should refactor the generator API to use the normal typed objects
+ jsonObj, err := u.MarshalJSON()
+ if err != nil {
+ return nil, nil, err
+ }
+
+ return generator, &apiextensions.JSON{Raw: jsonObj}, nil
+}
+
+// clusterGeneratorToVirtual converts a ClusterGenerator to a "virtual" namespaced generator that doesn't actually exist in the API.
+func clusterGeneratorToVirtual(gen *genv1alpha1.ClusterGenerator) (client.Object, error) {
+ switch gen.Spec.Kind {
+ case genv1alpha1.GeneratorKindACRAccessToken:
+ if gen.Spec.Generator.ACRAccessTokenSpec == nil {
+ return nil, fmt.Errorf("when kind is %s, ACRAccessTokenSpec must be set", gen.Spec.Kind)
+ }
+ return &genv1alpha1.ACRAccessToken{
+ Spec: *gen.Spec.Generator.ACRAccessTokenSpec,
+ }, nil
+ case genv1alpha1.GeneratorKindECRAuthorizationToken:
+ if gen.Spec.Generator.ECRAuthorizationTokenSpec == nil {
+ return nil, fmt.Errorf("when kind is %s, ECRAuthorizationTokenSpec must be set", gen.Spec.Kind)
+ }
+ return &genv1alpha1.ECRAuthorizationToken{
+ Spec: *gen.Spec.Generator.ECRAuthorizationTokenSpec,
+ }, nil
+ case genv1alpha1.GeneratorKindFake:
+ if gen.Spec.Generator.FakeSpec == nil {
+ return nil, fmt.Errorf("when kind is %s, FakeSpec must be set", gen.Spec.Kind)
+ }
+ return &genv1alpha1.Fake{
+ Spec: *gen.Spec.Generator.FakeSpec,
+ }, nil
+ case genv1alpha1.GeneratorKindGCRAccessToken:
+ if gen.Spec.Generator.GCRAccessTokenSpec == nil {
+ return nil, fmt.Errorf("when kind is %s, GCRAccessTokenSpec must be set", gen.Spec.Kind)
+ }
+ return &genv1alpha1.GCRAccessToken{
+ Spec: *gen.Spec.Generator.GCRAccessTokenSpec,
+ }, nil
+ case genv1alpha1.GeneratorKindGithubAccessToken:
+ if gen.Spec.Generator.GithubAccessTokenSpec == nil {
+ return nil, fmt.Errorf("when kind is %s, GithubAccessTokenSpec must be set", gen.Spec.Kind)
+ }
+ return &genv1alpha1.GithubAccessToken{
+ Spec: *gen.Spec.Generator.GithubAccessTokenSpec,
+ }, nil
+ case genv1alpha1.GeneratorKindQuayAccessToken:
+ if gen.Spec.Generator.QuayAccessTokenSpec == nil {
+ return nil, fmt.Errorf("when kind is %s, QuayAccessTokenSpec must be set", gen.Spec.Kind)
+ }
+ return &genv1alpha1.QuayAccessToken{
+ Spec: *gen.Spec.Generator.QuayAccessTokenSpec,
+ }, nil
+ case genv1alpha1.GeneratorKindPassword:
+ if gen.Spec.Generator.PasswordSpec == nil {
+ return nil, fmt.Errorf("when kind is %s, PasswordSpec must be set", gen.Spec.Kind)
+ }
+ return &genv1alpha1.Password{
+ Spec: *gen.Spec.Generator.PasswordSpec,
+ }, nil
+ case genv1alpha1.GeneratorKindSTSSessionToken:
+ if gen.Spec.Generator.STSSessionTokenSpec == nil {
+ return nil, fmt.Errorf("when kind is %s, STSSessionTokenSpec must be set", gen.Spec.Kind)
+ }
+ return &genv1alpha1.STSSessionToken{
+ Spec: *gen.Spec.Generator.STSSessionTokenSpec,
+ }, nil
+ case genv1alpha1.GeneratorKindUUID:
+ if gen.Spec.Generator.UUIDSpec == nil {
+ return nil, fmt.Errorf("when kind is %s, UUIDSpec must be set", gen.Spec.Kind)
+ }
+ return &genv1alpha1.UUID{
+ Spec: *gen.Spec.Generator.UUIDSpec,
+ }, nil
+ case genv1alpha1.GeneratorKindVaultDynamicSecret:
+ if gen.Spec.Generator.VaultDynamicSecretSpec == nil {
+ return nil, fmt.Errorf("when kind is %s, VaultDynamicSecretSpec must be set", gen.Spec.Kind)
+ }
+ return &genv1alpha1.VaultDynamicSecret{
+ Spec: *gen.Spec.Generator.VaultDynamicSecretSpec,
+ }, nil
+ case genv1alpha1.GeneratorKindWebhook:
+ if gen.Spec.Generator.WebhookSpec == nil {
+ return nil, fmt.Errorf("when kind is %s, WebhookSpec must be set", gen.Spec.Kind)
+ }
+ return &genv1alpha1.Webhook{
+ Spec: *gen.Spec.Generator.WebhookSpec,
+ }, nil
+ default:
+ return nil, fmt.Errorf("unknown kind %s", gen.Spec.Kind)
+ }
+}
diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go
index 150be5d84b6..7af21c1fc51 100644
--- a/pkg/utils/utils.go
+++ b/pkg/utils/utils.go
@@ -16,9 +16,12 @@ package utils
import (
"bytes"
+ "context"
"crypto/md5" //nolint:gosec
+ "crypto/x509"
"encoding/base64"
"encoding/json"
+ "encoding/pem"
"errors"
"fmt"
"net"
@@ -31,12 +34,15 @@ import (
"time"
"unicode"
+ corev1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ "sigs.k8s.io/controller-runtime/pkg/client"
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
"github.com/external-secrets/external-secrets/pkg/template/v2"
+ "github.com/external-secrets/external-secrets/pkg/utils/resolvers"
)
const (
@@ -139,7 +145,7 @@ func transform(val string, data map[string][]byte) ([]byte, error) {
return buf.Bytes(), nil
}
-// DecodeValues decodes values from a secretMap.
+// DecodeMap decodes values from a secretMap.
func DecodeMap(strategy esv1beta1.ExternalSecretDecodingStrategy, in map[string][]byte) (map[string][]byte, error) {
out := make(map[string][]byte, len(in))
for k, v := range in {
@@ -186,19 +192,23 @@ func Decode(strategy esv1beta1.ExternalSecretDecodingStrategy, in []byte) ([]byt
}
}
-func ValidateKeys(in map[string][]byte) bool {
+// ValidateKeys checks if the keys in the secret map are valid keys for a Kubernetes secret.
+func ValidateKeys(in map[string][]byte) error {
for key := range in {
- for _, v := range key {
- if !unicode.IsNumber(v) &&
- !unicode.IsLetter(v) &&
- v != '-' &&
- v != '.' &&
- v != '_' {
- return false
+ keyLength := len(key)
+ if keyLength == 0 {
+ return fmt.Errorf("found empty key")
+ }
+ if keyLength > 253 {
+ return fmt.Errorf("key has length %d but max is 253: (following is truncated): %s", keyLength, key[:253])
+ }
+ for _, c := range key {
+ if !unicode.IsLetter(c) && !unicode.IsNumber(c) && c != '-' && c != '.' && c != '_' {
+ return fmt.Errorf("key has invalid character %c, only alphanumeric, '-', '.' and '_' are allowed: %s", c, key)
}
}
}
- return true
+ return nil
}
// ConvertKeys converts a secret map into a valid key.
@@ -359,7 +369,7 @@ func ErrorContains(out error, want string) bool {
}
var (
- errNamespaceNotAllowed = errors.New("namespace not allowed with namespaced SecretStore")
+ errNamespaceNotAllowed = errors.New("namespace should either be empty or match the namespace of the SecretStore for a namespaced SecretStore")
errRequireNamespace = errors.New("cluster scope requires namespace")
)
@@ -371,7 +381,7 @@ func ValidateSecretSelector(store esv1beta1.GenericStore, ref esmeta.SecretKeySe
if clusterScope && ref.Namespace == nil {
return errRequireNamespace
}
- if !clusterScope && ref.Namespace != nil {
+ if !clusterScope && ref.Namespace != nil && *ref.Namespace != store.GetNamespace() {
return errNamespaceNotAllowed
}
return nil
@@ -383,7 +393,7 @@ func ValidateSecretSelector(store esv1beta1.GenericStore, ref esmeta.SecretKeySe
// support referent auth.
func ValidateReferentSecretSelector(store esv1beta1.GenericStore, ref esmeta.SecretKeySelector) error {
clusterScope := store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind
- if !clusterScope && ref.Namespace != nil {
+ if !clusterScope && ref.Namespace != nil && *ref.Namespace != store.GetNamespace() {
return errNamespaceNotAllowed
}
return nil
@@ -397,7 +407,7 @@ func ValidateServiceAccountSelector(store esv1beta1.GenericStore, ref esmeta.Ser
if clusterScope && ref.Namespace == nil {
return errRequireNamespace
}
- if !clusterScope && ref.Namespace != nil {
+ if !clusterScope && ref.Namespace != nil && *ref.Namespace != store.GetNamespace() {
return errNamespaceNotAllowed
}
return nil
@@ -409,7 +419,7 @@ func ValidateServiceAccountSelector(store esv1beta1.GenericStore, ref esmeta.Ser
// support referent auth.
func ValidateReferentServiceAccountSelector(store esv1beta1.GenericStore, ref esmeta.ServiceAccountSelector) error {
clusterScope := store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind
- if !clusterScope && ref.Namespace != nil {
+ if !clusterScope && ref.Namespace != nil && *ref.Namespace != store.GetNamespace() {
return errNamespaceNotAllowed
}
return nil
@@ -507,3 +517,131 @@ func dig[T any](key string, data map[string]any) (t T, _ error) {
return t, errKeyNotFound
}
+
+func CompareStringAndByteSlices(valueString *string, valueByte []byte) bool {
+ if valueString == nil {
+ return false
+ }
+
+ return bytes.Equal(valueByte, []byte(*valueString))
+}
+
+// CreateCertOpts contains options for a cert pool creation.
+type CreateCertOpts struct {
+ CABundle []byte
+ CAProvider *esv1beta1.CAProvider
+ StoreKind string
+ Namespace string
+ Client client.Client
+}
+
+// FetchCACertFromSource creates a CertPool using either a CABundle directly, or
+// a ConfigMap / Secret.
+func FetchCACertFromSource(ctx context.Context, opts CreateCertOpts) ([]byte, error) {
+ if len(opts.CABundle) == 0 && opts.CAProvider == nil {
+ return nil, nil
+ }
+
+ if len(opts.CABundle) > 0 {
+ pem, err := base64decode(opts.CABundle)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decode ca bundle: %w", err)
+ }
+
+ return pem, nil
+ }
+
+ if opts.CAProvider != nil &&
+ opts.StoreKind == esv1beta1.ClusterSecretStoreKind &&
+ opts.CAProvider.Namespace == nil {
+ return nil, errors.New("missing namespace on caProvider secret")
+ }
+
+ switch opts.CAProvider.Type {
+ case esv1beta1.CAProviderTypeSecret:
+ cert, err := getCertFromSecret(ctx, opts.Client, opts.CAProvider, opts.StoreKind, opts.Namespace)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get cert from secret: %w", err)
+ }
+
+ return cert, nil
+ case esv1beta1.CAProviderTypeConfigMap:
+ cert, err := getCertFromConfigMap(ctx, opts.Namespace, opts.Client, opts.CAProvider)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get cert from configmap: %w", err)
+ }
+
+ return cert, nil
+ }
+
+ return nil, fmt.Errorf("unsupported CA provider type: %s", opts.CAProvider.Type)
+}
+
+func base64decode(cert []byte) ([]byte, error) {
+ if c, err := parseCertificateBytes(cert); err == nil {
+ return c, nil
+ }
+
+ // try decoding and test for validity again...
+ certificate, err := Decode(esv1beta1.ExternalSecretDecodeAuto, cert)
+ if err != nil {
+ return nil, fmt.Errorf("failed to decode base64: %w", err)
+ }
+
+ return parseCertificateBytes(certificate)
+}
+
+func parseCertificateBytes(certBytes []byte) ([]byte, error) {
+ block, _ := pem.Decode(certBytes)
+ if block == nil {
+ return nil, errors.New("failed to parse the new certificate, not valid pem data")
+ }
+
+ if _, err := x509.ParseCertificate(block.Bytes); err != nil {
+ return nil, fmt.Errorf("failed to validate certificate: %w", err)
+ }
+
+ return certBytes, nil
+}
+
+func getCertFromSecret(ctx context.Context, c client.Client, provider *esv1beta1.CAProvider, storeKind, namespace string) ([]byte, error) {
+ secretRef := esmeta.SecretKeySelector{
+ Name: provider.Name,
+ Key: provider.Key,
+ }
+
+ if provider.Namespace != nil {
+ secretRef.Namespace = provider.Namespace
+ }
+
+ cert, err := resolvers.SecretKeyRef(ctx, c, storeKind, namespace, &secretRef)
+ if err != nil {
+ return nil, fmt.Errorf("failed to resolve secret key ref: %w", err)
+ }
+
+ return []byte(cert), nil
+}
+
+func getCertFromConfigMap(ctx context.Context, namespace string, c client.Client, provider *esv1beta1.CAProvider) ([]byte, error) {
+ objKey := client.ObjectKey{
+ Name: provider.Name,
+ Namespace: namespace,
+ }
+
+ if provider.Namespace != nil {
+ objKey.Namespace = *provider.Namespace
+ }
+
+ configMapRef := &corev1.ConfigMap{}
+ err := c.Get(ctx, objKey, configMapRef)
+ if err != nil {
+ return nil, fmt.Errorf("failed to get caProvider secret %s: %w", objKey.Name, err)
+ }
+
+ val, ok := configMapRef.Data[provider.Key]
+ if !ok {
+ return nil, fmt.Errorf("failed to get caProvider configMap %s -> %s", objKey.Name, provider.Key)
+ }
+
+ return []byte(val), nil
+}
diff --git a/pkg/utils/utils_test.go b/pkg/utils/utils_test.go
index c4751620eed..cd9d6868c49 100644
--- a/pkg/utils/utils_test.go
+++ b/pkg/utils/utils_test.go
@@ -16,16 +16,21 @@ package utils
import (
"encoding/json"
+ "errors"
"reflect"
"testing"
"time"
+ "github.com/aws/aws-sdk-go/aws"
"github.com/oracle/oci-go-sdk/v65/vault"
+ "github.com/stretchr/testify/assert"
v1 "k8s.io/api/core/v1"
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+ esmetav1 "github.com/external-secrets/external-secrets/apis/meta/v1"
)
const (
@@ -730,6 +735,20 @@ func TestFetchValueFromMetadata(t *testing.T) {
wantT: "value",
wantErr: false,
},
+ {
+ name: "digging for a slice",
+ args: args{
+ key: "topics",
+ data: &apiextensionsv1.JSON{
+ Raw: []byte(
+ `{"topics": ["topic1", "topic2"]}`,
+ ),
+ },
+ def: []string{},
+ },
+ wantT: []any{"topic1", "topic2"}, // we don't have deep type matching so it's not an []string{} but []any.
+ wantErr: false,
+ },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -738,9 +757,7 @@ func TestFetchValueFromMetadata(t *testing.T) {
t.Errorf("FetchValueFromMetadata() error = %v, wantErr %v", err, tt.wantErr)
return
}
- if !reflect.DeepEqual(gotT, tt.wantT) {
- t.Errorf("FetchValueFromMetadata() gotT = %v, want %v", gotT, tt.wantT)
- }
+ assert.Equal(t, tt.wantT, gotT)
})
}
}
@@ -845,3 +862,369 @@ func TestGetByteValue(t *testing.T) {
})
}
}
+
+func TestCompareStringAndByteSlices(t *testing.T) {
+ type args struct {
+ stringValue *string
+ byteValueSlice []byte
+ }
+ type testCase struct {
+ name string
+ args args
+ want bool
+ wantErr bool
+ }
+ tests := []testCase{
+ {
+ name: "same contents",
+ args: args{
+ stringValue: aws.String("value"),
+ byteValueSlice: []byte("value"),
+ },
+ want: true,
+ wantErr: true,
+ }, {
+ name: "different contents",
+ args: args{
+ stringValue: aws.String("value89"),
+ byteValueSlice: []byte("value"),
+ },
+ want: true,
+ wantErr: false,
+ }, {
+ name: "same contents with random",
+ args: args{
+ stringValue: aws.String("value89!3#@212"),
+ byteValueSlice: []byte("value89!3#@212"),
+ },
+ want: true,
+ wantErr: true,
+ }, {
+ name: "check Nil",
+ args: args{
+ stringValue: nil,
+ byteValueSlice: []byte("value89!3#@212"),
+ },
+ want: false,
+ wantErr: false,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got := CompareStringAndByteSlices(tt.args.stringValue, tt.args.byteValueSlice)
+ if got != tt.wantErr {
+ t.Errorf("CompareStringAndByteSlices() got = %v, want = %v", got, tt.wantErr)
+ return
+ }
+ })
+ }
+}
+
+func TestValidateSecretSelector(t *testing.T) {
+ tests := []struct {
+ desc string
+ store esv1beta1.GenericStore
+ ref esmetav1.SecretKeySelector
+ expected error
+ }{
+ {
+ desc: "cluster secret store with namespace reference",
+ store: &esv1beta1.ClusterSecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.ClusterSecretStoreKind,
+ },
+ },
+ ref: esmetav1.SecretKeySelector{
+ Namespace: Ptr("test"),
+ },
+ expected: nil,
+ },
+ {
+ desc: "secret store without namespace reference",
+ store: &esv1beta1.SecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.SecretStoreKind,
+ },
+ },
+ ref: esmetav1.SecretKeySelector{},
+ expected: nil,
+ },
+ {
+ desc: "secret store with the same namespace reference",
+ store: &esv1beta1.SecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.SecretStoreKind,
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: "test",
+ },
+ },
+ ref: esmetav1.SecretKeySelector{
+ Namespace: Ptr("test"),
+ },
+ expected: nil,
+ },
+ {
+ desc: "cluster secret store without namespace reference",
+ store: &esv1beta1.ClusterSecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.ClusterSecretStoreKind,
+ },
+ },
+ ref: esmetav1.SecretKeySelector{},
+ expected: errRequireNamespace,
+ },
+ {
+ desc: "secret store with the different namespace reference",
+ store: &esv1beta1.SecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.SecretStoreKind,
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: "test",
+ },
+ },
+ ref: esmetav1.SecretKeySelector{
+ Namespace: Ptr("different"),
+ },
+ expected: errNamespaceNotAllowed,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ got := ValidateSecretSelector(tt.store, tt.ref)
+ if !errors.Is(got, tt.expected) {
+ t.Errorf("ValidateSecretSelector() got = %v, want = %v", got, tt.expected)
+ return
+ }
+ })
+ }
+}
+
+func TestValidateReferentSecretSelector(t *testing.T) {
+ tests := []struct {
+ desc string
+ store esv1beta1.GenericStore
+ ref esmetav1.SecretKeySelector
+ expected error
+ }{
+ {
+ desc: "cluster secret store with namespace reference",
+ store: &esv1beta1.ClusterSecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.ClusterSecretStoreKind,
+ },
+ },
+ ref: esmetav1.SecretKeySelector{
+ Namespace: Ptr("test"),
+ },
+ expected: nil,
+ },
+ {
+ desc: "secret store without namespace reference",
+ store: &esv1beta1.SecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.SecretStoreKind,
+ },
+ },
+ ref: esmetav1.SecretKeySelector{},
+ expected: nil,
+ },
+ {
+ desc: "secret store with the same namespace reference",
+ store: &esv1beta1.SecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.SecretStoreKind,
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: "test",
+ },
+ },
+ ref: esmetav1.SecretKeySelector{
+ Namespace: Ptr("test"),
+ },
+ expected: nil,
+ },
+ {
+ desc: "secret store with the different namespace reference",
+ store: &esv1beta1.SecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.SecretStoreKind,
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: "test",
+ },
+ },
+ ref: esmetav1.SecretKeySelector{
+ Namespace: Ptr("different"),
+ },
+ expected: errNamespaceNotAllowed,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ got := ValidateReferentSecretSelector(tt.store, tt.ref)
+ if !errors.Is(got, tt.expected) {
+ t.Errorf("ValidateReferentSecretSelector() got = %v, want = %v", got, tt.expected)
+ return
+ }
+ })
+ }
+}
+
+func TestValidateServiceAccountSelector(t *testing.T) {
+ tests := []struct {
+ desc string
+ store esv1beta1.GenericStore
+ ref esmetav1.ServiceAccountSelector
+ expected error
+ }{
+ {
+ desc: "cluster secret store with namespace reference",
+ store: &esv1beta1.ClusterSecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.ClusterSecretStoreKind,
+ },
+ },
+ ref: esmetav1.ServiceAccountSelector{
+ Namespace: Ptr("test"),
+ },
+ expected: nil,
+ },
+ {
+ desc: "secret store without namespace reference",
+ store: &esv1beta1.SecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.SecretStoreKind,
+ },
+ },
+ ref: esmetav1.ServiceAccountSelector{},
+ expected: nil,
+ },
+ {
+ desc: "secret store with the same namespace reference",
+ store: &esv1beta1.SecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.SecretStoreKind,
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: "test",
+ },
+ },
+ ref: esmetav1.ServiceAccountSelector{
+ Namespace: Ptr("test"),
+ },
+ expected: nil,
+ },
+ {
+ desc: "cluster secret store without namespace reference",
+ store: &esv1beta1.ClusterSecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.ClusterSecretStoreKind,
+ },
+ },
+ ref: esmetav1.ServiceAccountSelector{},
+ expected: errRequireNamespace,
+ },
+ {
+ desc: "secret store with the different namespace reference",
+ store: &esv1beta1.SecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.SecretStoreKind,
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: "test",
+ },
+ },
+ ref: esmetav1.ServiceAccountSelector{
+ Namespace: Ptr("different"),
+ },
+ expected: errNamespaceNotAllowed,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ got := ValidateServiceAccountSelector(tt.store, tt.ref)
+ if !errors.Is(got, tt.expected) {
+ t.Errorf("ValidateServiceAccountSelector() got = %v, want = %v", got, tt.expected)
+ return
+ }
+ })
+ }
+}
+
+func TestValidateReferentServiceAccountSelector(t *testing.T) {
+ tests := []struct {
+ desc string
+ store esv1beta1.GenericStore
+ ref esmetav1.ServiceAccountSelector
+ expected error
+ }{
+ {
+ desc: "cluster secret store with namespace reference",
+ store: &esv1beta1.ClusterSecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.ClusterSecretStoreKind,
+ },
+ },
+ ref: esmetav1.ServiceAccountSelector{
+ Namespace: Ptr("test"),
+ },
+ expected: nil,
+ },
+ {
+ desc: "secret store without namespace reference",
+ store: &esv1beta1.SecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.SecretStoreKind,
+ },
+ },
+ ref: esmetav1.ServiceAccountSelector{},
+ expected: nil,
+ },
+ {
+ desc: "secret store with the same namespace reference",
+ store: &esv1beta1.SecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.SecretStoreKind,
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: "test",
+ },
+ },
+ ref: esmetav1.ServiceAccountSelector{
+ Namespace: Ptr("test"),
+ },
+ expected: nil,
+ },
+ {
+ desc: "secret store with the different namespace reference",
+ store: &esv1beta1.SecretStore{
+ TypeMeta: metav1.TypeMeta{
+ Kind: esv1beta1.SecretStoreKind,
+ },
+ ObjectMeta: metav1.ObjectMeta{
+ Namespace: "test",
+ },
+ },
+ ref: esmetav1.ServiceAccountSelector{
+ Namespace: Ptr("different"),
+ },
+ expected: errNamespaceNotAllowed,
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.desc, func(t *testing.T) {
+ got := ValidateReferentServiceAccountSelector(tt.store, tt.ref)
+ if !errors.Is(got, tt.expected) {
+ t.Errorf("ValidateReferentServiceAccountSelector() got = %v, want = %v", got, tt.expected)
+ return
+ }
+ })
+ }
+}
diff --git a/tilt.debug.dockerfile b/tilt.debug.dockerfile
index c24a71470e1..0b9fd0cd16b 100644
--- a/tilt.debug.dockerfile
+++ b/tilt.debug.dockerfile
@@ -1,10 +1,8 @@
-FROM golang:1.22.2@sha256:450e3822c7a135e1463cd83e51c8e2eb03b86a02113c89424e6f0f8344bb4168
+FROM golang:1.23.5@sha256:8c10f21bec412f08f73aa7b97ca5ac5f28a39d8a88030ad8a339fd0a781d72b4
WORKDIR /
COPY ./bin/external-secrets /external-secrets
-RUN go install github.com/go-delve/delve/cmd/dlv@v1.22.0
-RUN chmod +x /go/bin/dlv
-RUN mv /go/bin/dlv /
+RUN go install github.com/go-delve/delve/cmd/dlv@v1.22.0 && chmod +x /go/bin/dlv && mv /go/bin/dlv /
EXPOSE 30000
diff --git a/tilt.dockerfile b/tilt.dockerfile
index 844eb40c6ed..efd3587d904 100644
--- a/tilt.dockerfile
+++ b/tilt.dockerfile
@@ -1,4 +1,4 @@
-FROM alpine@sha256:c5b1261d6d3e43071626931fc004f70149baeba2c8ec672bd4f27761f8e1ad6b
+FROM alpine@sha256:56fa17d2a7e7f168a043a2712e63aed1f8543aeafdcee47c58dcffe38ed51099
WORKDIR /
COPY ./bin/external-secrets /external-secrets
diff --git a/ubi-build-files-amd64.txt b/ubi-build-files-amd64.txt
new file mode 100644
index 00000000000..0e55ec2e910
--- /dev/null
+++ b/ubi-build-files-amd64.txt
@@ -0,0 +1,15 @@
+etc/pki
+root/buildinfo
+etc/ssl/certs
+etc/redhat-release
+usr/share/zoneinfo
+usr/lib64/ld-2.28.so
+usr/lib64/ld-linux-x86-64.so.2
+usr/lib64/libc-2.28.so
+usr/lib64/libc.so.6
+usr/lib64/libdl-2.28.so
+usr/lib64/libdl.so.2
+usr/lib64/libpthread-2.28.so
+usr/lib64/libpthread.so.0
+usr/lib64/libm-2.28.so
+usr/lib64/libm.so.6
\ No newline at end of file
diff --git a/ubi-build-files-arm64.txt b/ubi-build-files-arm64.txt
new file mode 100644
index 00000000000..18571f6f6c6
--- /dev/null
+++ b/ubi-build-files-arm64.txt
@@ -0,0 +1,15 @@
+etc/pki
+root/buildinfo
+etc/ssl/certs
+etc/redhat-release
+usr/share/zoneinfo
+usr/lib64/ld-2.28.so
+usr/lib64/ld-linux-aarch64.so.1
+usr/lib64/libc-2.28.so
+usr/lib64/libc.so.6
+usr/lib64/libdl-2.28.so
+usr/lib64/libdl.so.2
+usr/lib64/libpthread-2.28.so
+usr/lib64/libpthread.so.0
+usr/lib64/libm-2.28.so
+usr/lib64/libm.so.6
\ No newline at end of file
diff --git a/ubi-build-files-ppc64le.txt b/ubi-build-files-ppc64le.txt
new file mode 100644
index 00000000000..4f9e8668342
--- /dev/null
+++ b/ubi-build-files-ppc64le.txt
@@ -0,0 +1,14 @@
+etc/pki
+root/buildinfo
+etc/ssl/certs
+etc/redhat-release
+usr/share/zoneinfo
+usr/lib64/ld-2.28.so
+usr/lib64/libc-2.28.so
+usr/lib64/libc.so.6
+usr/lib64/libdl-2.28.so
+usr/lib64/libdl.so.2
+usr/lib64/libpthread-2.28.so
+usr/lib64/libpthread.so.0
+usr/lib64/libm-2.28.so
+usr/lib64/libm.so.6
\ No newline at end of file
diff --git a/ubi-build-files-s390x.txt b/ubi-build-files-s390x.txt
new file mode 100644
index 00000000000..4f9e8668342
--- /dev/null
+++ b/ubi-build-files-s390x.txt
@@ -0,0 +1,14 @@
+etc/pki
+root/buildinfo
+etc/ssl/certs
+etc/redhat-release
+usr/share/zoneinfo
+usr/lib64/ld-2.28.so
+usr/lib64/libc-2.28.so
+usr/lib64/libc.so.6
+usr/lib64/libdl-2.28.so
+usr/lib64/libdl.so.2
+usr/lib64/libpthread-2.28.so
+usr/lib64/libpthread.so.0
+usr/lib64/libm-2.28.so
+usr/lib64/libm.so.6
\ No newline at end of file