Selaa lähdekoodia

Merge from main

Sebastián Gómez 4 vuotta sitten
vanhempi
sitoutus
3cfb9ba2c1
100 muutettua tiedostoa jossa 2186 lisäystä ja 174 poistoa
  1. 1 1
      .github/PAUL.yaml
  2. 21 0
      .github/workflows/ci.yml
  3. 1 1
      .github/workflows/e2e-managed.yml
  4. 1 1
      .github/workflows/helm.yml
  5. 24 0
      .github/workflows/release.yml
  6. 6 0
      Makefile
  7. 1 1
      PROJECT
  8. 1 0
      README.md
  9. 11 0
      apis/externalsecrets/v1alpha1/externalsecret_types.go
  10. 23 8
      apis/externalsecrets/v1alpha1/secretstore_azurekv_types.go
  11. 1 0
      apis/externalsecrets/v1alpha1/secretstore_gcpsm_types.go
  12. 6 1
      apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go
  13. 99 0
      apis/externalsecrets/v1beta1/clusterexternalsecret_types.go
  14. 19 2
      apis/externalsecrets/v1beta1/externalsecret_types.go
  15. 9 0
      apis/externalsecrets/v1beta1/register.go
  16. 23 8
      apis/externalsecrets/v1beta1/secretstore_azurekv_types.go
  17. 1 0
      apis/externalsecrets/v1beta1/secretstore_gcpsm_types.go
  18. 152 1
      apis/externalsecrets/v1beta1/zz_generated.deepcopy.go
  19. 51 30
      cmd/root.go
  20. 366 0
      config/crds/bases/external-secrets.io_clusterexternalsecrets.yaml
  21. 38 6
      config/crds/bases/external-secrets.io_clustersecretstores.yaml
  22. 23 2
      config/crds/bases/external-secrets.io_externalsecrets.yaml
  23. 38 6
      config/crds/bases/external-secrets.io_secretstores.yaml
  24. 6 0
      deploy/charts/external-secrets/README.md
  25. 2 0
      deploy/charts/external-secrets/templates/cert-controller-deployment.yaml
  26. 1 1
      deploy/charts/external-secrets/templates/cert-controller-service.yaml
  27. 1 1
      deploy/charts/external-secrets/templates/cert-controller-serviceaccount.yaml
  28. 14 1
      deploy/charts/external-secrets/templates/deployment.yaml
  29. 44 0
      deploy/charts/external-secrets/templates/rbac.yaml
  30. 1 0
      deploy/charts/external-secrets/templates/service.yaml
  31. 2 0
      deploy/charts/external-secrets/templates/validatingwebhook.yaml
  32. 2 0
      deploy/charts/external-secrets/templates/webhook-deployment.yaml
  33. 2 0
      deploy/charts/external-secrets/templates/webhook-secret.yaml
  34. 2 0
      deploy/charts/external-secrets/templates/webhook-service.yaml
  35. 1 1
      deploy/charts/external-secrets/templates/webhook-serviceaccount.yaml
  36. 17 0
      deploy/charts/external-secrets/values.yaml
  37. 408 14
      deploy/crds/bundle.yaml
  38. 185 0
      design/001-secretsink.md
  39. 118 0
      design/cluster-external-secret-spec.md
  40. 11 0
      docs/api-clusterexternalsecret.md
  41. 6 1
      docs/eso-blogs.md
  42. 7 1
      docs/eso-demos.md
  43. 1 1
      docs/guides-controller-class.md
  44. 1 1
      docs/guides-templating.md
  45. 1 1
      docs/provider-aws-parameter-store.md
  46. 1 1
      docs/provider-aws-secrets-manager.md
  47. 41 1
      docs/provider-azure-key-vault.md
  48. 6 5
      docs/provider-google-secrets-manager.md
  49. 86 7
      docs/provider-hashicorp-vault.md
  50. 2 2
      docs/provider-ibm-secrets-manager.md
  51. 95 0
      docs/provider-kubernetes.md
  52. 3 3
      docs/provider-webhook.md
  53. 2 2
      docs/provider-yandex-lockbox.md
  54. 3 2
      docs/snippets/akeyless-external-secret-json.yaml
  55. 1 1
      docs/snippets/akeyless-external-secret.yaml
  56. 1 1
      docs/snippets/akeyless-secret-store.yaml
  57. 3 2
      docs/snippets/aws-anchore-engine-access-credentials-external-secret.yaml
  58. 1 1
      docs/snippets/aws-jenkins-credential-github-ssh-external-secret.yaml
  59. 1 1
      docs/snippets/aws-jenkins-credential-sonarqube-api-token-external-secret.yaml
  60. 1 1
      docs/snippets/aws-jenkins-credentials-harbor-chart-robot-external-secret.yaml
  61. 1 1
      docs/snippets/aws-parameter-store.yaml
  62. 1 1
      docs/snippets/aws-sm-external-secret.yaml
  63. 1 1
      docs/snippets/aws-sm-store.yaml
  64. 1 1
      docs/snippets/azkv-external-secret.yaml
  65. 1 1
      docs/snippets/azkv-secret-store-mi.yaml
  66. 1 1
      docs/snippets/azkv-secret-store.yaml
  67. 19 0
      docs/snippets/azkv-workload-identity-mounted.yaml
  68. 20 0
      docs/snippets/azkv-workload-identity.yaml
  69. 3 2
      docs/snippets/basic-external-secret.yaml
  70. 1 1
      docs/snippets/basic-secret-store.yaml
  71. 1 1
      docs/snippets/controller-class-store.yaml
  72. 3 2
      docs/snippets/fake-provider-es.yaml
  73. 1 1
      docs/snippets/fake-provider-store.yaml
  74. 78 0
      docs/snippets/full-cluster-external-secret.yaml
  75. 1 1
      docs/snippets/full-cluster-secret-store.yaml
  76. 13 4
      docs/snippets/full-external-secret.yaml
  77. 1 5
      docs/snippets/full-secret-store.yaml
  78. 3 2
      docs/snippets/gcpsm-data-from-external-secret.yaml
  79. 1 1
      docs/snippets/gcpsm-docker-config-externalsecret.yaml
  80. 1 1
      docs/snippets/gcpsm-external-secret.yaml
  81. 1 1
      docs/snippets/gcpsm-pod-wi-secret-store.yaml
  82. 1 1
      docs/snippets/gcpsm-secret-store.yaml
  83. 1 1
      docs/snippets/gcpsm-ssh-auth-externalsecret.yaml
  84. 1 1
      docs/snippets/gcpsm-tls-externalsecret.yaml
  85. 3 1
      docs/snippets/gcpsm-wi-secret-store.yaml
  86. 3 2
      docs/snippets/gitlab-external-secret-json.yaml
  87. 1 1
      docs/snippets/gitlab-external-secret.yaml
  88. 1 1
      docs/snippets/gitlab-secret-store.yaml
  89. 1 1
      docs/snippets/gitops/crs/clusterSecretStore.yaml
  90. 7 1
      docs/snippets/ibm-es-types.yaml
  91. 2 2
      docs/snippets/ibm-external-secret.yaml
  92. 1 1
      docs/snippets/ibm-secret-store.yaml
  93. 1 1
      docs/snippets/jwk-template-v2-external-secret.yaml
  94. 1 1
      docs/snippets/multiline-template-v1-external-secret.yaml
  95. 1 1
      docs/snippets/multiline-template-v2-external-secret.yaml
  96. 3 2
      docs/snippets/oracle-external-secret.yaml
  97. 2 2
      docs/snippets/oracle-secret-store.yaml
  98. 1 1
      docs/snippets/pkcs12-template-v1-external-secret.yaml
  99. 1 1
      docs/snippets/pkcs12-template-v2-external-secret.yaml
  100. 5 3
      docs/snippets/provider-aws-access.md

+ 1 - 1
.github/PAUL.yaml

@@ -40,7 +40,7 @@ pull_requests:
   # This will limit the amount of PR's a single contributer can have
   # Limits work in progress
   limit_pull_requests:
-    max_number: 15
+    max_number: 30
   # This is the message that will displayed when a user opens a pull request
   open_message: |
     Greetings!

+ 21 - 0
.github/workflows/ci.yml

@@ -186,6 +186,10 @@ jobs:
     needs: detect-noop
     if: needs.detect-noop.outputs.noop != 'true'
 
+    permissions:
+      id-token: write
+      contents: read
+
     steps:
       - name: Setup QEMU
         uses: docker/setup-qemu-action@v1
@@ -248,3 +252,20 @@ jobs:
         run: make docker.promote
         env:
           RELEASE_TAG: main
+
+      - name: Set up crane
+        if: github.ref == 'refs/heads/main' && env.GHCR_USERNAME != ''
+        run: go install github.com/google/go-containerregistry/cmd/crane@v0.8.0
+
+      - name: Install cosign
+        if: github.ref == 'refs/heads/main' && env.GHCR_USERNAME != ''
+        uses: sigstore/cosign-installer@main
+        with:
+          cosign-release: 'v1.6.0'
+
+      - name: Sign Artifacts to main release channel
+        if: github.ref == 'refs/heads/main' && env.GHCR_USERNAME != ''
+        run: make docker.sign
+        env:
+          RELEASE_TAG: main
+          COSIGN_EXPERIMENTAL: 1

+ 1 - 1
.github/workflows/e2e-managed.yml

@@ -164,7 +164,7 @@ jobs:
 
     - name: Setup gcloud CLI
       if: github.event.client_payload.slash_command.args.named.provider == 'gcp'
-      uses: google-github-actions/setup-gcloud@master
+      uses: google-github-actions/setup-gcloud@v0
       with:
         service_account_key: ${{ env.GCP_SM_SA_GKE_JSON }}
         project_id: ${{ env.GCP_PROJECT_ID }}

+ 1 - 1
.github/workflows/helm.yml

@@ -35,7 +35,7 @@ jobs:
           python-version: 3.7
 
       - name: Set up chart-testing
-        uses: helm/chart-testing-action@v2.2.0
+        uses: helm/chart-testing-action@v2.2.1
 
       - name: Run chart-testing (list-changed)
         id: list-changed

+ 24 - 0
.github/workflows/release.yml

@@ -16,6 +16,7 @@ jobs:
   release:
     name: Create Release
     runs-on: ubuntu-latest
+
     steps:
       - name: Checkout
         uses: actions/checkout@v3
@@ -68,6 +69,11 @@ jobs:
   promote:
     name: Promote Container Image
     runs-on: ubuntu-latest
+
+    permissions:
+      id-token: write
+      contents: read
+
     steps:
       - name: Checkout
         uses: actions/checkout@v3
@@ -88,3 +94,21 @@ jobs:
         env:
           RELEASE_TAG: ${{ github.event.inputs.version }}
           SOURCE_TAG: main
+
+      - name: Set up crane
+        if: env.GHCR_USERNAME != ''
+        run: go install github.com/google/go-containerregistry/cmd/crane@v0.8.0
+
+      - name: Install cosign
+        if: env.GHCR_USERNAME != ''
+        uses: sigstore/cosign-installer@main
+        with:
+          cosign-release: 'v1.6.0'
+
+      - name: Sign Container Image
+        if: env.GHCR_USERNAME != ''
+        run: make docker.sign
+        env:
+          RELEASE_TAG: ${{ github.event.inputs.version }}
+          SOURCE_TAG: main
+          COSIGN_EXPERIMENTAL: 1

+ 6 - 0
Makefile

@@ -231,6 +231,12 @@ docker.promote: ## Promote the docker image to the registry
 	docker manifest push $(IMAGE_REGISTRY):$(RELEASE_TAG)
 	@$(OK) docker push $(RELEASE_TAG) \
 
+docker.sign: ## Sign
+	@$(INFO) signing $(IMAGE_REGISTRY):$(RELEASE_TAG)
+	crane digest $(IMAGE_REGISTRY):$(RELEASE_TAG) > .digest
+	cosign sign $(IMAGE_REGISTRY)@$$(cat .digest)
+	@$(OK) cosign sign $(IMAGE_REGISTRY):$(RELEASE_TAG)
+
 # ====================================================================================
 # Terraform
 

+ 1 - 1
PROJECT

@@ -11,7 +11,7 @@ resources:
 - group: external-secrets
   kind: ExternalSecret
   version: v1alpha1
-- group: external-secrets
+version: "2"
   kind: ClusterSecretStore
   version: v1beta1
 - group: external-secrets

+ 1 - 0
README.md

@@ -23,6 +23,7 @@ Multiple people and organizations are joining efforts to create a single Externa
 - [Alibaba Cloud KMS](https://www.alibabacloud.com/product/kms) (Docs still missing, PRs welcomed!)
 - [Oracle Vault](https://external-secrets.io/provider-oracle-vault)
 - [Generic Webhook](https://external-secrets.io/provider-webhook)
+- [Kubernetes](https://external-secrets.io/provider-kubernetes)
 
 ## Stability and Support Level
 

+ 11 - 0
apis/externalsecrets/v1alpha1/externalsecret_types.go

@@ -141,8 +141,19 @@ type ExternalSecretDataRemoteRef struct {
 	// +optional
 	// Used to select a specific property of the Provider value (if a map), if supported
 	Property string `json:"property,omitempty"`
+	// +optional
+	// Used to define a conversion Strategy
+	// +kubebuilder:default="Default"
+	ConversionStrategy ExternalSecretConversionStrategy `json:"conversionStrategy,omitempty"`
 }
 
+type ExternalSecretConversionStrategy string
+
+const (
+	ExternalSecretConversionDefault ExternalSecretConversionStrategy = "Default"
+	ExternalSecretConversionUnicode ExternalSecretConversionStrategy = "Unicode"
+)
+
 // ExternalSecretSpec defines the desired state of ExternalSecret.
 type ExternalSecretSpec struct {
 	SecretStoreRef SecretStoreRef `json:"secretStoreRef"`

+ 23 - 8
apis/externalsecrets/v1alpha1/secretstore_azurekv_types.go

@@ -20,15 +20,18 @@ import smmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 // Only one of the following auth types may be specified.
 // If none of the following auth type is specified, the default one
 // is ServicePrincipal.
-// +kubebuilder:validation:Enum=ServicePrincipal;ManagedIdentity
-type AuthType string
+// +kubebuilder:validation:Enum=ServicePrincipal;ManagedIdentity;WorkloadIdentity
+type AzureAuthType string
 
 const (
 	// Using service principal to authenticate, which needs a tenantId, a clientId and a clientSecret.
-	ServicePrincipal AuthType = "ServicePrincipal"
+	AzureServicePrincipal AzureAuthType = "ServicePrincipal"
 
-	// Using Managed Identity to authenticate. Used with aad-pod-identity instelled in the clister.
-	ManagedIdentity AuthType = "ManagedIdentity"
+	// Using Managed Identity to authenticate. Used with aad-pod-identity installed in the clister.
+	AzureManagedIdentity AzureAuthType = "ManagedIdentity"
+
+	// Using Workload Identity service accounts to authenticate.
+	AzureWorkloadIdentity AzureAuthType = "WorkloadIdentity"
 )
 
 // Configures an store to sync secrets using Azure KV.
@@ -39,15 +42,24 @@ type AzureKVProvider struct {
 	// - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)
 	// +optional
 	// +kubebuilder:default=ServicePrincipal
-	AuthType *AuthType `json:"authType,omitempty"`
+	AuthType *AzureAuthType `json:"authType,omitempty"`
+
 	// Vault Url from which the secrets to be fetched from.
 	VaultURL *string `json:"vaultUrl"`
+
 	// TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type.
 	// +optional
 	TenantID *string `json:"tenantId,omitempty"`
+
 	// Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type.
 	// +optional
 	AuthSecretRef *AzureKVAuth `json:"authSecretRef,omitempty"`
+
+	// ServiceAccountRef specified the service account
+	// that should be used when authenticating with WorkloadIdentity.
+	// +optional
+	ServiceAccountRef *smmeta.ServiceAccountSelector `json:"serviceAccountRef,omitempty"`
+
 	// If multiple Managed Identity is assigned to the pod, you can select the one to be used
 	// +optional
 	IdentityID *string `json:"identityId,omitempty"`
@@ -56,7 +68,10 @@ type AzureKVProvider struct {
 // Configuration used to authenticate with Azure.
 type AzureKVAuth struct {
 	// The Azure clientId of the service principle used for authentication.
-	ClientID *smmeta.SecretKeySelector `json:"clientId"`
+	// +optional
+	ClientID *smmeta.SecretKeySelector `json:"clientId,omitempty"`
+
 	// The Azure ClientSecret of the service principle used for authentication.
-	ClientSecret *smmeta.SecretKeySelector `json:"clientSecret"`
+	// +optional
+	ClientSecret *smmeta.SecretKeySelector `json:"clientSecret,omitempty"`
 }

+ 1 - 0
apis/externalsecrets/v1alpha1/secretstore_gcpsm_types.go

@@ -35,6 +35,7 @@ type GCPWorkloadIdentity struct {
 	ServiceAccountRef esmeta.ServiceAccountSelector `json:"serviceAccountRef"`
 	ClusterLocation   string                        `json:"clusterLocation"`
 	ClusterName       string                        `json:"clusterName"`
+	ClusterProjectID  string                        `json:"clusterProjectID,omitempty"`
 }
 
 // GCPSMProvider Configures a store to sync secrets using the GCP Secret Manager provider.

+ 6 - 1
apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go

@@ -245,7 +245,7 @@ func (in *AzureKVProvider) DeepCopyInto(out *AzureKVProvider) {
 	*out = *in
 	if in.AuthType != nil {
 		in, out := &in.AuthType, &out.AuthType
-		*out = new(AuthType)
+		*out = new(AzureAuthType)
 		**out = **in
 	}
 	if in.VaultURL != nil {
@@ -263,6 +263,11 @@ func (in *AzureKVProvider) DeepCopyInto(out *AzureKVProvider) {
 		*out = new(AzureKVAuth)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.ServiceAccountRef != nil {
+		in, out := &in.ServiceAccountRef, &out.ServiceAccountRef
+		*out = new(metav1.ServiceAccountSelector)
+		(*in).DeepCopyInto(*out)
+	}
 	if in.IdentityID != nil {
 		in, out := &in.IdentityID, &out.IdentityID
 		*out = new(string)

+ 99 - 0
apis/externalsecrets/v1beta1/clusterexternalsecret_types.go

@@ -0,0 +1,99 @@
+/*
+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 (
+	corev1 "k8s.io/api/core/v1"
+	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+)
+
+// ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret.
+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
+	// +optional
+	ExternalSecretName string `json:"externalSecretName"`
+
+	// The labels to select by to find the Namespaces to create the ExternalSecrets in.
+	NamespaceSelector metav1.LabelSelector `json:"namespaceSelector"`
+
+	// The time in which the controller should reconcile it's objects and recheck namespaces for labels.
+	RefreshInterval *metav1.Duration `json:"refreshTime,omitempty"`
+}
+
+type ClusterExternalSecretConditionType string
+
+const (
+	ClusterExternalSecretReady          ClusterExternalSecretConditionType = "Ready"
+	ClusterExternalSecretPartiallyReady ClusterExternalSecretConditionType = "PartiallyReady"
+	ClusterExternalSecretNotReady       ClusterExternalSecretConditionType = "NotReady"
+)
+
+type ClusterExternalSecretStatusCondition struct {
+	Type   ClusterExternalSecretConditionType `json:"type"`
+	Status corev1.ConditionStatus             `json:"status"`
+
+	// +optional
+	Message string `json:"message,omitempty"`
+}
+
+// ClusterExternalSecretNamespaceFailure represents a failed namespace deployment and it's reason.
+type ClusterExternalSecretNamespaceFailure struct {
+
+	// Namespace is the namespace that failed when trying to apply an ExternalSecret
+	Namespace string `json:"namespace"`
+
+	// Reason is why the ExternalSecret failed to apply to the namespace
+	// +optional
+	Reason string `json:"reason,omitempty"`
+}
+
+// ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret.
+type ClusterExternalSecretStatus struct {
+	// Failed namespaces are the namespaces that failed to apply an ExternalSecret
+	// +optional
+	FailedNamespaces []ClusterExternalSecretNamespaceFailure `json:"failedNamespaces,omitempty"`
+
+	// ProvisionedNamespaces are the namespaces where the ClusterExternalSecret has secrets
+	// +optional
+	ProvisionedNamespaces []string `json:"provisionedNamespaces,omitempty"`
+
+	// +optional
+	Conditions []ClusterExternalSecretStatusCondition `json:"conditions,omitempty"`
+}
+
+//+kubebuilder:object:root=true
+// +kubebuilder:storageversion
+// +kubebuilder:resource:scope=Cluster,categories={externalsecrets},shortName=ces
+//+kubebuilder:subresource:status
+// ClusterExternalSecret is the Schema for the clusterexternalsecrets API.
+type ClusterExternalSecret struct {
+	metav1.TypeMeta   `json:",inline"`
+	metav1.ObjectMeta `json:"metadata,omitempty"`
+
+	Spec   ClusterExternalSecretSpec   `json:"spec,omitempty"`
+	Status ClusterExternalSecretStatus `json:"status,omitempty"`
+}
+
+//+kubebuilder:object:root=true
+
+// ClusterExternalSecretList contains a list of ClusterExternalSecret.
+type ClusterExternalSecretList struct {
+	metav1.TypeMeta `json:",inline"`
+	metav1.ListMeta `json:"metadata,omitempty"`
+	Items           []ClusterExternalSecret `json:"items"`
+}

+ 19 - 2
apis/externalsecrets/v1beta1/externalsecret_types.go

@@ -159,8 +159,20 @@ type ExternalSecretDataRemoteRef struct {
 	// +optional
 	// Used to select a specific property of the Provider value (if a map), if supported
 	Property string `json:"property,omitempty"`
+
+	// +optional
+	// Used to define a conversion Strategy
+	// +kubebuilder:default="Default"
+	ConversionStrategy ExternalSecretConversionStrategy `json:"conversionStrategy,omitempty"`
 }
 
+type ExternalSecretConversionStrategy string
+
+const (
+	ExternalSecretConversionDefault ExternalSecretConversionStrategy = "Default"
+	ExternalSecretConversionUnicode ExternalSecretConversionStrategy = "Unicode"
+)
+
 // +kubebuilder:validation:MinProperties=1
 // +kubebuilder:validation:MaxProperties=1
 type ExternalSecretDataFromRemoteRef struct {
@@ -172,9 +184,10 @@ type ExternalSecretDataFromRemoteRef struct {
 	Find *ExternalSecretFind `json:"find,omitempty"`
 }
 
-// +kubebuilder:validation:MinProperties=1
-// +kubebuilder:validation:MaxProperties=1
 type ExternalSecretFind struct {
+	// A root path to start the find operations.
+	// +optional
+	Path *string `json:"path,omitempty"`
 	// Finds secrets based on the name.
 	// +optional
 	Name *FindName `json:"name,omitempty"`
@@ -182,6 +195,10 @@ type ExternalSecretFind struct {
 	// Find secrets based on tags.
 	// +optional
 	Tags map[string]string `json:"tags,omitempty"`
+	// +optional
+	// Used to define a conversion Strategy
+	// +kubebuilder:default="Default"
+	ConversionStrategy ExternalSecretConversionStrategy `json:"conversionStrategy,omitempty"`
 }
 
 type FindName struct {

+ 9 - 0
apis/externalsecrets/v1beta1/register.go

@@ -44,6 +44,14 @@ var (
 	ExtSecretGroupVersionKind = SchemeGroupVersion.WithKind(ExtSecretKind)
 )
 
+// ClusterExternalSecret type metadata.
+var (
+	ClusterExtSecretKind             = reflect.TypeOf(ClusterExternalSecret{}).Name()
+	ClusterExtSecretGroupKind        = schema.GroupKind{Group: Group, Kind: ClusterExtSecretKind}.String()
+	ClusterExtSecretKindAPIVersion   = ClusterExtSecretKind + "." + SchemeGroupVersion.String()
+	ClusterExtSecretGroupVersionKind = SchemeGroupVersion.WithKind(ClusterExtSecretKind)
+)
+
 // SecretStore type metadata.
 var (
 	SecretStoreKind             = reflect.TypeOf(SecretStore{}).Name()
@@ -62,6 +70,7 @@ var (
 
 func init() {
 	SchemeBuilder.Register(&ExternalSecret{}, &ExternalSecretList{})
+	SchemeBuilder.Register(&ClusterExternalSecret{}, &ClusterExternalSecretList{})
 	SchemeBuilder.Register(&SecretStore{}, &SecretStoreList{})
 	SchemeBuilder.Register(&ClusterSecretStore{}, &ClusterSecretStoreList{})
 }

+ 23 - 8
apis/externalsecrets/v1beta1/secretstore_azurekv_types.go

@@ -20,15 +20,18 @@ import smmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 // Only one of the following auth types may be specified.
 // If none of the following auth type is specified, the default one
 // is ServicePrincipal.
-// +kubebuilder:validation:Enum=ServicePrincipal;ManagedIdentity
-type AuthType string
+// +kubebuilder:validation:Enum=ServicePrincipal;ManagedIdentity;WorkloadIdentity
+type AzureAuthType string
 
 const (
 	// Using service principal to authenticate, which needs a tenantId, a clientId and a clientSecret.
-	ServicePrincipal AuthType = "ServicePrincipal"
+	AzureServicePrincipal AzureAuthType = "ServicePrincipal"
 
-	// Using Managed Identity to authenticate. Used with aad-pod-identity instelled in the clister.
-	ManagedIdentity AuthType = "ManagedIdentity"
+	// Using Managed Identity to authenticate. Used with aad-pod-identity installed in the clister.
+	AzureManagedIdentity AzureAuthType = "ManagedIdentity"
+
+	// Using Workload Identity service accounts to authenticate.
+	AzureWorkloadIdentity AzureAuthType = "WorkloadIdentity"
 )
 
 // Configures an store to sync secrets using Azure KV.
@@ -39,15 +42,24 @@ type AzureKVProvider struct {
 	// - "ManagedIdentity": Using Managed Identity assigned to the pod (see aad-pod-identity)
 	// +optional
 	// +kubebuilder:default=ServicePrincipal
-	AuthType *AuthType `json:"authType,omitempty"`
+	AuthType *AzureAuthType `json:"authType,omitempty"`
+
 	// Vault Url from which the secrets to be fetched from.
 	VaultURL *string `json:"vaultUrl"`
+
 	// TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type.
 	// +optional
 	TenantID *string `json:"tenantId,omitempty"`
+
 	// Auth configures how the operator authenticates with Azure. Required for ServicePrincipal auth type.
 	// +optional
 	AuthSecretRef *AzureKVAuth `json:"authSecretRef,omitempty"`
+
+	// ServiceAccountRef specified the service account
+	// that should be used when authenticating with WorkloadIdentity.
+	// +optional
+	ServiceAccountRef *smmeta.ServiceAccountSelector `json:"serviceAccountRef,omitempty"`
+
 	// If multiple Managed Identity is assigned to the pod, you can select the one to be used
 	// +optional
 	IdentityID *string `json:"identityId,omitempty"`
@@ -56,7 +68,10 @@ type AzureKVProvider struct {
 // Configuration used to authenticate with Azure.
 type AzureKVAuth struct {
 	// The Azure clientId of the service principle used for authentication.
-	ClientID *smmeta.SecretKeySelector `json:"clientId"`
+	// +optional
+	ClientID *smmeta.SecretKeySelector `json:"clientId,omitempty"`
+
 	// The Azure ClientSecret of the service principle used for authentication.
-	ClientSecret *smmeta.SecretKeySelector `json:"clientSecret"`
+	// +optional
+	ClientSecret *smmeta.SecretKeySelector `json:"clientSecret,omitempty"`
 }

+ 1 - 0
apis/externalsecrets/v1beta1/secretstore_gcpsm_types.go

@@ -35,6 +35,7 @@ type GCPWorkloadIdentity struct {
 	ServiceAccountRef esmeta.ServiceAccountSelector `json:"serviceAccountRef"`
 	ClusterLocation   string                        `json:"clusterLocation"`
 	ClusterName       string                        `json:"clusterName"`
+	ClusterProjectID  string                        `json:"clusterProjectID,omitempty"`
 }
 
 // GCPSMProvider Configures a store to sync secrets using the GCP Secret Manager provider.

+ 152 - 1
apis/externalsecrets/v1beta1/zz_generated.deepcopy.go

@@ -245,7 +245,7 @@ func (in *AzureKVProvider) DeepCopyInto(out *AzureKVProvider) {
 	*out = *in
 	if in.AuthType != nil {
 		in, out := &in.AuthType, &out.AuthType
-		*out = new(AuthType)
+		*out = new(AzureAuthType)
 		**out = **in
 	}
 	if in.VaultURL != nil {
@@ -263,6 +263,11 @@ func (in *AzureKVProvider) DeepCopyInto(out *AzureKVProvider) {
 		*out = new(AzureKVAuth)
 		(*in).DeepCopyInto(*out)
 	}
+	if in.ServiceAccountRef != nil {
+		in, out := &in.ServiceAccountRef, &out.ServiceAccountRef
+		*out = new(metav1.ServiceAccountSelector)
+		(*in).DeepCopyInto(*out)
+	}
 	if in.IdentityID != nil {
 		in, out := &in.IdentityID, &out.IdentityID
 		*out = new(string)
@@ -317,6 +322,147 @@ func (in *CertAuth) DeepCopy() *CertAuth {
 	return out
 }
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterExternalSecret) DeepCopyInto(out *ClusterExternalSecret) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
+	in.Spec.DeepCopyInto(&out.Spec)
+	in.Status.DeepCopyInto(&out.Status)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExternalSecret.
+func (in *ClusterExternalSecret) DeepCopy() *ClusterExternalSecret {
+	if in == nil {
+		return nil
+	}
+	out := new(ClusterExternalSecret)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ClusterExternalSecret) 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 *ClusterExternalSecretList) DeepCopyInto(out *ClusterExternalSecretList) {
+	*out = *in
+	out.TypeMeta = in.TypeMeta
+	in.ListMeta.DeepCopyInto(&out.ListMeta)
+	if in.Items != nil {
+		in, out := &in.Items, &out.Items
+		*out = make([]ClusterExternalSecret, len(*in))
+		for i := range *in {
+			(*in)[i].DeepCopyInto(&(*out)[i])
+		}
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExternalSecretList.
+func (in *ClusterExternalSecretList) DeepCopy() *ClusterExternalSecretList {
+	if in == nil {
+		return nil
+	}
+	out := new(ClusterExternalSecretList)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
+func (in *ClusterExternalSecretList) 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 *ClusterExternalSecretNamespaceFailure) DeepCopyInto(out *ClusterExternalSecretNamespaceFailure) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExternalSecretNamespaceFailure.
+func (in *ClusterExternalSecretNamespaceFailure) DeepCopy() *ClusterExternalSecretNamespaceFailure {
+	if in == nil {
+		return nil
+	}
+	out := new(ClusterExternalSecretNamespaceFailure)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterExternalSecretSpec) DeepCopyInto(out *ClusterExternalSecretSpec) {
+	*out = *in
+	in.ExternalSecretSpec.DeepCopyInto(&out.ExternalSecretSpec)
+	in.NamespaceSelector.DeepCopyInto(&out.NamespaceSelector)
+	if in.RefreshInterval != nil {
+		in, out := &in.RefreshInterval, &out.RefreshInterval
+		*out = new(v1.Duration)
+		**out = **in
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExternalSecretSpec.
+func (in *ClusterExternalSecretSpec) DeepCopy() *ClusterExternalSecretSpec {
+	if in == nil {
+		return nil
+	}
+	out := new(ClusterExternalSecretSpec)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterExternalSecretStatus) DeepCopyInto(out *ClusterExternalSecretStatus) {
+	*out = *in
+	if in.FailedNamespaces != nil {
+		in, out := &in.FailedNamespaces, &out.FailedNamespaces
+		*out = make([]ClusterExternalSecretNamespaceFailure, len(*in))
+		copy(*out, *in)
+	}
+	if in.ProvisionedNamespaces != nil {
+		in, out := &in.ProvisionedNamespaces, &out.ProvisionedNamespaces
+		*out = make([]string, len(*in))
+		copy(*out, *in)
+	}
+	if in.Conditions != nil {
+		in, out := &in.Conditions, &out.Conditions
+		*out = make([]ClusterExternalSecretStatusCondition, len(*in))
+		copy(*out, *in)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExternalSecretStatus.
+func (in *ClusterExternalSecretStatus) DeepCopy() *ClusterExternalSecretStatus {
+	if in == nil {
+		return nil
+	}
+	out := new(ClusterExternalSecretStatus)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *ClusterExternalSecretStatusCondition) DeepCopyInto(out *ClusterExternalSecretStatusCondition) {
+	*out = *in
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterExternalSecretStatusCondition.
+func (in *ClusterExternalSecretStatusCondition) DeepCopy() *ClusterExternalSecretStatusCondition {
+	if in == nil {
+		return nil
+	}
+	out := new(ClusterExternalSecretStatusCondition)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ClusterSecretStore) DeepCopyInto(out *ClusterSecretStore) {
 	*out = *in
@@ -462,6 +608,11 @@ func (in *ExternalSecretDataRemoteRef) DeepCopy() *ExternalSecretDataRemoteRef {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *ExternalSecretFind) DeepCopyInto(out *ExternalSecretFind) {
 	*out = *in
+	if in.Path != nil {
+		in, out := &in.Path, &out.Path
+		*out = new(string)
+		**out = **in
+	}
 	if in.Name != nil {
 		in, out := &in.Name, &out.Name
 		*out = new(FindName)

+ 51 - 30
cmd/root.go

@@ -35,27 +35,30 @@ import (
 
 	esv1alpha1 "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/clusterexternalsecret"
 	"github.com/external-secrets/external-secrets/pkg/controllers/externalsecret"
 	"github.com/external-secrets/external-secrets/pkg/controllers/secretstore"
 )
 
 var (
-	scheme                        = runtime.NewScheme()
-	setupLog                      = ctrl.Log.WithName("setup")
-	dnsName                       string
-	certDir                       string
-	metricsAddr                   string
-	healthzAddr                   string
-	controllerClass               string
-	enableLeaderElection          bool
-	concurrent                    int
-	loglevel                      string
-	namespace                     string
-	storeRequeueInterval          time.Duration
-	serviceName, serviceNamespace string
-	secretName, secretNamespace   string
-	crdRequeueInterval            time.Duration
-	certCheckInterval             time.Duration
+	scheme                                = runtime.NewScheme()
+	setupLog                              = ctrl.Log.WithName("setup")
+	dnsName                               string
+	certDir                               string
+	metricsAddr                           string
+	healthzAddr                           string
+	controllerClass                       string
+	enableLeaderElection                  bool
+	concurrent                            int
+	loglevel                              string
+	namespace                             string
+	enableClusterStoreReconciler          bool
+	enableClusterExternalSecretReconciler bool
+	storeRequeueInterval                  time.Duration
+	serviceName, serviceNamespace         string
+	secretName, secretNamespace           string
+	crdRequeueInterval                    time.Duration
+	certCheckInterval                     time.Duration
 )
 
 const (
@@ -115,28 +118,44 @@ var rootCmd = &cobra.Command{
 			setupLog.Error(err, errCreateController, "controller", "SecretStore")
 			os.Exit(1)
 		}
-		if err = (&secretstore.ClusterStoreReconciler{
-			Client:          mgr.GetClient(),
-			Log:             ctrl.Log.WithName("controllers").WithName("ClusterSecretStore"),
-			Scheme:          mgr.GetScheme(),
-			ControllerClass: controllerClass,
-			RequeueInterval: storeRequeueInterval,
-		}).SetupWithManager(mgr); err != nil {
-			setupLog.Error(err, errCreateController, "controller", "ClusterSecretStore")
-			os.Exit(1)
+		if enableClusterStoreReconciler {
+			if err = (&secretstore.ClusterStoreReconciler{
+				Client:          mgr.GetClient(),
+				Log:             ctrl.Log.WithName("controllers").WithName("ClusterSecretStore"),
+				Scheme:          mgr.GetScheme(),
+				ControllerClass: controllerClass,
+				RequeueInterval: storeRequeueInterval,
+			}).SetupWithManager(mgr); err != nil {
+				setupLog.Error(err, errCreateController, "controller", "ClusterSecretStore")
+				os.Exit(1)
+			}
 		}
 		if err = (&externalsecret.Reconciler{
-			Client:          mgr.GetClient(),
-			Log:             ctrl.Log.WithName("controllers").WithName("ExternalSecret"),
-			Scheme:          mgr.GetScheme(),
-			ControllerClass: controllerClass,
-			RequeueInterval: time.Hour,
+			Client:                    mgr.GetClient(),
+			Log:                       ctrl.Log.WithName("controllers").WithName("ExternalSecret"),
+			Scheme:                    mgr.GetScheme(),
+			ControllerClass:           controllerClass,
+			RequeueInterval:           time.Hour,
+			ClusterSecretStoreEnabled: enableClusterStoreReconciler,
 		}).SetupWithManager(mgr, controller.Options{
 			MaxConcurrentReconciles: concurrent,
 		}); err != nil {
 			setupLog.Error(err, errCreateController, "controller", "ExternalSecret")
 			os.Exit(1)
 		}
+		if enableClusterExternalSecretReconciler {
+			if err = (&clusterexternalsecret.Reconciler{
+				Client:          mgr.GetClient(),
+				Log:             ctrl.Log.WithName("controllers").WithName("ClusterExternalSecret"),
+				Scheme:          mgr.GetScheme(),
+				RequeueInterval: time.Hour,
+			}).SetupWithManager(mgr, controller.Options{
+				MaxConcurrentReconciles: concurrent,
+			}); err != nil {
+				setupLog.Error(err, errCreateController, "controller", "ClusterExternalSecret")
+				os.Exit(1)
+			}
+		}
 		setupLog.Info("starting manager")
 		if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
 			setupLog.Error(err, "problem running manager")
@@ -159,5 +178,7 @@ func init() {
 	rootCmd.Flags().IntVar(&concurrent, "concurrent", 1, "The number of concurrent ExternalSecret reconciles.")
 	rootCmd.Flags().StringVar(&loglevel, "loglevel", "info", "loglevel to use, one of: debug, info, warn, error, dpanic, panic, fatal")
 	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().DurationVar(&storeRequeueInterval, "store-requeue-interval", time.Minute*5, "Time duration between reconciling (Cluster)SecretStores")
 }

+ 366 - 0
config/crds/bases/external-secrets.io_clusterexternalsecrets.yaml

@@ -0,0 +1,366 @@
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.8.0
+  creationTimestamp: null
+  name: clusterexternalsecrets.external-secrets.io
+spec:
+  group: external-secrets.io
+  names:
+    categories:
+    - externalsecrets
+    kind: ClusterExternalSecret
+    listKind: ClusterExternalSecretList
+    plural: clusterexternalsecrets
+    shortNames:
+    - ces
+    singular: clusterexternalsecret
+  scope: Cluster
+  versions:
+  - name: v1beta1
+    schema:
+      openAPIV3Schema:
+        description: ClusterExternalSecret is the Schema for the clusterexternalsecrets
+          API.
+        properties:
+          apiVersion:
+            description: 'APIVersion defines the versioned schema of this representation
+              of an object. Servers should convert recognized schemas to the latest
+              internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+            type: string
+          kind:
+            description: 'Kind is a string value representing the REST resource this
+              object represents. Servers may infer this from the endpoint the client
+              submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+            type: string
+          metadata:
+            type: object
+          spec:
+            description: ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret.
+            properties:
+              externalSecretName:
+                description: The name of the external secrets to be created defaults
+                  to the name of the ClusterExternalSecret
+                type: string
+              externalSecretSpec:
+                description: The spec for the ExternalSecrets to be created
+                properties:
+                  data:
+                    description: Data defines the connection between the Kubernetes
+                      Secret keys and the Provider data
+                    items:
+                      description: ExternalSecretData defines the connection between
+                        the Kubernetes Secret key (spec.data.<key>) and the Provider
+                        data.
+                      properties:
+                        remoteRef:
+                          description: ExternalSecretDataRemoteRef defines Provider
+                            data location.
+                          properties:
+                            conversionStrategy:
+                              default: Default
+                              description: Used to define a conversion Strategy
+                              type: string
+                            key:
+                              description: Key is the key used in the Provider, mandatory
+                              type: string
+                            property:
+                              description: Used to select a specific property of the
+                                Provider value (if a map), if supported
+                              type: string
+                            version:
+                              description: Used to select a specific version of the
+                                Provider value, if supported
+                              type: string
+                          required:
+                          - key
+                          type: object
+                        secretKey:
+                          type: string
+                      required:
+                      - remoteRef
+                      - secretKey
+                      type: object
+                    type: array
+                  dataFrom:
+                    description: 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
+                    items:
+                      maxProperties: 1
+                      minProperties: 1
+                      properties:
+                        extract:
+                          description: Used to extract multiple key/value pairs from
+                            one secret
+                          properties:
+                            conversionStrategy:
+                              default: Default
+                              description: Used to define a conversion Strategy
+                              type: string
+                            key:
+                              description: Key is the key used in the Provider, mandatory
+                              type: string
+                            property:
+                              description: Used to select a specific property of the
+                                Provider value (if a map), if supported
+                              type: string
+                            version:
+                              description: Used to select a specific version of the
+                                Provider value, if supported
+                              type: string
+                          required:
+                          - key
+                          type: object
+                        find:
+                          description: Used to find secrets based on tags or regular
+                            expressions
+                          properties:
+                            conversionStrategy:
+                              default: Default
+                              description: Used to define a conversion Strategy
+                              type: string
+                            name:
+                              description: Finds secrets based on the name.
+                              properties:
+                                regexp:
+                                  description: Finds secrets base
+                                  type: string
+                              type: object
+                            path:
+                              description: A root path to start the find operations.
+                              type: string
+                            tags:
+                              additionalProperties:
+                                type: string
+                              description: Find secrets based on tags.
+                              type: object
+                          type: object
+                      type: object
+                    type: array
+                  refreshInterval:
+                    default: 1h
+                    description: 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.
+                    type: string
+                  secretStoreRef:
+                    description: SecretStoreRef defines which SecretStore to fetch
+                      the ExternalSecret data.
+                    properties:
+                      kind:
+                        description: Kind of the SecretStore resource (SecretStore
+                          or ClusterSecretStore) Defaults to `SecretStore`
+                        type: string
+                      name:
+                        description: Name of the SecretStore resource
+                        type: string
+                    required:
+                    - name
+                    type: object
+                  target:
+                    description: ExternalSecretTarget defines the Kubernetes Secret
+                      to be created There can be only one target per ExternalSecret.
+                    properties:
+                      creationPolicy:
+                        default: Owner
+                        description: CreationPolicy defines rules on how to create
+                          the resulting Secret Defaults to 'Owner'
+                        type: string
+                      deletionPolicy:
+                        default: None
+                        description: DeletionPolicy defines rules on how to delete
+                          the resulting Secret Defaults to 'None'
+                        type: string
+                      immutable:
+                        description: Immutable defines if the final secret will be
+                          immutable
+                        type: boolean
+                      name:
+                        description: Name defines the name of the Secret resource
+                          to be managed This field is immutable Defaults to the .metadata.name
+                          of the ExternalSecret resource
+                        type: string
+                      template:
+                        description: Template defines a blueprint for the created
+                          Secret resource.
+                        properties:
+                          data:
+                            additionalProperties:
+                              type: string
+                            type: object
+                          engineVersion:
+                            default: v2
+                            type: string
+                          metadata:
+                            description: ExternalSecretTemplateMetadata defines metadata
+                              fields for the Secret blueprint.
+                            properties:
+                              annotations:
+                                additionalProperties:
+                                  type: string
+                                type: object
+                              labels:
+                                additionalProperties:
+                                  type: string
+                                type: object
+                            type: object
+                          templateFrom:
+                            items:
+                              maxProperties: 1
+                              minProperties: 1
+                              properties:
+                                configMap:
+                                  properties:
+                                    items:
+                                      items:
+                                        properties:
+                                          key:
+                                            type: string
+                                        required:
+                                        - key
+                                        type: object
+                                      type: array
+                                    name:
+                                      type: string
+                                  required:
+                                  - items
+                                  - name
+                                  type: object
+                                secret:
+                                  properties:
+                                    items:
+                                      items:
+                                        properties:
+                                          key:
+                                            type: string
+                                        required:
+                                        - key
+                                        type: object
+                                      type: array
+                                    name:
+                                      type: string
+                                  required:
+                                  - items
+                                  - name
+                                  type: object
+                              type: object
+                            type: array
+                          type:
+                            type: string
+                        type: object
+                    type: object
+                required:
+                - secretStoreRef
+                - target
+                type: object
+              namespaceSelector:
+                description: The labels to select by to find the Namespaces to create
+                  the ExternalSecrets in.
+                properties:
+                  matchExpressions:
+                    description: matchExpressions is a list of label selector requirements.
+                      The requirements are ANDed.
+                    items:
+                      description: A label selector requirement is a selector that
+                        contains values, a key, and an operator that relates the key
+                        and values.
+                      properties:
+                        key:
+                          description: key is the label key that the selector applies
+                            to.
+                          type: string
+                        operator:
+                          description: operator represents a key's relationship to
+                            a set of values. Valid operators are In, NotIn, Exists
+                            and DoesNotExist.
+                          type: string
+                        values:
+                          description: values is an array of string values. If the
+                            operator is In or NotIn, the values array must be non-empty.
+                            If the operator is Exists or DoesNotExist, the values
+                            array must be empty. This array is replaced during a strategic
+                            merge patch.
+                          items:
+                            type: string
+                          type: array
+                      required:
+                      - key
+                      - operator
+                      type: object
+                    type: array
+                  matchLabels:
+                    additionalProperties:
+                      type: string
+                    description: matchLabels is a map of {key,value} pairs. A single
+                      {key,value} in the matchLabels map is equivalent to an element
+                      of matchExpressions, whose key field is "key", the operator
+                      is "In", and the values array contains only "value". The requirements
+                      are ANDed.
+                    type: object
+                type: object
+              refreshTime:
+                description: The time in which the controller should reconcile it's
+                  objects and recheck namespaces for labels.
+                type: string
+            required:
+            - externalSecretSpec
+            - namespaceSelector
+            type: object
+          status:
+            description: ClusterExternalSecretStatus defines the observed state of
+              ClusterExternalSecret.
+            properties:
+              conditions:
+                items:
+                  properties:
+                    message:
+                      type: string
+                    status:
+                      type: string
+                    type:
+                      type: string
+                  required:
+                  - status
+                  - type
+                  type: object
+                type: array
+              failedNamespaces:
+                description: Failed namespaces are the namespaces that failed to apply
+                  an ExternalSecret
+                items:
+                  description: ClusterExternalSecretNamespaceFailure represents a
+                    failed namespace deployment and it's reason.
+                  properties:
+                    namespace:
+                      description: Namespace is the namespace that failed when trying
+                        to apply an ExternalSecret
+                      type: string
+                    reason:
+                      description: Reason is why the ExternalSecret failed to apply
+                        to the namespace
+                      type: string
+                  required:
+                  - namespace
+                  type: object
+                type: array
+              provisionedNamespaces:
+                description: ProvisionedNamespaces are the namespaces where the ClusterExternalSecret
+                  has secrets
+                items:
+                  type: string
+                type: array
+            type: object
+        type: object
+    served: true
+    storage: true
+    subresources:
+      status: {}
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []

+ 38 - 6
config/crds/bases/external-secrets.io_clustersecretstores.yaml

@@ -353,9 +353,6 @@ spec:
                                   defaults to the namespace of the referent.
                                 type: string
                             type: object
-                        required:
-                        - clientId
-                        - clientSecret
                         type: object
                       authType:
                         default: ServicePrincipal
@@ -367,11 +364,28 @@ spec:
                         enum:
                         - ServicePrincipal
                         - ManagedIdentity
+                        - WorkloadIdentity
                         type: string
                       identityId:
                         description: If multiple Managed Identity is assigned to the
                           pod, you can select the one to be used
                         type: string
+                      serviceAccountRef:
+                        description: ServiceAccountRef specified the service account
+                          that should be used when authenticating with WorkloadIdentity.
+                        properties:
+                          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
                       tenantId:
                         description: TenantID configures the Azure Tenant to send
                           requests to. Required for ServicePrincipal auth type.
@@ -443,6 +457,8 @@ spec:
                                 type: string
                               clusterName:
                                 type: string
+                              clusterProjectID:
+                                type: string
                               serviceAccountRef:
                                 description: A reference to a ServiceAccount resource.
                                 properties:
@@ -1656,9 +1672,6 @@ spec:
                                   defaults to the namespace of the referent.
                                 type: string
                             type: object
-                        required:
-                        - clientId
-                        - clientSecret
                         type: object
                       authType:
                         default: ServicePrincipal
@@ -1670,11 +1683,28 @@ spec:
                         enum:
                         - ServicePrincipal
                         - ManagedIdentity
+                        - WorkloadIdentity
                         type: string
                       identityId:
                         description: If multiple Managed Identity is assigned to the
                           pod, you can select the one to be used
                         type: string
+                      serviceAccountRef:
+                        description: ServiceAccountRef specified the service account
+                          that should be used when authenticating with WorkloadIdentity.
+                        properties:
+                          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
                       tenantId:
                         description: TenantID configures the Azure Tenant to send
                           requests to. Required for ServicePrincipal auth type.
@@ -1746,6 +1776,8 @@ spec:
                                 type: string
                               clusterName:
                                 type: string
+                              clusterProjectID:
+                                type: string
                               serviceAccountRef:
                                 description: A reference to a ServiceAccount resource.
                                 properties:

+ 23 - 2
config/crds/bases/external-secrets.io_externalsecrets.yaml

@@ -59,6 +59,10 @@ spec:
                       description: ExternalSecretDataRemoteRef defines Provider data
                         location.
                       properties:
+                        conversionStrategy:
+                          default: Default
+                          description: Used to define a conversion Strategy
+                          type: string
                         key:
                           description: Key is the key used in the Provider, mandatory
                           type: string
@@ -87,6 +91,10 @@ spec:
                 items:
                   description: ExternalSecretDataRemoteRef defines Provider data location.
                   properties:
+                    conversionStrategy:
+                      default: Default
+                      description: Used to define a conversion Strategy
+                      type: string
                     key:
                       description: Key is the key used in the Provider, mandatory
                       type: string
@@ -294,6 +302,10 @@ spec:
                       description: ExternalSecretDataRemoteRef defines Provider data
                         location.
                       properties:
+                        conversionStrategy:
+                          default: Default
+                          description: Used to define a conversion Strategy
+                          type: string
                         key:
                           description: Key is the key used in the Provider, mandatory
                           type: string
@@ -327,6 +339,10 @@ spec:
                       description: Used to extract multiple key/value pairs from one
                         secret
                       properties:
+                        conversionStrategy:
+                          default: Default
+                          description: Used to define a conversion Strategy
+                          type: string
                         key:
                           description: Key is the key used in the Provider, mandatory
                           type: string
@@ -343,9 +359,11 @@ spec:
                       type: object
                     find:
                       description: Used to find secrets based on tags or regular expressions
-                      maxProperties: 1
-                      minProperties: 1
                       properties:
+                        conversionStrategy:
+                          default: Default
+                          description: Used to define a conversion Strategy
+                          type: string
                         name:
                           description: Finds secrets based on the name.
                           properties:
@@ -353,6 +371,9 @@ spec:
                               description: Finds secrets base
                               type: string
                           type: object
+                        path:
+                          description: A root path to start the find operations.
+                          type: string
                         tags:
                           additionalProperties:
                             type: string

+ 38 - 6
config/crds/bases/external-secrets.io_secretstores.yaml

@@ -353,9 +353,6 @@ spec:
                                   defaults to the namespace of the referent.
                                 type: string
                             type: object
-                        required:
-                        - clientId
-                        - clientSecret
                         type: object
                       authType:
                         default: ServicePrincipal
@@ -367,11 +364,28 @@ spec:
                         enum:
                         - ServicePrincipal
                         - ManagedIdentity
+                        - WorkloadIdentity
                         type: string
                       identityId:
                         description: If multiple Managed Identity is assigned to the
                           pod, you can select the one to be used
                         type: string
+                      serviceAccountRef:
+                        description: ServiceAccountRef specified the service account
+                          that should be used when authenticating with WorkloadIdentity.
+                        properties:
+                          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
                       tenantId:
                         description: TenantID configures the Azure Tenant to send
                           requests to. Required for ServicePrincipal auth type.
@@ -443,6 +457,8 @@ spec:
                                 type: string
                               clusterName:
                                 type: string
+                              clusterProjectID:
+                                type: string
                               serviceAccountRef:
                                 description: A reference to a ServiceAccount resource.
                                 properties:
@@ -1659,9 +1675,6 @@ spec:
                                   defaults to the namespace of the referent.
                                 type: string
                             type: object
-                        required:
-                        - clientId
-                        - clientSecret
                         type: object
                       authType:
                         default: ServicePrincipal
@@ -1673,11 +1686,28 @@ spec:
                         enum:
                         - ServicePrincipal
                         - ManagedIdentity
+                        - WorkloadIdentity
                         type: string
                       identityId:
                         description: If multiple Managed Identity is assigned to the
                           pod, you can select the one to be used
                         type: string
+                      serviceAccountRef:
+                        description: ServiceAccountRef specified the service account
+                          that should be used when authenticating with WorkloadIdentity.
+                        properties:
+                          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
                       tenantId:
                         description: TenantID configures the Azure Tenant to send
                           requests to. Required for ServicePrincipal auth type.
@@ -1749,6 +1779,8 @@ spec:
                                 type: string
                               clusterName:
                                 type: string
+                              clusterProjectID:
+                                type: string
                               serviceAccountRef:
                                 description: A reference to a ServiceAccount resource.
                                 properties:

+ 6 - 0
deploy/charts/external-secrets/README.md

@@ -36,6 +36,7 @@ The command removes all the Kubernetes components associated with the chart and
 |-----|------|---------|-------------|
 | affinity | object | `{}` |  |
 | certController.affinity | object | `{}` |  |
+| certController.create | bool | `true` | Specifies whether a certificate controller deployment be created. |
 | certController.deploymentAnnotations | object | `{}` | Annotations to add to Deployment |
 | certController.extraArgs | object | `{}` |  |
 | certController.extraEnv | list | `[]` |  |
@@ -62,6 +63,7 @@ The command removes all the Kubernetes components associated with the chart and
 | certController.tolerations | list | `[]` |  |
 | 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. |
+| createOperator | bool | `true` | Specifies whether an external secret operator deployment be created. |
 | deploymentAnnotations | object | `{}` | Annotations to add to Deployment |
 | extraArgs | object | `{}` |  |
 | extraEnv | list | `[]` |  |
@@ -78,12 +80,15 @@ The command removes all the Kubernetes components associated with the chart and
 | podLabels | object | `{}` |  |
 | podSecurityContext | object | `{}` |  |
 | priorityClassName | string | `""` | Pod priority class name. |
+| processClusterExternalSecret | bool | `true` | if true, the operator will process cluster external secret. Else, it will ignore them. |
+| processClusterStore | bool | `true` | if true, the operator will process cluster store. Else, it will ignore them. |
 | prometheus.enabled | bool | `false` | Specifies whether to expose Service resource for collecting Prometheus metrics |
 | prometheus.service.port | int | `8080` |  |
 | rbac.create | bool | `true` | Specifies whether role and rolebinding resources should be created. |
 | replicaCount | int | `1` |  |
 | resources | object | `{}` |  |
 | scopedNamespace | string | `""` | If set external secrets are only reconciled in the provided namespace |
+| scopedRBAC | bool | `false` | Must be used with scopedNamespace. If true, create scoped RBAC roles under the scoped namespace and implicitly disable cluster stores and cluster external secrets |
 | securityContext | object | `{}` |  |
 | serviceAccount.annotations | object | `{}` | Annotations to add to the service account. |
 | serviceAccount.create | bool | `true` | Specifies whether a service account should be created. |
@@ -92,6 +97,7 @@ The command removes all the Kubernetes components associated with the chart and
 | webhook.affinity | object | `{}` |  |
 | webhook.certCheckInterval | string | `"5m"` |  |
 | webhook.certDir | string | `"/tmp/certs"` |  |
+| webhook.create | bool | `true` | Specifies whether a webhook deployment be created. |
 | webhook.deploymentAnnotations | object | `{}` | Annotations to add to Deployment |
 | webhook.extraArgs | object | `{}` |  |
 | webhook.extraEnv | list | `[]` |  |

+ 2 - 0
deploy/charts/external-secrets/templates/cert-controller-deployment.yaml

@@ -1,3 +1,4 @@
+{{- if .Values.certController.create }}
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -90,3 +91,4 @@ spec:
       {{- if .Values.certController.priorityClassName }}
       priorityClassName: {{ .Values.certController.priorityClassName }}
       {{- end }}
+{{- end }}

+ 1 - 1
deploy/charts/external-secrets/templates/cert-controller-service.yaml

@@ -1,4 +1,4 @@
-{{- if .Values.certController.prometheus.enabled }}
+{{- if and .Values.certController.create .Values.certController.prometheus.enabled }}
 apiVersion: v1
 kind: Service
 metadata:

+ 1 - 1
deploy/charts/external-secrets/templates/cert-controller-serviceaccount.yaml

@@ -1,4 +1,4 @@
-{{- if .Values.certController.serviceAccount.create -}}
+{{- if and .Values.certController.create .Values.certController.serviceAccount.create -}}
 apiVersion: v1
 kind: ServiceAccount
 metadata:

+ 14 - 1
deploy/charts/external-secrets/templates/deployment.yaml

@@ -1,3 +1,4 @@
+{{- if .Values.createOperator }}
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -43,7 +44,7 @@ spec:
           {{- end }}
           image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
           imagePullPolicy: {{ .Values.image.pullPolicy }}
-          {{- if or (.Values.leaderElect) (.Values.scopedNamespace) (.Values.concurrent) (.Values.extraArgs) }}
+          {{- if or (.Values.leaderElect) (.Values.scopedNamespace) (.Values.processClusterStore) (.Values.processClusterExternalSecret) (.Values.concurrent) (.Values.extraArgs) }}
           args:
           {{- if .Values.leaderElect }}
           - --enable-leader-election=true
@@ -51,6 +52,17 @@ spec:
           {{- if .Values.scopedNamespace }}
           - --namespace={{ .Values.scopedNamespace }}
           {{- end }}
+          {{- if and .Values.scopedNamespace .Values.scopedRBAC }}
+          - --enable-cluster-store-reconciler=false
+          - --enable-cluster-external-secret-reconciler=false
+          {{- else }}
+            {{- if not .Values.processClusterStore }}
+          - --enable-cluster-store-reconciler=false
+            {{- end }}
+            {{- if not .Values.processClusterExternalSecret }}
+          - --enable-cluster-external-secret-reconciler=false
+            {{- end }}
+          {{- end }}
           {{- if .Values.controllerClass }}
           - --controller-class={{ .Values.controllerClass }}
           {{- end }}
@@ -92,3 +104,4 @@ spec:
       {{- if .Values.priorityClassName }}
       priorityClassName: {{ .Values.priorityClassName }}
       {{- end }}
+{{- end }}

+ 44 - 0
deploy/charts/external-secrets/templates/rbac.yaml

@@ -1,8 +1,15 @@
 {{- if .Values.rbac.create -}}
 apiVersion: rbac.authorization.k8s.io/v1
+{{- if and .Values.scopedNamespace .Values.scopedRBAC }}
+kind: Role
+{{- else }}
 kind: ClusterRole
+{{- end }}
 metadata:
   name: {{ include "external-secrets.fullname" . }}-controller
+  {{- if and .Values.scopedNamespace .Values.scopedRBAC }}
+  namespace: {{ .Values.scopedNamespace | quote }}
+  {{- end }}
   labels:
     {{- include "external-secrets.labels" . | nindent 4 }}
 rules:
@@ -12,6 +19,7 @@ rules:
     - "secretstores"
     - "clustersecretstores"
     - "externalsecrets"
+    - "clusterexternalsecrets"
     verbs:
     - "get"
     - "list"
@@ -28,6 +36,9 @@ rules:
     - "clustersecretstores"
     - "clustersecretstores/status"
     - "clustersecretstores/finalizers"
+    - "clusterexternalsecrets"
+    - "clusterexternalsecrets/status"
+    - "clusterexternalsecrets/finalizers"
     verbs:
     - "update"
     - "patch"
@@ -35,6 +46,7 @@ rules:
     - ""
     resources:
     - "serviceaccounts"
+    - "namespaces"
     verbs:
     - "get"
     - "list"
@@ -72,11 +84,25 @@ rules:
     verbs:
     - "create"
     - "patch"
+  - apiGroups:
+    - "external-secrets.io"
+    resources:
+    - "externalsecrets"
+    verbs:
+    - "create"
+    - "update"
 ---
 apiVersion: rbac.authorization.k8s.io/v1
+{{- if and .Values.scopedNamespace .Values.scopedRBAC }}
+kind: Role
+{{- else }}
 kind: ClusterRole
+{{- end }}
 metadata:
   name: {{ include "external-secrets.fullname" . }}-view
+  {{- if and .Values.scopedNamespace .Values.scopedRBAC }}
+  namespace: {{ .Values.scopedNamespace | quote }}
+  {{- end }}
   labels:
     {{- include "external-secrets.labels" . | nindent 4 }}
     rbac.authorization.k8s.io/aggregate-to-view: "true"
@@ -95,9 +121,16 @@ rules:
       - "list"
 ---
 apiVersion: rbac.authorization.k8s.io/v1
+{{- if and .Values.scopedNamespace .Values.scopedRBAC }}
+kind: Role
+{{- else }}
 kind: ClusterRole
+{{- end }}
 metadata:
   name: {{ include "external-secrets.fullname" . }}-edit
+  {{- if and .Values.scopedNamespace .Values.scopedRBAC }}
+  namespace: {{ .Values.scopedNamespace | quote }}
+  {{- end }}
   labels:
     {{- include "external-secrets.labels" . | nindent 4 }}
     rbac.authorization.k8s.io/aggregate-to-edit: "true"
@@ -117,14 +150,25 @@ rules:
       - "update"
 ---
 apiVersion: rbac.authorization.k8s.io/v1
+{{- if and .Values.scopedNamespace .Values.scopedRBAC }}
+kind: RoleBinding
+{{- else }}
 kind: ClusterRoleBinding
+{{- end }}
 metadata:
   name: {{ include "external-secrets.fullname" . }}-controller
+  {{- if and .Values.scopedNamespace .Values.scopedRBAC }}
+  namespace: {{ .Values.scopedNamespace | quote }}
+  {{- end }}
   labels:
     {{- include "external-secrets.labels" . | nindent 4 }}
 roleRef:
   apiGroup: rbac.authorization.k8s.io
+  {{- if and .Values.scopedNamespace .Values.scopedRBAC }}
+  kind: Role
+  {{- else }}
   kind: ClusterRole
+  {{- end }}
   name: {{ include "external-secrets.fullname" . }}-controller
 subjects:
   - name: {{ include "external-secrets.serviceAccountName" . }}

+ 1 - 0
deploy/charts/external-secrets/templates/service.yaml

@@ -3,6 +3,7 @@ apiVersion: v1
 kind: Service
 metadata:
   name: {{ include "external-secrets.fullname" . }}-metrics
+  namespace: {{ .Release.Namespace | quote }}
   labels:
     {{- include "external-secrets.labels" . | nindent 4 }}
   annotations:

+ 2 - 0
deploy/charts/external-secrets/templates/validatingwebhook.yaml

@@ -1,3 +1,4 @@
+{{- if .Values.webhook.create }}
 apiVersion: admissionregistration.k8s.io/v1
 kind: ValidatingWebhookConfiguration
 metadata:
@@ -39,3 +40,4 @@ webhooks:
   admissionReviewVersions: ["v1", "v1beta1"]
   sideEffects: None
   timeoutSeconds: 5
+{{- end }}

+ 2 - 0
deploy/charts/external-secrets/templates/webhook-deployment.yaml

@@ -1,3 +1,4 @@
+{{- if .Values.webhook.create }}
 apiVersion: apps/v1
 kind: Deployment
 metadata:
@@ -99,3 +100,4 @@ spec:
       {{- if .Values.webhook.priorityClassName }}
       priorityClassName: {{ .Values.webhook.priorityClassName }}
       {{- end }}
+{{- end }}

+ 2 - 0
deploy/charts/external-secrets/templates/webhook-secret.yaml

@@ -1,3 +1,4 @@
+{{- if .Values.webhook.create }}
 apiVersion: v1
 kind: Secret
 metadata:
@@ -6,3 +7,4 @@ metadata:
   labels:
     {{- include "external-secrets-webhook.labels" . | nindent 4 }}
     external-secrets.io/component : webhook
+{{- end }}

+ 2 - 0
deploy/charts/external-secrets/templates/webhook-service.yaml

@@ -1,3 +1,4 @@
+{{- if .Values.webhook.create }}
 apiVersion: v1
 kind: Service
 metadata:
@@ -27,3 +28,4 @@ spec:
   {{- end }}
   selector:
     {{- include "external-secrets-webhook.selectorLabels" . | nindent 4 }}
+{{- end }}

+ 1 - 1
deploy/charts/external-secrets/templates/webhook-serviceaccount.yaml

@@ -1,4 +1,4 @@
-{{- if .Values.webhook.serviceAccount.create -}}
+{{- if and .Values.webhook.create .Values.webhook.serviceAccount.create -}}
 apiVersion: v1
 kind: ServiceAccount
 metadata:

+ 17 - 0
deploy/charts/external-secrets/values.yaml

@@ -25,6 +25,19 @@ controllerClass: ""
 # provided namespace
 scopedNamespace: ""
 
+# -- Must be used with scopedNamespace. If true, create scoped RBAC roles under the scoped namespace
+# and implicitly disable cluster stores and cluster external secrets
+scopedRBAC: false
+
+# -- if true, the operator will process cluster external secret. Else, it will ignore them.
+processClusterExternalSecret: true
+
+# -- if true, the operator will process cluster store. Else, it will ignore them.
+processClusterStore: true
+
+# -- Specifies whether an external secret operator deployment be created.
+createOperator: true
+
 # -- Specifies the number of concurrent ExternalSecret Reconciles external-secret executes at
 # a time.
 concurrent: 1
@@ -88,6 +101,8 @@ affinity: {}
 priorityClassName: ""
 
 webhook:
+  # -- Specifies whether a webhook deployment be created.
+  create: true
   certCheckInterval: "5m"
   replicaCount: 1
   certDir: /tmp/certs
@@ -154,6 +169,8 @@ webhook:
       #   memory: 32Mi
 
 certController:
+  # -- Specifies whether a certificate controller deployment be created.
+  create: true
   requeueInterval: "5m"
   image:
     repository: ghcr.io/external-secrets/external-secrets

+ 408 - 14
deploy/crds/bundle.yaml

@@ -1,5 +1,330 @@
 apiVersion: apiextensions.k8s.io/v1
 kind: CustomResourceDefinition
+metadata:
+  annotations:
+    controller-gen.kubebuilder.io/version: v0.8.0
+  creationTimestamp: null
+  name: clusterexternalsecrets.external-secrets.io
+spec:
+  group: external-secrets.io
+  names:
+    categories:
+      - externalsecrets
+    kind: ClusterExternalSecret
+    listKind: ClusterExternalSecretList
+    plural: clusterexternalsecrets
+    shortNames:
+      - ces
+    singular: clusterexternalsecret
+  scope: Cluster
+  versions:
+    - name: v1beta1
+      schema:
+        openAPIV3Schema:
+          description: ClusterExternalSecret is the Schema for the clusterexternalsecrets API.
+          properties:
+            apiVersion:
+              description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+              type: string
+            kind:
+              description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+              type: string
+            metadata:
+              type: object
+            spec:
+              description: ClusterExternalSecretSpec defines the desired state of ClusterExternalSecret.
+              properties:
+                externalSecretName:
+                  description: The name of the external secrets to be created defaults to the name of the ClusterExternalSecret
+                  type: string
+                externalSecretSpec:
+                  description: The spec for the ExternalSecrets to be created
+                  properties:
+                    data:
+                      description: Data defines the connection between the Kubernetes Secret keys and the Provider data
+                      items:
+                        description: ExternalSecretData defines the connection between the Kubernetes Secret key (spec.data.<key>) and the Provider data.
+                        properties:
+                          remoteRef:
+                            description: ExternalSecretDataRemoteRef defines Provider data location.
+                            properties:
+                              conversionStrategy:
+                                default: Default
+                                description: Used to define a conversion Strategy
+                                type: string
+                              key:
+                                description: Key is the key used in the Provider, mandatory
+                                type: string
+                              property:
+                                description: Used to select a specific property of the Provider value (if a map), if supported
+                                type: string
+                              version:
+                                description: Used to select a specific version of the Provider value, if supported
+                                type: string
+                            required:
+                              - key
+                            type: object
+                          secretKey:
+                            type: string
+                        required:
+                          - remoteRef
+                          - secretKey
+                        type: object
+                      type: array
+                    dataFrom:
+                      description: 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
+                      items:
+                        maxProperties: 1
+                        minProperties: 1
+                        properties:
+                          extract:
+                            description: Used to extract multiple key/value pairs from one secret
+                            properties:
+                              conversionStrategy:
+                                default: Default
+                                description: Used to define a conversion Strategy
+                                type: string
+                              key:
+                                description: Key is the key used in the Provider, mandatory
+                                type: string
+                              property:
+                                description: Used to select a specific property of the Provider value (if a map), if supported
+                                type: string
+                              version:
+                                description: Used to select a specific version of the Provider value, if supported
+                                type: string
+                            required:
+                              - key
+                            type: object
+                          find:
+                            description: Used to find secrets based on tags or regular expressions
+                            properties:
+                              conversionStrategy:
+                                default: Default
+                                description: Used to define a conversion Strategy
+                                type: string
+                              name:
+                                description: Finds secrets based on the name.
+                                properties:
+                                  regexp:
+                                    description: Finds secrets base
+                                    type: string
+                                type: object
+                              path:
+                                description: A root path to start the find operations.
+                                type: string
+                              tags:
+                                additionalProperties:
+                                  type: string
+                                description: Find secrets based on tags.
+                                type: object
+                            type: object
+                        type: object
+                      type: array
+                    refreshInterval:
+                      default: 1h
+                      description: 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.
+                      type: string
+                    secretStoreRef:
+                      description: SecretStoreRef defines which SecretStore to fetch the ExternalSecret data.
+                      properties:
+                        kind:
+                          description: Kind of the SecretStore resource (SecretStore or ClusterSecretStore) Defaults to `SecretStore`
+                          type: string
+                        name:
+                          description: Name of the SecretStore resource
+                          type: string
+                      required:
+                        - name
+                      type: object
+                    target:
+                      description: ExternalSecretTarget defines the Kubernetes Secret to be created There can be only one target per ExternalSecret.
+                      properties:
+                        creationPolicy:
+                          default: Owner
+                          description: CreationPolicy defines rules on how to create the resulting Secret Defaults to 'Owner'
+                          type: string
+                        deletionPolicy:
+                          default: None
+                          description: DeletionPolicy defines rules on how to delete the resulting Secret Defaults to 'None'
+                          type: string
+                        immutable:
+                          description: Immutable defines if the final secret will be immutable
+                          type: boolean
+                        name:
+                          description: Name defines the name of the Secret resource to be managed This field is immutable Defaults to the .metadata.name of the ExternalSecret resource
+                          type: string
+                        template:
+                          description: Template defines a blueprint for the created Secret resource.
+                          properties:
+                            data:
+                              additionalProperties:
+                                type: string
+                              type: object
+                            engineVersion:
+                              default: v2
+                              type: string
+                            metadata:
+                              description: ExternalSecretTemplateMetadata defines metadata fields for the Secret blueprint.
+                              properties:
+                                annotations:
+                                  additionalProperties:
+                                    type: string
+                                  type: object
+                                labels:
+                                  additionalProperties:
+                                    type: string
+                                  type: object
+                              type: object
+                            templateFrom:
+                              items:
+                                maxProperties: 1
+                                minProperties: 1
+                                properties:
+                                  configMap:
+                                    properties:
+                                      items:
+                                        items:
+                                          properties:
+                                            key:
+                                              type: string
+                                          required:
+                                            - key
+                                          type: object
+                                        type: array
+                                      name:
+                                        type: string
+                                    required:
+                                      - items
+                                      - name
+                                    type: object
+                                  secret:
+                                    properties:
+                                      items:
+                                        items:
+                                          properties:
+                                            key:
+                                              type: string
+                                          required:
+                                            - key
+                                          type: object
+                                        type: array
+                                      name:
+                                        type: string
+                                    required:
+                                      - items
+                                      - name
+                                    type: object
+                                type: object
+                              type: array
+                            type:
+                              type: string
+                          type: object
+                      type: object
+                  required:
+                    - secretStoreRef
+                    - target
+                  type: object
+                namespaceSelector:
+                  description: The labels to select by to find the Namespaces to create the ExternalSecrets in.
+                  properties:
+                    matchExpressions:
+                      description: matchExpressions is a list of label selector requirements. The requirements are ANDed.
+                      items:
+                        description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.
+                        properties:
+                          key:
+                            description: key is the label key that the selector applies to.
+                            type: string
+                          operator:
+                            description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist.
+                            type: string
+                          values:
+                            description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.
+                            items:
+                              type: string
+                            type: array
+                        required:
+                          - key
+                          - operator
+                        type: object
+                      type: array
+                    matchLabels:
+                      additionalProperties:
+                        type: string
+                      description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed.
+                      type: object
+                  type: object
+                refreshTime:
+                  description: The time in which the controller should reconcile it's objects and recheck namespaces for labels.
+                  type: string
+              required:
+                - externalSecretSpec
+                - namespaceSelector
+              type: object
+            status:
+              description: ClusterExternalSecretStatus defines the observed state of ClusterExternalSecret.
+              properties:
+                conditions:
+                  items:
+                    properties:
+                      message:
+                        type: string
+                      status:
+                        type: string
+                      type:
+                        type: string
+                    required:
+                      - status
+                      - type
+                    type: object
+                  type: array
+                failedNamespaces:
+                  description: Failed namespaces are the namespaces that failed to apply an ExternalSecret
+                  items:
+                    description: ClusterExternalSecretNamespaceFailure represents a failed namespace deployment and it's reason.
+                    properties:
+                      namespace:
+                        description: Namespace is the namespace that failed when trying to apply an ExternalSecret
+                        type: string
+                      reason:
+                        description: Reason is why the ExternalSecret failed to apply to the namespace
+                        type: string
+                    required:
+                      - namespace
+                    type: object
+                  type: array
+                provisionedNamespaces:
+                  description: ProvisionedNamespaces are the namespaces where the ClusterExternalSecret has secrets
+                  items:
+                    type: string
+                  type: array
+              type: object
+          type: object
+      served: true
+      storage: true
+      subresources:
+        status: {}
+  conversion:
+    strategy: Webhook
+    webhook:
+      conversionReviewVersions:
+        - v1
+      clientConfig:
+        caBundle: Cg==
+        service:
+          name: kubernetes
+          namespace: default
+          path: /convert
+status:
+  acceptedNames:
+    kind: ""
+    plural: ""
+  conditions: []
+  storedVersions: []
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
 metadata:
   annotations:
     controller-gen.kubebuilder.io/version: v0.8.0
@@ -260,9 +585,6 @@ spec:
                                   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
-                          required:
-                            - clientId
-                            - clientSecret
                           type: object
                         authType:
                           default: ServicePrincipal
@@ -270,10 +592,23 @@ spec:
                           enum:
                             - ServicePrincipal
                             - ManagedIdentity
+                            - WorkloadIdentity
                           type: string
                         identityId:
                           description: If multiple Managed Identity is assigned to the pod, you can select the one to be used
                           type: string
+                        serviceAccountRef:
+                          description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity.
+                          properties:
+                            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
                         tenantId:
                           description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type.
                           type: string
@@ -334,6 +669,8 @@ spec:
                                   type: string
                                 clusterName:
                                   type: string
+                                clusterProjectID:
+                                  type: string
                                 serviceAccountRef:
                                   description: A reference to a ServiceAccount resource.
                                   properties:
@@ -1221,9 +1558,6 @@ spec:
                                   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
-                          required:
-                            - clientId
-                            - clientSecret
                           type: object
                         authType:
                           default: ServicePrincipal
@@ -1231,10 +1565,23 @@ spec:
                           enum:
                             - ServicePrincipal
                             - ManagedIdentity
+                            - WorkloadIdentity
                           type: string
                         identityId:
                           description: If multiple Managed Identity is assigned to the pod, you can select the one to be used
                           type: string
+                        serviceAccountRef:
+                          description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity.
+                          properties:
+                            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
                         tenantId:
                           description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type.
                           type: string
@@ -1295,6 +1642,8 @@ spec:
                                   type: string
                                 clusterName:
                                   type: string
+                                clusterProjectID:
+                                  type: string
                                 serviceAccountRef:
                                   description: A reference to a ServiceAccount resource.
                                   properties:
@@ -2015,6 +2364,10 @@ spec:
                       remoteRef:
                         description: ExternalSecretDataRemoteRef defines Provider data location.
                         properties:
+                          conversionStrategy:
+                            default: Default
+                            description: Used to define a conversion Strategy
+                            type: string
                           key:
                             description: Key is the key used in the Provider, mandatory
                             type: string
@@ -2039,6 +2392,10 @@ spec:
                   items:
                     description: ExternalSecretDataRemoteRef defines Provider data location.
                     properties:
+                      conversionStrategy:
+                        default: Default
+                        description: Used to define a conversion Strategy
+                        type: string
                       key:
                         description: Key is the key used in the Provider, mandatory
                         type: string
@@ -2222,6 +2579,10 @@ spec:
                       remoteRef:
                         description: ExternalSecretDataRemoteRef defines Provider data location.
                         properties:
+                          conversionStrategy:
+                            default: Default
+                            description: Used to define a conversion Strategy
+                            type: string
                           key:
                             description: Key is the key used in the Provider, mandatory
                             type: string
@@ -2250,6 +2611,10 @@ spec:
                       extract:
                         description: Used to extract multiple key/value pairs from one secret
                         properties:
+                          conversionStrategy:
+                            default: Default
+                            description: Used to define a conversion Strategy
+                            type: string
                           key:
                             description: Key is the key used in the Provider, mandatory
                             type: string
@@ -2264,9 +2629,11 @@ spec:
                         type: object
                       find:
                         description: Used to find secrets based on tags or regular expressions
-                        maxProperties: 1
-                        minProperties: 1
                         properties:
+                          conversionStrategy:
+                            default: Default
+                            description: Used to define a conversion Strategy
+                            type: string
                           name:
                             description: Finds secrets based on the name.
                             properties:
@@ -2274,6 +2641,9 @@ spec:
                                 description: Finds secrets base
                                 type: string
                             type: object
+                          path:
+                            description: A root path to start the find operations.
+                            type: string
                           tags:
                             additionalProperties:
                               type: string
@@ -2701,9 +3071,6 @@ spec:
                                   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
-                          required:
-                            - clientId
-                            - clientSecret
                           type: object
                         authType:
                           default: ServicePrincipal
@@ -2711,10 +3078,23 @@ spec:
                           enum:
                             - ServicePrincipal
                             - ManagedIdentity
+                            - WorkloadIdentity
                           type: string
                         identityId:
                           description: If multiple Managed Identity is assigned to the pod, you can select the one to be used
                           type: string
+                        serviceAccountRef:
+                          description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity.
+                          properties:
+                            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
                         tenantId:
                           description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type.
                           type: string
@@ -2775,6 +3155,8 @@ spec:
                                   type: string
                                 clusterName:
                                   type: string
+                                clusterProjectID:
+                                  type: string
                                 serviceAccountRef:
                                   description: A reference to a ServiceAccount resource.
                                   properties:
@@ -3665,9 +4047,6 @@ spec:
                                   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
-                          required:
-                            - clientId
-                            - clientSecret
                           type: object
                         authType:
                           default: ServicePrincipal
@@ -3675,10 +4054,23 @@ spec:
                           enum:
                             - ServicePrincipal
                             - ManagedIdentity
+                            - WorkloadIdentity
                           type: string
                         identityId:
                           description: If multiple Managed Identity is assigned to the pod, you can select the one to be used
                           type: string
+                        serviceAccountRef:
+                          description: ServiceAccountRef specified the service account that should be used when authenticating with WorkloadIdentity.
+                          properties:
+                            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
                         tenantId:
                           description: TenantID configures the Azure Tenant to send requests to. Required for ServicePrincipal auth type.
                           type: string
@@ -3739,6 +4131,8 @@ spec:
                                   type: string
                                 clusterName:
                                   type: string
+                                clusterProjectID:
+                                  type: string
                                 serviceAccountRef:
                                   description: A reference to a ServiceAccount resource.
                                   properties:

+ 185 - 0
design/001-secretsink.md

@@ -0,0 +1,185 @@
+```yaml
+---
+title: SecretSink
+version: v1alpha1
+authors: 
+creation-date: 2022-01-25
+status: draft
+---
+```
+
+# Secret Sink
+
+## Table of Contents
+
+<!-- toc -->
+// autogen please
+<!-- /toc -->
+
+
+## Summary
+The Secret Sink is a feature to allow Secrets from Kubernetes to be saved back into some providers. Where ExternalSecret is responsible to download a Secret from a Provider into Kubernetes (as a K8s Secret), SecretSink will upload a Kubernetes Secret to a Provider.
+
+## Motivation
+Secret Sink allows some inCluster generated secrets to also be available on a given secret provider. It also allows multiple Providers having the same secret (which means a way to perform failover in case a given secret provider is on downtime or compromised for whatever the reason).
+
+### Goals
+- CRD Design for the SecretSink
+- Define the need for a SinkStore
+- 
+### Non-Goals
+Do not implement full compatibility mechanisms with each provider (we are not Terraform neither Crossplane)
+
+### Terminology
+- Sink object: any Secret (a part or the whole secret) from Kubernetes that is going to be uploaded to a Provider.
+## Proposal
+
+A controller that checks for Sink Objects, gets K8s Secrets and creates the equivalent secret on the SecretStore Provider.
+
+### User Stories
+1. As an ESO Operator I want to be able to Sync Secrets in my cluster with my External Provider
+1. As an ESO Operator I want to be able to Sync Secrets even if they are not bound to a given ExternalSecret
+
+### API
+Proposed CRD changes:
+
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: SecretStore
+metadata:
+  name: example
+  namespace: example-ns
+spec:
+  controller: dev
+  retrySettings:
+    maxRetries: 5
+    retryInterval: "10s"
+  provider:
+    aws:
+      service: SecretsManager
+      role: iam-role
+      region: eu-central-1
+      encryptionConfig: {} # Specific config for Creating Secrets on AWS
+      auth:
+        secretRef:
+          accessKeyID:
+            name: awssm-secret
+            key: access-key
+          secretAccessKey:
+            name: awssm-secret
+            key: secret-access-key
+    vault:
+      server: "https://vault.acme.org"
+      path: "secret"
+      version: "v2"
+      encryptionConfig: {} # Specific config for Creating Secrets on Vault ()
+      namespace: "a-team"
+      caBundle: "..."
+      caProvider:
+        type: "Secret"
+        name: "my-cert-secret"
+        key: "cert-key"
+      auth:
+        tokenSecretRef:
+          name: "my-secret"
+          namespace: "secret-admin"
+          key: "vault-token"
+        appRole:
+          path: "approle"
+          roleId: "db02de05-fa39-4855-059b-67221c5c2f63"
+          secretRef:
+            name: "my-secret"
+            namespace: "secret-admin"
+            key: "vault-token"
+        kubernetes:
+          mountPath: "kubernetes"
+          role: "demo"
+          serviceAccountRef:
+            name: "my-sa"
+            namespace: "secret-admin"
+          secretRef:
+            name: "my-secret"
+            namespace: "secret-admin"
+            key: "vault"
+    gcpsm:
+      encryptionConfig: {} # Specific config for Creating Secrets GCP SM
+      auth:
+        secretRef:
+          secretAccessKeySecretRef:
+            name: gcpsm-secret
+            key: secret-access-credentials
+      projectID: myproject
+```
+
+```yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: SecretSink
+metadata:
+  name: "hello-world"
+  namespace: my-ns # Same of the SecretStores
+spec:
+  secretStoreRefs:
+  - name: secret-store
+    kind: SecretStore
+  - name: secret-store-2
+    kind: SecretStore
+  - name: cluster-secret-store
+    kind: ClusterSecretStore
+  refreshInterval: "1h"
+  selector:
+    secret:
+      name: foobar
+  data:
+    match:
+    - secretKey: foobar
+      remoteRefs:
+      - remoteKey: my/path/foobar 
+        property: my-property #optional. To allow coming back from a 'dataFrom'
+      - remoteKey: secret/my-path-foobar
+        property: another-property
+    rewrite:
+    - secretKey: game-(.+).(.+)
+      remoteRefs:
+      - remoteKey: my/path/($1) 
+        property: prop-($2)
+      - remoteKey: my-path-($1)-($2) #Applies this way to all other secretStores
+
+status:
+  refreshTime: "2019-08-12T12:33:02Z"
+  conditions:
+  - type: Ready
+    status: "True" 
+    reason: "SecretSynced"
+    message: "Secret was synced" #Fully synced
+    lastTransitionTime: "2019-08-12T12:33:02Z"
+  - type: Ready
+    status: "True"
+    reason: "SecretSyncError"
+    message: "Secret sync failed to Sink SecretStore: abc"
+    lastTransitionTime: "2019-08-12T12:33:02Z"
+  - type: Ready
+    status: "False"
+    reason: "SecretSyncError"
+    message: "Secret sync failed to Sink SecretStore: abc, def"
+    lastTransitionTime: "2019-08-12T12:33:02Z"
+```
+
+### Behavior
+When checking SecretSink for the Source Secret, check existing labels for SecretStore reference of that particular Secret. If this SecretStore reference is an object in SecretSink SecretStore lists, a SecretSyncError should be emited as we cannot sync the secret to the same SecretStore.
+
+If the SecretStores are all fine or if the Secret has no labels (secret created by user / another tool), for Each SecretStore, get the SyncState of this store (New, SecretSynced, SecretSyncedErr).
+
+If new Secret, or SecretSynced with refreshInterval expired, get the secret from the secretStore and see if it matches the content of the secrets. If it doesn't match, create a new secret (bumping the version, if possible) within the provider. On errors, emit SecretSyncedErr.
+
+### Drawbacks
+
+We had several discussions on how to implement this feature, and it turns out just by typing how many duplicate fields we would have defeated my original issue to have two separate CRDs. The biggest drawback of this solution is that it implies SecretStores to be able to write with no other mechanism available. Also, it might overload the reconciliation loop as we have 1xN secret Syncing, where most of them are actually outside the cluster.
+
+### Acceptance Criteria
++ ExternalSecrets create appropriate labels on generated Secrets
++ SecretSinks can read labels on source Secrets
++ SecretSinks cannot have same references to SecretStores
++ SecretSinks respect refreshInterval
+## Alternatives
+Using some integration with Crossplane can allow to sync the secrets. Cons is this must be either manual or through some integration that would be an independent project on its own.
+

+ 118 - 0
design/cluster-external-secret-spec.md

@@ -0,0 +1,118 @@
+```yaml
+---
+title: Adding Cluster External Secrets
+version: v1alpha1
+authors: Daniel "ADustyOldMuffin" Hix
+creation-date: 2020-09-01
+status: draft
+---
+```
+
+# Adding Cluster External Secrets
+
+## Table of Contents
+
+<!-- toc -->
+- [Adding Cluster External Secrets](#adding-cluster-external-secrets)
+  - [Table of Contents](#table-of-contents)
+  - [Summary](#summary)
+  - [Motivation](#motivation)
+    - [Goals](#goals)
+    - [Non-Goals](#non-goals)
+  - [Proposal](#proposal)
+    - [User Stories](#user-stories)
+    - [API](#api)
+    - [Behavior](#behavior)
+    - [Drawbacks](#drawbacks)
+    - [Acceptance Criteria](#acceptance-criteria)
+  - [Alternatives](#alternatives)
+<!-- /toc -->
+
+
+## Summary
+This would provide a way to template an `ExternalSecret` and based on a namespace selector in the CRD it would generate `ExternalSecret`s in matching namespaces.
+
+## Motivation
+It's a pain point to have to create a `Secret`/`ExternalSecret` in every namespace where it's needed, and this would provide a way to to do this easily. Another motivation is the possible creation of a Kubernetes secret provider which would provide an `ExternalSecret` from a secret already located in cluster. This in combination with that provider could provide a way to sync a secret across namespaces with a single provider call.
+
+### Goals
+To provide a way to deploy multiple `ExternalSecret`s with a single CRD.
+
+### Non-Goals
+Lower provider calls based on the created `ExternalSecrets` or manage their sync.
+
+## Proposal
+
+### User Stories
+As an ESO User I would like to create the same `Secret`/`ExternalSecret` in multiple namespaces.
+
+### API
+``` yaml
+apiVersion: external-secrets.io/v1alpha1
+kind: ClusterExternalSecret
+metadata:
+  name: testing-secret
+spec:
+  # The selector used to select the namespaces to deploy to
+  namespaceSelector:
+    matchLabels:
+      foo: bar
+  # This spec is the exact same spec as an ExternalSecret, and is used as a template
+  externalSecretSpec:
+    refreshInterval: "15s"
+    secretStoreRef:
+      name: some-provider
+      kind: ClusterSecretStore
+    target:
+      name: my-cool-new-secret
+      creationPolicy: Owner
+    data:
+    - secretKey: my-cool-new-secret-key
+      remoteRef:
+        key: test
+        property: foo
+```
+
+### Behavior
+When created the controller will find namespaces via a label selector, and matching namespaces will then have an `ExternalSecret` deployed to them.
+
+Edge cases are, 
+
+1. namespaces being labeled after creation - currently handled via a re-queue interval, but the interval is a tad high and the changes won't take place right away. --
+This has been handled by adding a refreshInterval to the spec which can be defined and controls when the controller is requeued.
+
+1. Template being changed after deployment - Handled via the `createOrUpdate` function which should reconcile the `ExternalSecrets`
+
+### Drawbacks
+This will incur a high load on providers as it will create N number of `ExternalSecret`s which will also poll the provider separately. This can be fixed in two ways,
+
+- In this CRD by adding the ability to reference an existing secret to replicate instead of creating `ExternalSecret`s, but this is not the "spirit" of the CRD and is less of a `ClusterExternalSecret` and more of a `ClusterSecret`. This is not the preferred way.
+
+- The creation of a new Kubernetes Provider which will allow for the targeting of secrets in Kubernetes for `ExternalSecret`s
+
+### Acceptance Criteria
+What does it take to make this feature producation ready? Please take the time to think about:
+* how would you rollout this feature and rollback if it causes harm?
+* Test Roadmap: what kinds of tests do we want to ensure a good user experience?
+* observability: Do users need to get insights into the inner workings of that feature?
+* monitoring: How can users tell whether the feature is working as expected or not?
+              can we provide dashboards, metrics, reasonable SLIs/SLOs
+              or example alerts for this feature?
+* troubleshooting: How would users want to troubleshoot this particular feature?
+                   Think about different failure modes of this feature.
+
+For this to be production ready it will need to be tested to ensure the expected behavior occurs, specifically around edge cases like
+
+- Adding labels after creation of resource
+- Changing of created `ExternalSecret`s
+- Cleanup of `ExternalSecret`s when resources is deleted
+- Deletion of owned resource
+- Removal of label from namespace after `ExternalSecret` is created
+
+Everything else is on the `ExternalSecret` and not the `ClusterExternalSecret` and troubleshooting would be the same.
+
+## Alternatives
+
+Adding a namespace selector to the regular `ExternalSecret`, but this would cause issues since it's not cluster scoped, and can't use "owned by" which would cause issues for cleanup.
+
+

+ 11 - 0
docs/api-clusterexternalsecret.md

@@ -0,0 +1,11 @@
+The `ClusterExternalSecret` is a cluster scoped resource that can be used to push an `ExternalSecret` to specific namespaces.
+
+Using the `namespaceSelector` you can select namespaces, and any matching namespaces will have the `ExternalSecret` specified in the `externalSecretSpec` created in it.
+
+## Example
+
+Below is an example of the `ClusterExternalSecret` in use.
+
+```yaml
+{% include 'full-cluster-external-secret.yaml' %}
+```

+ 6 - 1
docs/eso-blogs.md

@@ -21,4 +21,9 @@ Gustavo writes about how to setup ESO with AWS Secrets Manager. He also shows yo
 
 ## [Kubernetes Hardening Tutorial Part 2: Network](https://blog.gitguardian.com/kubernetes-tutorial-part-2-network/)
 
-Tiexin Guo Writes about Kubernetes hardening in this series of blogs. He mentions ESO as one of the convenient options when dealing with secrets in Kubernetes, and how to use it with AWS Secret Manager using AWS credentials. [Kubernetes Hardening Tutorial Part 2: Network](https://blog.gitguardian.com/kubernetes-tutorial-part-2-network/)
+Tiexin Guo Writes about Kubernetes hardening in this series of blogs. He mentions ESO as one of the convenient options when dealing with secrets in Kubernetes, and how to use it with AWS Secret Manager using AWS credentials. [Kubernetes Hardening Tutorial Part 2: Network](https://blog.gitguardian.com/kubernetes-tutorial-part-2-network/)
+
+
+## [Tutorial: How to manage secrets in OpenShift using Vault and External Secrets Operator](https://youtu.be/PgiXKBTel1E)
+
+Balkrishna Pandey published a video tutorial and a [blog post](https://goglides.io/how-to-manage-secrets-in-openshift-using-vault-and-external-secrets/1164/) on integrating HashiCorp Vault and External Secret Operator (ESO) to manage application secrets on OpenShift Cluster. In this blog, he demonstrates the strength of the `ClusterSecretStore` functionality, a cluster scoped SecretStore and is global to the Cluster that all `ExternalSecrets` can reference from all namespaces.

+ 7 - 1
docs/eso-demos.md

@@ -6,4 +6,10 @@ A list of demos given by people going through simple setups with ESO. Feel free
 
 This was an old demo going through an old version of ESO. Most of it is still valid, but beware of CRD and breaking change differences.
 
-[![GCP SM + AWS SM + Azure Key Vault Demo](https://img.youtube.com/vi/L6tn1YdMkF8/0.jpg)](https://www.youtube.com/watch?v=L6tn1YdMkF8)
+[![GCP SM + AWS SM + Azure Key Vault Demo](https://img.youtube.com/vi/L6tn1YdMkF8/0.jpg)](https://www.youtube.com/watch?v=L6tn1YdMkF8)
+
+## How to manage secrets in OpenShift using Vault and External Secrets Operator
+
+Balkrishna Pandey shows us here how to use ClusterSecretStore and how to integrate ESO with Hashicorp Vault on Openshift.
+
+[![How to manage secrets in OpenShift using Vault and External Secrets Operator](https://img.youtube.com/vi/PgiXKBTel1E/0.jpg)](https://www.youtube.com/watch?v=PgiXKBTel1E)

+ 1 - 1
docs/guides-controller-class.md

@@ -2,7 +2,7 @@
 
 > NOTE: this feature is experimental and not highly tested
 
-Controller classes are a proprierty set during the deployment that allows multiple controllers to work in a group of workload. It works by separating which secretStores are going to be attributed to which controller. For the behavior of a single controller, no extra configuration is needed.
+Controller classes are a property set during the deployment that allows multiple controllers to work in a group of workload. It works by separating which secretStores are going to be attributed to which controller. For the behavior of a single controller, no extra configuration is needed.
 
 ## Setting up Controller Class
 

+ 1 - 1
docs/guides-templating.md

@@ -96,7 +96,7 @@ In addition to that you can use over 200+ [sprig functions](http://masterminds.g
 
 ## Migrating from v1
 
-You have to opt-in to use the new engine version by specifying `template.engineVersion=v2`:
+If you are still using `v1alpha1`, You have to opt-in to use the new engine version by specifying `template.engineVersion=v2`:
 
 ```yaml
 apiVersion: external-secrets.io/v1alpha1

+ 1 - 1
docs/provider-aws-parameter-store.md

@@ -54,7 +54,7 @@ Consider the following JSON object that is stored in the Parameter Store key `my
 
 This is an example on how you would look up nested keys in the above json object:
 ``` yaml
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: example

+ 1 - 1
docs/provider-aws-secrets-manager.md

@@ -29,7 +29,7 @@ Create a IAM Policy to pin down access to secrets matching `dev-*`.
         "secretsmanager:ListSecretVersionIds"
       ],
       "Resource": [
-        "arn:aws:secretsmanager:us-west-2:111122223333:secret:dev-*",
+        "arn:aws:secretsmanager:us-west-2:111122223333:secret:dev-*"
       ]
     }
   ]

+ 41 - 1
docs/provider-azure-key-vault.md

@@ -7,7 +7,7 @@ External Secrets Operator integrates with [Azure Key vault](https://azure.micros
 
 ### Authentication
 
-We support Service Principals and Managed Identity [authentication](https://docs.microsoft.com/en-us/azure/key-vault/general/authentication).
+We support Service Principals, Managed Identity and Workload Identity authentication.
 
 To use Managed Identity authentication, you should use [aad-pod-identity](https://azure.github.io/aad-pod-identity/docs/) to assign the identity to external-secrets operator. To add the selector to external-secrets operator, use `podLabels` in your values.yaml in case of Helm installation of external-secrets.
 
@@ -25,6 +25,46 @@ If there are multiple Managed Identitites for different keyvaults, the operator
 {% include 'azkv-credentials-secret.yaml' %}
 ```
 
+#### Workload Identity
+
+You can use [Azure AD Workload Identity Federation](https://docs.microsoft.com/en-us/azure/active-directory/develop/workload-identity-federation) to access Azure managed services like Key Vault **without needing to manage secrets**. You need to configure a trust relationship between your Kubernetes Cluster and Azure AD. This can be done in various ways, for instance using `terraform`, the Azure Portal or the `az` cli. We found the [azwi](https://azure.github.io/azure-workload-identity/docs/installation/azwi.html) cli very helpful. The Azure [Workload Identity Quick Start Guide](https://azure.github.io/azure-workload-identity/docs/quick-start.html) is also good place to get started.
+
+This is basically a two step process:
+
+1. Create a Kubernetes Service Account ([guide](https://azure.github.io/azure-workload-identity/docs/quick-start.html#5-create-a-kubernetes-service-account))
+
+```sh
+azwi serviceaccount create phase sa \
+  --aad-application-name "${APPLICATION_NAME}" \
+  --service-account-namespace "${SERVICE_ACCOUNT_NAMESPACE}" \
+  --service-account-name "${SERVICE_ACCOUNT_NAME}"
+```
+2. Configure the trust relationship between Azure AD and Kubernetes ([guide](https://azure.github.io/azure-workload-identity/docs/quick-start.html#6-establish-federated-identity-credential-between-the-aad-application-and-the-service-account-issuer--subject))
+
+```sh
+azwi serviceaccount create phase federated-identity \
+  --aad-application-name "${APPLICATION_NAME}" \
+  --service-account-namespace "${SERVICE_ACCOUNT_NAMESPACE}" \
+  --service-account-name "${SERVICE_ACCOUNT_NAME}" \
+  --service-account-issuer-url "${SERVICE_ACCOUNT_ISSUER}"
+```
+
+With these prerequisites met you can configure `ESO` to use that Service Account. You have two options:
+
+##### Mounted Service Account
+You run the controller and mount that particular service account into the pod. That grants _everyone_ who is able to create a secret store or reference a correctly configured one the ability to read secrets. **This approach is usually not recommended**. But may make sense when you want to share an identity with multiple namespaces. Also see our [Multi-Tenancy Guide](guides-multi-tenancy.md) for design considerations.
+
+```yaml
+{% include 'azkv-workload-identity-mounted.yaml' %}
+```
+
+##### Referenced Service Account
+You run the controller without service account (effectively without azure permissions). Now you have to configure the SecretStore and set the `serviceAccountRef` and point to the service account you have just created. **This is usually the recommended approach**. It makes sense for everyone who wants to run the controller withour Azure permissions and delegate authentication via service accounts in particular namespaces. Also see our [Multi-Tenancy Guide] for design considerations.
+
+```yaml
+{% include 'azkv-workload-identity.yaml' %}
+```
+
 ### Update secret store
 Be sure the `azurekv` provider is listed in the `Kind=SecretStore`
 

+ 6 - 5
docs/provider-google-secrets-manager.md

@@ -12,12 +12,13 @@ Your Google Kubernetes Engine (GKE) applications can consume GCP services like S
 
 You can find the documentation for Workload Identity [here](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity). We will walk you through how to navigate it here.
 
-Search [the documment](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) for this editable values and change them to your values:
+Search [the document](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) for this editable values and change them to your values:  
+_Note: If you have installed ESO, a serviceaccount has already been created. You can either patch the existing `external-secrets` SA or create a new one that fits your needs._
 
 - `CLUSTER_NAME`: The name of your cluster
 - `PROJECT_ID`: Your project ID (not your Project number nor your Project name)
-- `K8S_NAMESPACE`: For us folowing these steps here it will be `es`, but this will be the namespace where you deployed the external-secrets operator
-- `KSA_NAME`: external-secrets (if you are not creating a new one to attach to the deployemnt)
+- `K8S_NAMESPACE`: For us following these steps here it will be `es`, but this will be the namespace where you deployed the external-secrets operator
+- `KSA_NAME`: external-secrets (if you are not creating a new one to attach to the deployment)
 - `GSA_NAME`: external-secrets for simplicity, or something else if you have to follow different naming convetions for cloud resources
 - `ROLE_NAME`: should be `roles/secretmanager.secretAccessor` - so you make the pod only be able to access secrets on Secret Manager
 
@@ -29,8 +30,8 @@ Let's assume you have created a service account correctly and attached a appropr
 apiVersion: v1
 kind: ServiceAccount
 metadata:
-  name: team-a
-  namespace: team-a
+  name: external-secrets
+  namespace: es
   annotations:
     iam.gke.io/gcp-service-account: example-team-a@my-project.iam.gserviceaccount.com
 ```

+ 86 - 7
docs/provider-hashicorp-vault.md

@@ -11,7 +11,7 @@ management. Vault itself implements lots of different secret engines, as of now
 First, create a SecretStore with a vault backend. For the sake of simplicity we'll use a static token `root`:
 
 ```yaml
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: vault-backend
@@ -46,7 +46,7 @@ vault kv put secret/foo my-value=s3cr3t
 Now create a ExternalSecret that uses the above SecretStore:
 
 ```yaml
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: vault-example
@@ -76,7 +76,7 @@ data:
 You can fetch all key/value pairs for a given path If you leave the `remoteRef.property` empty. This returns the json-encoded secret value for that path.
 
 ```yaml
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: vault-example
@@ -105,7 +105,7 @@ Given the following secret - assume its path is `/dev/config`:
 
 You can set the `remoteRef.property` to point to the nested key using a [gjson](https://github.com/tidwall/gjson) expression.
 ```yaml
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: vault-example
@@ -141,15 +141,16 @@ Given the following secret - assume its path is `/dev/config`:
 
 You can set the `remoteRef.property` to point to the nested key using a [gjson](https://github.com/tidwall/gjson) expression.
 ```yaml
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: vault-example
 spec:
   # ...
   dataFrom:
-  - key: /dev/config
-    property: foo.nested
+  - extract:
+      key: /dev/config
+      property: foo.nested
 ```
 
 That results in a secret with these values:
@@ -158,6 +159,84 @@ bar=mysecret
 baz=bang
 ```
 
+#### Getting multiple secrets
+
+You can extract multiple secrets from Hashicorp vault by using `dataFrom.Find`
+
+Currently, `dataFrom.Find` allows users to fetch secret names that match a given regexp pattern, or fetch secrets whose `custom_metadata` tags match a predefined set.
+
+
+!!! warning
+    The way hashicorp Vault currently allows LIST operations is through the existence of a secret metadata. If you delete the secret, you will also need to delete the secret's metadata or this will currently make Find operations fail.
+
+Given the following secret - assume its path is `/dev/config`:
+```json
+{
+  "foo": {
+    "nested": {
+      "bar": "mysecret",
+      "baz": "bang"
+    }
+  }
+}
+```
+
+Also consider the following secret has the following `custom_metadata`:
+```json
+{
+  "environment": "dev",
+  "component": "app-1"
+}
+```
+
+It is possible to find this secret by all the following possibilities:
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+  name: vault-example
+spec:
+  # ...
+  dataFrom: 
+  - find: #will return every secret with 'dev' in it (including paths) 
+      name: 
+        regexp: dev
+  - find: #will return every secret matching environment:dev tags from dev/ folder and beyond 
+      tags: 
+        environment: dev
+```
+will generate a secret with: 
+```json
+{
+  "dev_config":"{\"foo\":{\"nested\":{\"bar\":\"mysecret\",\"baz\":\"bang\"}}}"
+}
+```
+
+Currently, `Find` operations are recursive throughout a given vault folder, starting on `provider.Path` definition. It is recommended to narrow down the scope of search by setting a `find.path` variable. This is also useful to automatically reduce the resulting secret key names:
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+  name: vault-example
+spec:
+  # ...
+  dataFrom: 
+  - find: #will return every secret from dev/ folder 
+      path: dev
+      name: 
+        regexp: ".*"
+  - find: #will return every secret matching environment:dev tags from dev/ folder
+      path: dev
+      tags: 
+        environment: dev
+```
+Will generate a secret with:
+```json
+{
+  "config":"{\"foo\": {\"nested\": {\"bar\": \"mysecret\",\"baz\": \"bang\"}}}"
+}
+
+```
 ### Authentication
 
 We support five different modes for authentication:

+ 2 - 2
docs/provider-ibm-secrets-manager.md

@@ -49,7 +49,7 @@ See here for a list of [publicly available endpoints](https://cloud.ibm.com/apid
 ![iam-create-success](./pictures/screenshot_service_url.png)
 
 ### Secret Types
-We support all secret types of [IBM Secrets Manager](https://cloud.ibm.com/apidocs/secrets-manager): `arbitrary`, `username_password`, `iam_credentials` and `imported_cert`. To define the type of secret you would like to sync you need to prefix the secret id with the desired type. If the secret type is not specified it is defaulted to `arbitrary`:
+We support the following secret types of [IBM Secrets Manager](https://cloud.ibm.com/apidocs/secrets-manager): `arbitrary`, `username_password`, `iam_credentials`, `public_cert` and `imported_cert`. To define the type of secret you would like to sync you need to prefix the secret id with the desired type. If the secret type is not specified it is defaulted to `arbitrary`:
 
 ```yaml
 {% include 'ibm-es-types.yaml' %}
@@ -71,7 +71,7 @@ The behavior for the different secret types is as following:
 * `remoteRef` retrieves an apikey from secrets manager and sets it for specified `secretKey`
 * `dataFrom` retrieves an apikey from secrets manager and sets it for the `apikey` Kubernetes secret key
 
-#### imported_cert
+#### imported_cert and public_cert
 * `remoteRef` requires a `property` to be set for either `certificate`, `private_key` or `intermediate` to retrieve respective fields from the secrets manager secret and set in specified `secretKey`
 * `dataFrom` retrieves all `certificate`, `private_key` and `intermediate` fields from the secrets manager secret and sets appropriate key:value pairs in the resulting Kubernetes secret
 

+ 95 - 0
docs/provider-kubernetes.md

@@ -0,0 +1,95 @@
+External Secrets Operator allows to retrieve in-cluster secrets or from a remote Kubernetes Cluster.
+
+### Authentication
+
+It's possible to authenticate against the Kubernetes API using client certificates, a bearer token or a service account (not implemented yet). The operator enforces that exactly one authentication method is used.
+
+## Example
+
+### K8s Cluster Secret
+
+
+```
+apiVersion: v1
+kind: Secret
+metadata:
+  name: cluster-secrets
+data:
+  # Fill with your encoded base64 CA
+  ca: Cg==
+  # Fill with your encoded base64 Certificate
+  certificate: Cg==
+  # Fill with your encoded base64 Key
+  key: Cg==
+stringData:
+  # Fill with your a string Token
+  bearerToken: "my-token"
+```
+
+## SecretStore
+
+The `Server` section specifies the url of the Kubernetes API and the location to fetch the CA. The `auth` section indicates the type of authentication to use, `cert`, `token` or `serviceAccount` and includes the path to fetch the certificates or the token.
+
+```
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+  name: example
+spec:
+  provider:
+      kubernetes:
+        # If not remoteNamesapce is provided, default namespace is used
+        remoteNamespace: default  
+        server: 
+          url:  https://127.0.0.1:36473
+          # Add your encoded base64 to caBundle or a referenced caProvider
+          # if both are provided caProvider will be ignored
+          caBundle: Cg==
+          caProvider: 
+            type: Secret
+            name : cluster-secrets
+            key: ca
+        auth:
+          # Add a referenced bearerToken or client certificates, 
+          # if both are provided client certificates will be ignored
+          token:
+            bearerToken:
+              name: cluster-secrets
+              key: bearerToken
+          cert:
+            clientCert: 
+                name: cluster-secrets
+                key: certificate
+            clientKey: 
+                name: cluster-secrets
+                key: key
+---
+apiVersion: v1
+kind: Secret
+metadata:
+  name: secret-example
+data:
+  extra: YmFyCg==
+```
+        
+### ExternalSecret
+
+```
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+  name: example
+spec:
+  refreshInterval: 1h           
+  secretStoreRef:
+    kind: SecretStore
+    name: example               # name of the SecretStore (or kind specified)
+  target:
+    name: secret-to-be-created  # name of the k8s Secret to be created
+    creationPolicy: Owner
+  data:
+  - secretKey: extra
+    remoteRef:
+      key: secret-example
+      property: extra
+```

+ 3 - 3
docs/provider-webhook.md

@@ -8,7 +8,7 @@ First, create a SecretStore with a webhook backend.  We'll use a static user/pas
 
 ```yaml
 {% raw %}
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: webhook-backend
@@ -43,7 +43,7 @@ NB: This is obviously not practical because it just returns the key as the resul
 Now create an ExternalSecret that uses the above SecretStore:
 
 ```yaml
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: webhook-example
@@ -83,7 +83,7 @@ Each secret has a `name` property which determines the name of the object in the
 ### All Parameters
 
 ```yaml
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ClusterSecretStore
 metadata:
   name: statervault

+ 2 - 2
docs/provider-yandex-lockbox.md

@@ -26,7 +26,7 @@ kubectl create secret generic yc-auth --from-file=authorized-key=authorized-key.
 ```
 * Create a [SecretStore](../api-secretstore/) pointing to `yc-auth` k8s secret:
 ```yaml
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: secret-store
@@ -63,7 +63,7 @@ yc lockbox secret list-access-bindings --name lockbox-secret
 ```
 * Create an [ExternalSecret](../api-externalsecret/) pointing to `secret-store` and `lockbox-secret`:
 ```yaml
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: external-secret

+ 3 - 2
docs/snippets/akeyless-external-secret-json.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: akeyless-external-secret-example-json
@@ -15,4 +15,5 @@ spec:
 
   # for json formatted secrets: each key in the json will be used as the secret key in the SECRET k8s target object
   dataFrom:
-  - key: secret-name # Full path of the secret on Akeyless
+  - extract:
+      key: secret-name # Full path of the secret on Akeyless

+ 1 - 1
docs/snippets/akeyless-external-secret.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: akeyless-external-secret-example

+ 1 - 1
docs/snippets/akeyless-secret-store.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: akeyless-secret-store

+ 3 - 2
docs/snippets/aws-anchore-engine-access-credentials-external-secret.yaml

@@ -1,5 +1,5 @@
 ---
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: anchore-access-credentials
@@ -12,4 +12,5 @@ spec:
   target:
     name: anchore-access-credentials
   dataFrom:
-  - key: service/anchore-engine/engineAccess
+  - extract:
+      key: service/anchore-engine/engineAccess

+ 1 - 1
docs/snippets/aws-jenkins-credential-github-ssh-external-secret.yaml

@@ -1,5 +1,5 @@
 ---
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: github-ssh-access

+ 1 - 1
docs/snippets/aws-jenkins-credential-sonarqube-api-token-external-secret.yaml

@@ -1,5 +1,5 @@
 ---
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: sonarqube-api-token

+ 1 - 1
docs/snippets/aws-jenkins-credentials-harbor-chart-robot-external-secret.yaml

@@ -1,5 +1,5 @@
 ---
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: harbor-chart-robot

+ 1 - 1
docs/snippets/aws-parameter-store.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: secretstore-sample

+ 1 - 1
docs/snippets/aws-sm-external-secret.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: example

+ 1 - 1
docs/snippets/aws-sm-store.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: secretstore-sample

+ 1 - 1
docs/snippets/azkv-external-secret.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: example-external-secret

+ 1 - 1
docs/snippets/azkv-secret-store-mi.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: example-secret-store

+ 1 - 1
docs/snippets/azkv-secret-store.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: example-secret-store

+ 19 - 0
docs/snippets/azkv-workload-identity-mounted.yaml

@@ -0,0 +1,19 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  # this service account was created by azwi
+  name: workload-identity-sa
+  annotations:
+    azure.workload.identity/client-id: 7d8cdf74-xxxx-xxxx-xxxx-274d963d358b
+    azure.workload.identity/tenant-id: 5a02a20e-xxxx-xxxx-xxxx-0ad5b634c5d8
+---
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+  name: example-secret-store
+spec:
+  provider:
+    azurekv:
+      authType: WorkloadIdentity
+      vaultUrl: "https://xx-xxxx-xx.vault.azure.net"
+      # note: no serviceAccountRef was provided

+ 20 - 0
docs/snippets/azkv-workload-identity.yaml

@@ -0,0 +1,20 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  # this service account was created by azwi
+  name: workload-identity-sa
+  annotations:
+    azure.workload.identity/client-id: 7d8cdf74-xxxx-xxxx-xxxx-274d963d358b
+    azure.workload.identity/tenant-id: 5a02a20e-xxxx-xxxx-xxxx-0ad5b634c5d8
+---
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+  name: example-secret-store
+spec:
+  provider:
+    azurekv:
+      authType: WorkloadIdentity
+      vaultUrl: "https://xx-xxxx-xx.vault.azure.net"
+      serviceAccountRef:
+        name: workload-identity-sa

+ 3 - 2
docs/snippets/basic-external-secret.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: example
@@ -17,4 +17,5 @@ spec:
       version: provider-key-version
       property: provider-key-property
   dataFrom:
-  - key: remote-key-in-the-provider
+  - extract:
+      key: remote-key-in-the-provider

+ 1 - 1
docs/snippets/basic-secret-store.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: secretstore-sample

+ 1 - 1
docs/snippets/controller-class-store.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: controller-custom-example

+ 3 - 2
docs/snippets/fake-provider-es.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: example
@@ -15,4 +15,5 @@ spec:
       key: /foo/bar
       version: v1
   dataFrom:
-  - key: /foo/baz
+  - extract:
+      key: /foo/baz

+ 1 - 1
docs/snippets/fake-provider-store.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ClusterSecretStore
 metadata:
   name: fake

+ 78 - 0
docs/snippets/full-cluster-external-secret.yaml

@@ -0,0 +1,78 @@
+{% raw %}
+apiVersion: external-secrets.io/v1beta1
+kind: ClusterExternalSecret
+metadata:
+  name: "hello-world"
+spec:
+  # The name to be used on the ExternalSecrets
+  externalSecretName: "hello-world-es"
+
+  # 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: 
+      cool: label
+
+  # How often the ClusterExternalSecret should reconcile itself
+  # This will decide how often to check and make sure that the ExternalSecrets exist in the matching namespaces
+  refreshTime: "1m"
+
+  # This is the spec of the ExternalSecrets to be created
+  # The content of this was taken from our ExternalSecret example
+  externalSecretSpec:
+    secretStoreRef:
+      name: secret-store-name
+      kind: SecretStore
+
+    refreshInterval: "1h"
+    target:
+      name: my-secret
+      creationPolicy: 'Merge'
+      template:
+        type: kubernetes.io/dockerconfigjson
+
+        metadata:
+          annotations: {}
+          labels: {}
+        data:
+          config.yml: |
+            endpoints:
+            - https://{{ .data.user }}:{{ .data.password }}@api.exmaple.com
+        templateFrom:
+        - configMap:
+            name: alertmanager
+            items:
+            - key: alertmanager.yaml
+    data:
+      - secretKey: secret-key-to-be-managed
+        remoteRef:
+          key: provider-key
+          version: provider-key-version
+          property: provider-key-property
+    dataFrom:
+    - key: provider-key
+      version: provider-key-version
+      property: provider-key-property
+
+status:
+  # This will list any namespaces where the creation of the ExternalSecret failed
+  # This will not list any issues with the ExternalSecrets, you will have to check the
+  # ExternalSecrets to see any issues with them.
+  failedNamespaces:
+    - namespace: "matching-ns-1"
+      # This is one of the possible messages, and likely the most common
+      reason: "external secret already exists in namespace"
+  
+  # You can find all matching and successfully deployed namespaces here
+  provisionedNamespaces:
+    - "matching-ns-3"
+    - "matching-ns-2"
+  
+  # The condition can be Ready, PartiallyReady, or NotReady 
+  # PartiallyReady would indicate an error in 1 or more namespaces
+  # NotReady would indicate errors in all namespaces meaning all ExternalSecrets resulted in errors
+  conditions:
+  - type: PartiallyReady
+    status: "True"
+    lastTransitionTime: "2022-01-12T12:33:02Z"
+{% endraw %}

+ 1 - 1
docs/snippets/full-cluster-secret-store.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ClusterSecretStore
 metadata:
   name: example

+ 13 - 4
docs/snippets/full-external-secret.yaml

@@ -1,5 +1,5 @@
 {% raw %}
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: "hello-world"
@@ -73,9 +73,18 @@ spec:
   # Used to fetch all properties from the Provider key
   # If multiple dataFrom are specified, secrets are merged in the specified order
   dataFrom:
-  - key: provider-key
-    version: provider-key-version
-    property: provider-key-property
+  - extract:
+      key: provider-key
+      version: provider-key-version
+      property: provider-key-property
+      conversionStrategy: Default
+  - find:
+      path: path-to-filter 
+      name: 
+        regexp: ".*foobar.*"
+      tags: 
+        foo: bar
+      conversionStrategy: Unicode
 
 status:
   # refreshTime is the time and date the external secret was fetched and

+ 1 - 5
docs/snippets/full-secret-store.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: example
@@ -65,7 +65,6 @@ spec:
         # static token: https://www.vaultproject.io/docs/auth/token
         tokenSecretRef:
           name: "my-secret"
-          namespace: "secret-admin"
           key: "vault-token"
 
         # AppRole auth: https://www.vaultproject.io/docs/auth/approle
@@ -74,7 +73,6 @@ spec:
           roleId: "db02de05-fa39-4855-059b-67221c5c2f63"
           secretRef:
             name: "my-secret"
-            namespace: "secret-admin"
             key: "vault-token"
 
         # Kubernetes auth: https://www.vaultproject.io/docs/auth/kubernetes
@@ -84,12 +82,10 @@ spec:
           # Optional service account reference
           serviceAccountRef:
             name: "my-sa"
-            namespace: "secret-admin"
           # Optional secret field containing a Kubernetes ServiceAccount JWT
           # used for authenticating with Vault
           secretRef:
             name: "my-secret"
-            namespace: "secret-admin"
             key: "vault"
 
     # (2): GCP Secret Manager

+ 3 - 2
docs/snippets/gcpsm-data-from-external-secret.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: example
@@ -11,4 +11,5 @@ spec:
     name: secret-to-be-created  # name of the k8s Secret to be created
     creationPolicy: Owner
   dataFrom:
-  - key: all-keys-example-secret  # name of the GCPSM secret
+  - extract:
+      key: all-keys-example-secret  # name of the GCPSM secret

+ 1 - 1
docs/snippets/gcpsm-docker-config-externalsecret.yaml

@@ -1,5 +1,5 @@
 {% raw %}
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: dk-cfg-example

+ 1 - 1
docs/snippets/gcpsm-external-secret.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: example

+ 1 - 1
docs/snippets/gcpsm-pod-wi-secret-store.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: example

+ 1 - 1
docs/snippets/gcpsm-secret-store.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: example

+ 1 - 1
docs/snippets/gcpsm-ssh-auth-externalsecret.yaml

@@ -1,5 +1,5 @@
 {% raw %}
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: ssh-auth-example

+ 1 - 1
docs/snippets/gcpsm-tls-externalsecret.yaml

@@ -1,5 +1,5 @@
 {% raw %}
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: template-tls-example

+ 3 - 1
docs/snippets/gcpsm-wi-secret-store.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ClusterSecretStore
 metadata:
   name: example
@@ -12,6 +12,8 @@ spec:
           clusterLocation: europe-central2
           # name of the GKE cluster
           clusterName: example-workload-identity
+          # projectID of the cluster (if omitted defaults to spec.provider.gcpsm.projectID)
+          clusterProjectID: my-cluster-project
           # reference the sa from above
           serviceAccountRef:
             name: team-a

+ 3 - 2
docs/snippets/gitlab-external-secret-json.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: gitlab-external-secret-example
@@ -15,4 +15,5 @@ spec:
 
   # each secret name in the KV will be used as the secret key in the SECRET k8s target object
   dataFrom:
-  - key: "myJsonVariable" # Key of the variable on Gitlab
+  - extract:
+      key: "myJsonVariable" # Key of the variable on Gitlab

+ 1 - 1
docs/snippets/gitlab-external-secret.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: gitlab-external-secret-example

+ 1 - 1
docs/snippets/gitlab-secret-store.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: gitlab-secret-store

+ 1 - 1
docs/snippets/gitops/crs/clusterSecretStore.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ClusterSecretStore
 metadata:
   name: vault-backend-global

+ 7 - 1
docs/snippets/ibm-es-types.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: ibm-sample
@@ -12,9 +12,15 @@ spec:
   - secretKey: foo
     remoteRef:
       key: username_password/yyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy
+      property: username
   - secretKey: bar
     remoteRef:
       key: iam_credentials/zzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz
   - secretKey: baz
     remoteRef:
       key: imported_cert/zzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz
+      property: certificate
+  - secretKey: bap
+      remoteRef:
+        key: public_cert/zzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz
+        property: certificate

+ 2 - 2
docs/snippets/ibm-external-secret.yaml

@@ -1,9 +1,9 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: external-secret-sample
 spec:
-  refreshInterval: 1m
+  refreshInterval: 60m
   secretStoreRef:
     name: secretstore-sample
     kind: SecretStore

+ 1 - 1
docs/snippets/ibm-secret-store.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: secretstore-sample

+ 1 - 1
docs/snippets/jwk-template-v2-external-secret.yaml

@@ -1,5 +1,5 @@
 {% raw %}
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: template

+ 1 - 1
docs/snippets/multiline-template-v1-external-secret.yaml

@@ -1,5 +1,5 @@
 {% raw %}
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: template

+ 1 - 1
docs/snippets/multiline-template-v2-external-secret.yaml

@@ -1,5 +1,5 @@
 {% raw %}
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: template

+ 3 - 2
docs/snippets/oracle-external-secret.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: example
@@ -11,4 +11,5 @@ spec:
     name: secret-to-be-created # Name for the secret on the cluster
     creationPolicy: Owner
   dataFrom:
-    - key: the-secret-name
+  - extract:
+      key: the-secret-name

+ 2 - 2
docs/snippets/oracle-secret-store.yaml

@@ -1,4 +1,4 @@
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: example-instance-principal
@@ -10,7 +10,7 @@ spec:
 
 ---
 
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: example-auth

+ 1 - 1
docs/snippets/pkcs12-template-v1-external-secret.yaml

@@ -1,5 +1,5 @@
 {% raw %}
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: template

+ 1 - 1
docs/snippets/pkcs12-template-v2-external-secret.yaml

@@ -1,5 +1,5 @@
 {% raw %}
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: ExternalSecret
 metadata:
   name: template

+ 5 - 3
docs/snippets/provider-aws-access.md

@@ -4,6 +4,8 @@
 
 ![Pod Identity Authentication](./pictures/diagrams-provider-aws-auth-pod-identity.png)
 
+Note: If you are using Paramater Store replace `service: SecretsManager` with `service: ParamaterStore` in all examples below.
+
 This is basicially a zero-configuration authentication method that inherits the credentials from the runtime environment using the [aws sdk default credential chain](https://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html#credentials-default).
 
 You can attach a role to the pod using [IRSA](https://docs.aws.amazon.com/eks/latest/userguide/iam-roles-for-service-accounts.html), [kiam](https://github.com/uswitch/kiam) or [kube2iam](https://github.com/jtblin/kube2iam). When no other authentication method is configured in the `Kind=Secretstore` this role is used to make all API calls against AWS Secrets Manager or SSM Parameter Store.
@@ -11,7 +13,7 @@ You can attach a role to the pod using [IRSA](https://docs.aws.amazon.com/eks/la
 Based on the Pod's identity you can do a `sts:assumeRole` before fetching the secrets to limit access to certain keys in your provider. This is optional.
 
 ```yaml
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: team-b-store
@@ -31,7 +33,7 @@ spec:
 You can store Access Key ID & Secret Access Key in a `Kind=Secret` and reference it from a SecretStore.
 
 ```yaml
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: team-b-store
@@ -76,7 +78,7 @@ metadata:
 Reference the service account from above in the Secret Store:
 
 ```yaml
-apiVersion: external-secrets.io/v1alpha1
+apiVersion: external-secrets.io/v1beta1
 kind: SecretStore
 metadata:
   name: secretstore-sample

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä