Browse Source

Implements Previder provider for Previder Secret Vault implementation (#3916)

* Added Previder Vault Provider and tests

Signed-off-by: Gijs Middelkamp <g.middelkamp@previder.nl>

* Set go version back to 1.23

Signed-off-by: Gijs Middelkamp <g.middelkamp@previder.nl>

* Updates after "make reviewable"

Signed-off-by: Gijs Middelkamp <g.middelkamp@previder.nl>

* Fixed methods to naming convention

Signed-off-by: Gijs Middelkamp <g.middelkamp@previder.nl>

* Added Previder to stability support doc

Signed-off-by: Gijs Middelkamp <g.middelkamp@previder.nl>

* Added installation documentation and Previder logo

Signed-off-by: Gijs Middelkamp <g.middelkamp@previder.nl>

* Altered last test name for naming convention

Signed-off-by: Gijs Middelkamp <g.middelkamp@previder.nl>

* Adds Previder provider to api-docs/mkdocs.yml

Signed-off-by: Gijs Middelkamp <g.middelkamp@previder.nl>

* Ran make check-diff

Signed-off-by: Gijs Middelkamp <g.middelkamp@previder.nl>

* Updated Tiltfile to check for new default image used in helm chart

Signed-off-by: Gijs Middelkamp <g.middelkamp@previder.nl>

* Added optional tag to PreviderAuth struct

Signed-off-by: Gijs Middelkamp <g.middelkamp@previder.nl>

* Removed toolchain

Signed-off-by: Gijs Middelkamp <g.middelkamp@previder.nl>

* Updated to go 1.23.1 for CVE; Updated previder/vault-cli to 0.1.2 for CVE fix also

Signed-off-by: Gijs Middelkamp <g.middelkamp@previder.nl>

---------

Signed-off-by: Gijs Middelkamp <g.middelkamp@previder.nl>
Signed-off-by: Gijs Middelkamp <17021438+gkwmiddelkamp@users.noreply.github.com>
Gijs Middelkamp 1 year ago
parent
commit
daa1297f3d

+ 38 - 0
apis/externalsecrets/v1beta1/secretstore_previder_types.go

@@ -0,0 +1,38 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package v1beta1
+
+import (
+	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+)
+
+// PreviderProvider configures a store to sync secrets using the Previder Secret Manager provider.
+type PreviderProvider struct {
+	Auth PreviderAuth `json:"auth"`
+	// +optional
+	BaseURI string `json:"baseUri,omitempty"`
+}
+
+// PreviderAuth contains a secretRef for credentials.
+type PreviderAuth struct {
+	// +optional
+	SecretRef *PreviderAuthSecretRef `json:"secretRef,omitempty"`
+}
+
+// PreviderAuthSecretRef holds secret references for Previder Vault credentials.
+type PreviderAuthSecretRef struct {
+	// The AccessToken is used for authentication
+	AccessToken esmeta.SecretKeySelector `json:"accessToken"`
+}

+ 4 - 0
apis/externalsecrets/v1beta1/secretstore_types.go

@@ -138,6 +138,10 @@ type SecretStoreProvider struct {
 	// +optional
 	// +optional
 	Doppler *DopplerProvider `json:"doppler,omitempty"`
 	Doppler *DopplerProvider `json:"doppler,omitempty"`
 
 
+	// Previder configures this store to sync secrets using the Previder provider
+	// +optional
+	Previder *PreviderProvider `json:"previder,omitempty"`
+
 	// Onboardbase configures this store to sync secrets using the Onboardbase provider
 	// Onboardbase configures this store to sync secrets using the Onboardbase provider
 	// +optional
 	// +optional
 	Onboardbase *OnboardbaseProvider `json:"onboardbase,omitempty"`
 	Onboardbase *OnboardbaseProvider `json:"onboardbase,omitempty"`

+ 57 - 0
apis/externalsecrets/v1beta1/zz_generated.deepcopy.go

@@ -2297,6 +2297,58 @@ func (in *PasswordDepotSecretRef) DeepCopy() *PasswordDepotSecretRef {
 }
 }
 
 
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PreviderAuth) DeepCopyInto(out *PreviderAuth) {
+	*out = *in
+	if in.SecretRef != nil {
+		in, out := &in.SecretRef, &out.SecretRef
+		*out = new(PreviderAuthSecretRef)
+		(*in).DeepCopyInto(*out)
+	}
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreviderAuth.
+func (in *PreviderAuth) DeepCopy() *PreviderAuth {
+	if in == nil {
+		return nil
+	}
+	out := new(PreviderAuth)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PreviderAuthSecretRef) DeepCopyInto(out *PreviderAuthSecretRef) {
+	*out = *in
+	in.AccessToken.DeepCopyInto(&out.AccessToken)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreviderAuthSecretRef.
+func (in *PreviderAuthSecretRef) DeepCopy() *PreviderAuthSecretRef {
+	if in == nil {
+		return nil
+	}
+	out := new(PreviderAuthSecretRef)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *PreviderProvider) DeepCopyInto(out *PreviderProvider) {
+	*out = *in
+	in.Auth.DeepCopyInto(&out.Auth)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PreviderProvider.
+func (in *PreviderProvider) DeepCopy() *PreviderProvider {
+	if in == nil {
+		return nil
+	}
+	out := new(PreviderProvider)
+	in.DeepCopyInto(out)
+	return out
+}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *PulumiProvider) DeepCopyInto(out *PulumiProvider) {
 func (in *PulumiProvider) DeepCopyInto(out *PulumiProvider) {
 	*out = *in
 	*out = *in
 	if in.AccessToken != nil {
 	if in.AccessToken != nil {
@@ -2583,6 +2635,11 @@ func (in *SecretStoreProvider) DeepCopyInto(out *SecretStoreProvider) {
 		*out = new(DopplerProvider)
 		*out = new(DopplerProvider)
 		(*in).DeepCopyInto(*out)
 		(*in).DeepCopyInto(*out)
 	}
 	}
+	if in.Previder != nil {
+		in, out := &in.Previder, &out.Previder
+		*out = new(PreviderProvider)
+		(*in).DeepCopyInto(*out)
+	}
 	if in.Onboardbase != nil {
 	if in.Onboardbase != nil {
 		in, out := &in.Onboardbase, &out.Onboardbase
 		in, out := &in.Onboardbase, &out.Onboardbase
 		*out = new(OnboardbaseProvider)
 		*out = new(OnboardbaseProvider)

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

@@ -3788,6 +3788,44 @@ spec:
                     - database
                     - database
                     - host
                     - host
                     type: object
                     type: object
+                  previder:
+                    description: Previder configures this store to sync secrets using
+                      the Previder provider
+                    properties:
+                      auth:
+                        description: PreviderAuth contains a secretRef for credentials.
+                        properties:
+                          secretRef:
+                            description: PreviderAuthSecretRef holds secret references
+                              for Previder Vault credentials.
+                            properties:
+                              accessToken:
+                                description: The AccessToken is used for authentication
+                                properties:
+                                  key:
+                                    description: |-
+                                      The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
+                                      defaulted, in others it may be required.
+                                    type: string
+                                  name:
+                                    description: The name of the Secret resource being
+                                      referred to.
+                                    type: string
+                                  namespace:
+                                    description: |-
+                                      Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
+                                      to the namespace of the referent.
+                                    type: string
+                                type: object
+                            required:
+                            - accessToken
+                            type: object
+                        type: object
+                      baseUri:
+                        type: string
+                    required:
+                    - auth
+                    type: object
                   pulumi:
                   pulumi:
                     description: Pulumi configures this store to sync secrets using
                     description: Pulumi configures this store to sync secrets using
                       the Pulumi provider
                       the Pulumi provider

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

@@ -3788,6 +3788,44 @@ spec:
                     - database
                     - database
                     - host
                     - host
                     type: object
                     type: object
+                  previder:
+                    description: Previder configures this store to sync secrets using
+                      the Previder provider
+                    properties:
+                      auth:
+                        description: PreviderAuth contains a secretRef for credentials.
+                        properties:
+                          secretRef:
+                            description: PreviderAuthSecretRef holds secret references
+                              for Previder Vault credentials.
+                            properties:
+                              accessToken:
+                                description: The AccessToken is used for authentication
+                                properties:
+                                  key:
+                                    description: |-
+                                      The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
+                                      defaulted, in others it may be required.
+                                    type: string
+                                  name:
+                                    description: The name of the Secret resource being
+                                      referred to.
+                                    type: string
+                                  namespace:
+                                    description: |-
+                                      Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
+                                      to the namespace of the referent.
+                                    type: string
+                                type: object
+                            required:
+                            - accessToken
+                            type: object
+                        type: object
+                      baseUri:
+                        type: string
+                    required:
+                    - auth
+                    type: object
                   pulumi:
                   pulumi:
                     description: Pulumi configures this store to sync secrets using
                     description: Pulumi configures this store to sync secrets using
                       the Pulumi provider
                       the Pulumi provider

+ 70 - 0
deploy/crds/bundle.yaml

@@ -4163,6 +4163,41 @@ spec:
                         - database
                         - database
                         - host
                         - host
                       type: object
                       type: object
+                    previder:
+                      description: Previder configures this store to sync secrets using the Previder provider
+                      properties:
+                        auth:
+                          description: PreviderAuth contains a secretRef for credentials.
+                          properties:
+                            secretRef:
+                              description: PreviderAuthSecretRef holds secret references for Previder Vault credentials.
+                              properties:
+                                accessToken:
+                                  description: The AccessToken is used for authentication
+                                  properties:
+                                    key:
+                                      description: |-
+                                        The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
+                                        defaulted, in others it may be required.
+                                      type: string
+                                    name:
+                                      description: The name of the Secret resource being referred to.
+                                      type: string
+                                    namespace:
+                                      description: |-
+                                        Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
+                                        to the namespace of the referent.
+                                      type: string
+                                  type: object
+                              required:
+                                - accessToken
+                              type: object
+                          type: object
+                        baseUri:
+                          type: string
+                      required:
+                        - auth
+                      type: object
                     pulumi:
                     pulumi:
                       description: Pulumi configures this store to sync secrets using the Pulumi provider
                       description: Pulumi configures this store to sync secrets using the Pulumi provider
                       properties:
                       properties:
@@ -9947,6 +9982,41 @@ spec:
                         - database
                         - database
                         - host
                         - host
                       type: object
                       type: object
+                    previder:
+                      description: Previder configures this store to sync secrets using the Previder provider
+                      properties:
+                        auth:
+                          description: PreviderAuth contains a secretRef for credentials.
+                          properties:
+                            secretRef:
+                              description: PreviderAuthSecretRef holds secret references for Previder Vault credentials.
+                              properties:
+                                accessToken:
+                                  description: The AccessToken is used for authentication
+                                  properties:
+                                    key:
+                                      description: |-
+                                        The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
+                                        defaulted, in others it may be required.
+                                      type: string
+                                    name:
+                                      description: The name of the Secret resource being referred to.
+                                      type: string
+                                    namespace:
+                                      description: |-
+                                        Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
+                                        to the namespace of the referent.
+                                      type: string
+                                  type: object
+                              required:
+                                - accessToken
+                              type: object
+                          type: object
+                        baseUri:
+                          type: string
+                      required:
+                        - auth
+                      type: object
                     pulumi:
                     pulumi:
                       description: Pulumi configures this store to sync secrets using the Pulumi provider
                       description: Pulumi configures this store to sync secrets using the Pulumi provider
                       properties:
                       properties:

+ 120 - 0
docs/api/spec.md

@@ -5951,6 +5951,112 @@ External Secrets meta/v1.SecretKeySelector
 </tr>
 </tr>
 </tbody>
 </tbody>
 </table>
 </table>
+<h3 id="external-secrets.io/v1beta1.PreviderAuth">PreviderAuth
+</h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1beta1.PreviderProvider">PreviderProvider</a>)
+</p>
+<p>
+<p>PreviderAuth contains a secretRef for credentials.</p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>secretRef</code></br>
+<em>
+<a href="#external-secrets.io/v1beta1.PreviderAuthSecretRef">
+PreviderAuthSecretRef
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+</td>
+</tr>
+</tbody>
+</table>
+<h3 id="external-secrets.io/v1beta1.PreviderAuthSecretRef">PreviderAuthSecretRef
+</h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1beta1.PreviderAuth">PreviderAuth</a>)
+</p>
+<p>
+<p>PreviderAuthSecretRef holds secret references for Previder Vault credentials.</p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>accessToken</code></br>
+<em>
+<a href="https://pkg.go.dev/github.com/external-secrets/external-secrets/apis/meta/v1#SecretKeySelector">
+External Secrets meta/v1.SecretKeySelector
+</a>
+</em>
+</td>
+<td>
+<p>The AccessToken is used for authentication</p>
+</td>
+</tr>
+</tbody>
+</table>
+<h3 id="external-secrets.io/v1beta1.PreviderProvider">PreviderProvider
+</h3>
+<p>
+(<em>Appears on:</em>
+<a href="#external-secrets.io/v1beta1.SecretStoreProvider">SecretStoreProvider</a>)
+</p>
+<p>
+<p>PreviderProvider configures a store to sync secrets using the Previder Secret Manager provider.</p>
+</p>
+<table>
+<thead>
+<tr>
+<th>Field</th>
+<th>Description</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>
+<code>auth</code></br>
+<em>
+<a href="#external-secrets.io/v1beta1.PreviderAuth">
+PreviderAuth
+</a>
+</em>
+</td>
+<td>
+</td>
+</tr>
+<tr>
+<td>
+<code>baseUri</code></br>
+<em>
+string
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+</td>
+</tr>
+</tbody>
+</table>
 <h3 id="external-secrets.io/v1beta1.Provider">Provider
 <h3 id="external-secrets.io/v1beta1.Provider">Provider
 </h3>
 </h3>
 <p>
 <p>
@@ -6750,6 +6856,20 @@ DopplerProvider
 </tr>
 </tr>
 <tr>
 <tr>
 <td>
 <td>
+<code>previder</code></br>
+<em>
+<a href="#external-secrets.io/v1beta1.PreviderProvider">
+PreviderProvider
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>Previder configures this store to sync secrets using the Previder provider</p>
+</td>
+</tr>
+<tr>
+<td>
 <code>onboardbase</code></br>
 <code>onboardbase</code></br>
 <em>
 <em>
 <a href="#external-secrets.io/v1beta1.OnboardbaseProvider">
 <a href="#external-secrets.io/v1beta1.OnboardbaseProvider">

+ 4 - 2
docs/introduction/stability-support.md

@@ -33,7 +33,7 @@ We want to cover the following cases:
 The following table describes the stability level of each provider and who's responsible.
 The following table describes the stability level of each provider and who's responsible.
 
 
 | Provider                                                                                                   | Stability |                                                                                                                                                                              Maintainer |
 | Provider                                                                                                   | Stability |                                                                                                                                                                              Maintainer |
-|------------------------------------------------------------------------------------------------------------| :-------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
+|------------------------------------------------------------------------------------------------------------|:---------:|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|
 | [AWS Secrets Manager](https://external-secrets.io/latest/provider/aws-secrets-manager/)                    |  stable   |                                                                                                                                 [external-secrets](https://github.com/external-secrets) |
 | [AWS Secrets Manager](https://external-secrets.io/latest/provider/aws-secrets-manager/)                    |  stable   |                                                                                                                                 [external-secrets](https://github.com/external-secrets) |
 | [AWS Parameter Store](https://external-secrets.io/latest/provider/aws-parameter-store/)                    |  stable   |                                                                                                                                 [external-secrets](https://github.com/external-secrets) |
 | [AWS Parameter Store](https://external-secrets.io/latest/provider/aws-parameter-store/)                    |  stable   |                                                                                                                                 [external-secrets](https://github.com/external-secrets) |
 | [Hashicorp Vault](https://external-secrets.io/latest/provider/hashicorp-vault/)                            |  stable   |                                                                                                                                 [external-secrets](https://github.com/external-secrets) |
 | [Hashicorp Vault](https://external-secrets.io/latest/provider/hashicorp-vault/)                            |  stable   |                                                                                                                                 [external-secrets](https://github.com/external-secrets) |
@@ -61,13 +61,14 @@ The following table describes the stability level of each provider and who's res
 | [Infisical](https://external-secrets.io/latest/provider/infisical)                                         |   alpha   |                                                                                                                                              [@akhilmhdh](https://github.com/akhilmhdh) |
 | [Infisical](https://external-secrets.io/latest/provider/infisical)                                         |   alpha   |                                                                                                                                              [@akhilmhdh](https://github.com/akhilmhdh) |
 | [Device42](https://external-secrets.io/latest/provider/device42)                                           |   alpha   |                                                                                                                                                                                         |
 | [Device42](https://external-secrets.io/latest/provider/device42)                                           |   alpha   |                                                                                                                                                                                         |
 | [Bitwarden Secrets Manager](https://external-secrets.io/latest/provider/bitwarden-secrets-manager)         |   alpha   |                                                                                                                                                  [@skarlso](https://github.com/Skarlso) |
 | [Bitwarden Secrets Manager](https://external-secrets.io/latest/provider/bitwarden-secrets-manager)         |   alpha   |                                                                                                                                                  [@skarlso](https://github.com/Skarlso) |
+| [Previder](https://external-secrets.io/latest/provider/previder)                                           |  stable   |                                                                                                                                                [@previder](https://github.com/previder) |
 
 
 ## Provider Feature Support
 ## Provider Feature Support
 
 
 The following table show the support for features across different providers.
 The following table show the support for features across different providers.
 
 
 | Provider                  | find by name | find by tags | metadataPolicy Fetch | referent authentication | store validation | push secret | DeletionPolicy Merge/Delete |
 | Provider                  | find by name | find by tags | metadataPolicy Fetch | referent authentication | store validation | push secret | DeletionPolicy Merge/Delete |
-| ------------------------- | :----------: | :----------: | :------------------: | :---------------------: | :--------------: | :---------: | :-------------------------: |
+|---------------------------| :----------: | :----------: | :------------------: | :---------------------: | :--------------: |:-----------:|:---------------------------:|
 | AWS Secrets Manager       |      x       |      x       |          x           |            x            |        x         |      x      |              x              |
 | AWS Secrets Manager       |      x       |      x       |          x           |            x            |        x         |      x      |              x              |
 | AWS Parameter Store       |      x       |      x       |          x           |            x            |        x         |      x      |              x              |
 | AWS Parameter Store       |      x       |      x       |          x           |            x            |        x         |      x      |              x              |
 | Hashicorp Vault           |      x       |      x       |          x           |            x            |        x         |      x      |              x              |
 | Hashicorp Vault           |      x       |      x       |          x           |            x            |        x         |      x      |              x              |
@@ -95,6 +96,7 @@ The following table show the support for features across different providers.
 | Infisical                 |      x       |              |                      |            x            |        x         |             |                             |
 | Infisical                 |      x       |              |                      |            x            |        x         |             |                             |
 | Device42                  |              |              |                      |                         |        x         |             |                             |
 | Device42                  |              |              |                      |                         |        x         |             |                             |
 | Bitwarden Secrets Manager |      x       |              |                      |                         |        x         |      x      |              x              |
 | Bitwarden Secrets Manager |      x       |              |                      |                         |        x         |      x      |              x              |
+| Previder                  |      x       |              |                      |                         |        x         |             |                             |
 
 
 ## Support Policy
 ## Support Policy
 
 

BIN
docs/pictures/previder-provider.png


+ 64 - 0
docs/provider/previder.md

@@ -0,0 +1,64 @@
+
+![Previder Secret Vault](../pictures/previder-provider.png)
+
+## Previder Secret Vault Manager
+
+External Secrets Operator integrates with [Previder Secrets Vault](https://vault.previder.io) for secure secret management.
+
+### Authentication
+
+We support Access Token authentication using a Secrets Vault ReadWrite or ReadOnly token.
+
+This token can be created with the [vault-cli](https://github.com/previder/vault-cli) using an Environment token which can be acquired via the [Previder Portal](https://portal.previder.nl).
+
+#### Access Token authentication
+
+To use the access token, first create it as a regular Kubernetes Secret and then associate it with the Previder Secret Store.
+
+```yaml
+apiVersion: v1
+kind: Secret
+metadata:
+  name: previder-vault-sample-secret
+data:
+  previder-vault-token: cHJldmlkZXIgdmF1bHQgZXhhbXBsZQ==
+```
+
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: SecretStore
+metadata:
+  name: previder-secretstore-sample
+spec:
+  provider:
+    previder:
+      auth:
+        secretRef:
+          accessToken:
+            name: previder-vault-sample-secret
+            key: previder-vault-token
+```
+
+
+### Creating external secret
+
+To create a kubernetes secret from the Previder Secret Vault, create an ExternalSecret with a reference to a Vault secret.
+
+```yaml
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+  name: example
+spec:
+  refreshInterval: 1h
+  secretStoreRef:
+    name: previder-secretstore-sample
+    kind: SecretStore
+  target:
+    name: example-secret
+    creationPolicy: Owner
+  data:
+    - secretKey: local-secret-key
+      remoteRef:
+        key: token-name-or-id
+```

+ 1 - 1
e2e/go.mod

@@ -1,6 +1,6 @@
 module github.com/external-secrets/external-secrets-e2e
 module github.com/external-secrets/external-secrets-e2e
 
 
-go 1.23
+go 1.23.1
 
 
 replace (
 replace (
 	github.com/Masterminds/sprig/v3 => github.com/external-secrets/sprig/v3 v3.3.0
 	github.com/Masterminds/sprig/v3 => github.com/external-secrets/sprig/v3 v3.3.0

+ 2 - 1
go.mod

@@ -1,6 +1,6 @@
 module github.com/external-secrets/external-secrets
 module github.com/external-secrets/external-secrets
 
 
-go 1.23
+go 1.23.1
 
 
 replace github.com/Masterminds/sprig/v3 => github.com/external-secrets/sprig/v3 v3.3.0
 replace github.com/Masterminds/sprig/v3 => github.com/external-secrets/sprig/v3 v3.3.0
 
 
@@ -89,6 +89,7 @@ require (
 	github.com/lestrrat-go/jwx/v2 v2.1.1
 	github.com/lestrrat-go/jwx/v2 v2.1.1
 	github.com/maxbrunsfeld/counterfeiter/v6 v6.9.0
 	github.com/maxbrunsfeld/counterfeiter/v6 v6.9.0
 	github.com/passbolt/go-passbolt v0.7.1
 	github.com/passbolt/go-passbolt v0.7.1
+	github.com/previder/vault-cli v0.1.2
 	github.com/pulumi/esc-sdk/sdk v0.10.0
 	github.com/pulumi/esc-sdk/sdk v0.10.0
 	github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30
 	github.com/scaleway/scaleway-sdk-go v1.0.0-beta.30
 	github.com/sethvargo/go-password v0.3.1
 	github.com/sethvargo/go-password v0.3.1

+ 2 - 0
go.sum

@@ -587,6 +587,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/previder/vault-cli v0.1.2 h1:aui5v+L243JGbRaJ65z5XsuItjyCtoBND32v1XU3gd4=
+github.com/previder/vault-cli v0.1.2/go.mod h1:u9JDPB5/Em/Czjb/yIwfTODr31kKmeSO3JGrheLMaP8=
 github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4=
 github.com/prometheus/client_golang v1.20.3 h1:oPksm4K8B+Vt35tUhw6GbSNSgVlVSBH0qELP/7u83l4=
 github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
 github.com/prometheus/client_golang v1.20.3/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
 github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=

+ 1 - 0
hack/api-docs/mkdocs.yml

@@ -136,6 +136,7 @@ nav:
       - Password Depot: provider-passworddepot.md
       - Password Depot: provider-passworddepot.md
       - Fortanix: provider/fortanix.md
       - Fortanix: provider/fortanix.md
       - Infisical: provider/infisical.md
       - Infisical: provider/infisical.md
+      - Previder: provider/previder.md
   - Examples:
   - Examples:
       - FluxCD: examples/gitops-using-fluxcd.md
       - FluxCD: examples/gitops-using-fluxcd.md
       - Anchore Engine: examples/anchore-engine-credentials.md
       - Anchore Engine: examples/anchore-engine-credentials.md

+ 46 - 0
pkg/provider/previder/client_test.go

@@ -0,0 +1,46 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package previder
+
+import (
+	"errors"
+
+	"github.com/previder/vault-cli/pkg"
+	"github.com/previder/vault-cli/pkg/model"
+)
+
+type PreviderVaultFakeClient struct {
+	pkg.PreviderVaultClient
+}
+
+var (
+	secrets = map[string]string{"secret1": "secret1content", "secret2": "secret2content"}
+)
+
+func (v *PreviderVaultFakeClient) DecryptSecret(id string) (*model.SecretDecrypt, error) {
+	for k, v := range secrets {
+		if k == id {
+			return &model.SecretDecrypt{Secret: v}, nil
+		}
+	}
+	return nil, errors.New("404 not found")
+}
+
+func (v *PreviderVaultFakeClient) GetSecrets() ([]model.Secret, error) {
+	secretList := make([]model.Secret, 0)
+	for k := range secrets {
+		secretList = append(secretList, model.Secret{Description: k})
+	}
+	return secretList, nil
+}

+ 136 - 0
pkg/provider/previder/provider.go

@@ -0,0 +1,136 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package previder
+
+import (
+	"context"
+	"errors"
+	"fmt"
+
+	previderclient "github.com/previder/vault-cli/pkg"
+	corev1 "k8s.io/api/core/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
+	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
+
+	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+	"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
+)
+
+const (
+	errNotImplemented = "not implemented"
+)
+
+var _ esv1beta1.Provider = &SecretManager{}
+
+type SecretManager struct {
+	VaultClient previderclient.PreviderVaultClient
+}
+
+func init() {
+	esv1beta1.Register(&SecretManager{}, &esv1beta1.SecretStoreProvider{
+		Previder: &esv1beta1.PreviderProvider{},
+	})
+}
+
+func (s *SecretManager) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
+	if store == nil {
+		return nil, fmt.Errorf("secret store not found: %v", "nil store")
+	}
+	storeSpec := store.GetSpec().Provider.Previder
+
+	storeKind := store.GetObjectKind().GroupVersionKind().Kind
+	accessToken, err := resolvers.SecretKeyRef(ctx, kube, storeKind, namespace, &storeSpec.Auth.SecretRef.AccessToken)
+	if err != nil {
+		return nil, fmt.Errorf(accessToken, err)
+	}
+
+	s.VaultClient, err = previderclient.NewVaultClient(storeSpec.BaseURI, accessToken)
+
+	if err != nil {
+		return nil, err
+	}
+	return s, nil
+}
+
+func (s *SecretManager) ValidateStore(store esv1beta1.GenericStore) (admission.Warnings, error) {
+	storeSpec := store.GetSpec()
+	previderSpec := storeSpec.Provider.Previder
+	if previderSpec == nil {
+		return nil, errors.New("missing Previder spec")
+	}
+	if previderSpec.Auth.SecretRef == nil {
+		return nil, errors.New("missing Previder Auth SecretRef")
+	}
+	accessToken := previderSpec.Auth.SecretRef.AccessToken
+
+	if accessToken.Name == "" {
+		return nil, errors.New("missing Previder accessToken name")
+	}
+	if accessToken.Key == "" {
+		return nil, errors.New("missing Previder accessToken key")
+	}
+
+	return nil, nil
+}
+
+func (s *SecretManager) Capabilities() esv1beta1.SecretStoreCapabilities {
+	return esv1beta1.SecretStoreReadOnly
+}
+
+func (s *SecretManager) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
+	secret, err := s.VaultClient.DecryptSecret(ref.Key)
+	if err != nil {
+		return nil, err
+	}
+	return []byte(secret.Secret), nil
+}
+
+func (s *SecretManager) PushSecret(ctx context.Context, secret *corev1.Secret, data esv1beta1.PushSecretData) error {
+	return errors.New(errNotImplemented)
+}
+
+func (s *SecretManager) DeleteSecret(ctx context.Context, remoteRef esv1beta1.PushSecretRemoteRef) error {
+	return errors.New(errNotImplemented)
+}
+
+func (s *SecretManager) SecretExists(ctx context.Context, remoteRef esv1beta1.PushSecretRemoteRef) (bool, error) {
+	return false, errors.New(errNotImplemented)
+}
+
+func (s *SecretManager) Validate() (esv1beta1.ValidationResult, error) {
+	_, err := s.VaultClient.GetSecrets()
+	if err != nil {
+		return esv1beta1.ValidationResultError, err
+	}
+
+	return esv1beta1.ValidationResultReady, nil
+}
+
+func (s *SecretManager) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) (map[string][]byte, error) {
+	secrets, err := s.GetSecret(ctx, ref)
+	if err != nil {
+		return nil, err
+	}
+	secretData := make(map[string][]byte)
+	secretData[ref.Key] = secrets
+	return secretData, nil
+}
+
+func (s *SecretManager) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
+	return nil, errors.New(errNotImplemented)
+}
+
+func (s *SecretManager) Close(ctx context.Context) error {
+	return nil
+}

+ 160 - 0
pkg/provider/previder/provider_test.go

@@ -0,0 +1,160 @@
+/*
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+package previder
+
+import (
+	"context"
+	"testing"
+
+	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+	v1 "github.com/external-secrets/external-secrets/apis/meta/v1"
+)
+
+func TestSecretManagerCapabilities(t *testing.T) {
+	previderProvider := &SecretManager{}
+	if previderProvider.Capabilities() != esv1beta1.SecretStoreReadOnly {
+		t.Errorf("Store does not return correct value for capabilities")
+	}
+}
+
+func TestSecretManagerClose(t *testing.T) {
+	previderProvider := &SecretManager{}
+	ctx := context.Background()
+	if previderProvider.Close(ctx) != nil {
+		t.Errorf("Store close acts different than expected")
+	}
+}
+
+func TestSecretManagerGetAllSecrets(t *testing.T) {
+	previderProvider := &SecretManager{}
+	ctx := context.Background()
+	ref := esv1beta1.ExternalSecretFind{}
+	result, err := previderProvider.GetAllSecrets(ctx, ref)
+	if result != nil || err == nil {
+		t.Errorf("Store close acts different than expected")
+	}
+}
+
+func TestSecretManagerGetSecret(t *testing.T) {
+	previderProvider := &SecretManager{VaultClient: &PreviderVaultFakeClient{}}
+	ctx := context.Background()
+	ref := esv1beta1.ExternalSecretDataRemoteRef{Key: "secret1"}
+	returnedSecret, err := previderProvider.GetSecret(ctx, ref)
+	if err != nil {
+		t.Errorf("Secret not found")
+	}
+	if string(returnedSecret) != "secret1content" {
+		t.Errorf("Wrong secret returned")
+	}
+}
+
+func TestSecretManagerGetSecretNotExisting(t *testing.T) {
+	previderProvider := &SecretManager{VaultClient: &PreviderVaultFakeClient{}}
+	ctx := context.Background()
+	ref := esv1beta1.ExternalSecretDataRemoteRef{Key: "secret3"}
+	_, err := previderProvider.GetSecret(ctx, ref)
+	if err == nil {
+		t.Errorf("Secret found while non were expected")
+	}
+}
+
+func TestSecretManagerGetSecretMap(t *testing.T) {
+	previderProvider := &SecretManager{VaultClient: &PreviderVaultFakeClient{}}
+	ctx := context.Background()
+	key := "secret1"
+
+	ref := esv1beta1.ExternalSecretDataRemoteRef{Key: key}
+	returnedSecret, err := previderProvider.GetSecretMap(ctx, ref)
+	if err != nil {
+		t.Errorf("Secret not found")
+	}
+	if value, ok := returnedSecret[key]; !ok || string(value) != "secret1content" {
+		t.Errorf("Key not found or wrong secret returned")
+	}
+}
+
+func TestSecretManagerValidate(t *testing.T) {
+	previderProvider := &SecretManager{VaultClient: &PreviderVaultFakeClient{}}
+	validate, err := previderProvider.Validate()
+	if err != nil || validate != esv1beta1.ValidationResultReady {
+		t.Errorf("Could not validate")
+	}
+}
+
+func TestSecretManagerValidateStore(t *testing.T) {
+	previderProvider := &SecretManager{}
+	store := &esv1beta1.SecretStore{
+		Spec: esv1beta1.SecretStoreSpec{
+			Provider: &esv1beta1.SecretStoreProvider{
+				Previder: &esv1beta1.PreviderProvider{
+					Auth: esv1beta1.PreviderAuth{
+						SecretRef: &esv1beta1.PreviderAuthSecretRef{
+							AccessToken: v1.SecretKeySelector{
+								Name: "token",
+								Key:  "key",
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	result, err := previderProvider.ValidateStore(store)
+	if result != nil || err != nil {
+		t.Errorf("Store Validation acts different than expected")
+	}
+
+	store = &esv1beta1.SecretStore{
+		Spec: esv1beta1.SecretStoreSpec{
+			Provider: &esv1beta1.SecretStoreProvider{
+				Previder: &esv1beta1.PreviderProvider{
+					Auth: esv1beta1.PreviderAuth{
+						SecretRef: &esv1beta1.PreviderAuthSecretRef{
+							AccessToken: v1.SecretKeySelector{
+								Name: "token",
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	result, err = previderProvider.ValidateStore(store)
+	if result != nil || err == nil {
+		t.Errorf("Store Validation key is not checked")
+	}
+
+	store = &esv1beta1.SecretStore{
+		Spec: esv1beta1.SecretStoreSpec{
+			Provider: &esv1beta1.SecretStoreProvider{
+				Previder: &esv1beta1.PreviderProvider{
+					Auth: esv1beta1.PreviderAuth{
+						SecretRef: &esv1beta1.PreviderAuthSecretRef{
+							AccessToken: v1.SecretKeySelector{
+								Key: "token",
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+
+	result, err = previderProvider.ValidateStore(store)
+	if result != nil || err == nil {
+		t.Errorf("Store Validation name is not checked")
+	}
+}

+ 1 - 0
pkg/provider/register/register.go

@@ -41,6 +41,7 @@ import (
 	_ "github.com/external-secrets/external-secrets/pkg/provider/oracle"
 	_ "github.com/external-secrets/external-secrets/pkg/provider/oracle"
 	_ "github.com/external-secrets/external-secrets/pkg/provider/passbolt"
 	_ "github.com/external-secrets/external-secrets/pkg/provider/passbolt"
 	_ "github.com/external-secrets/external-secrets/pkg/provider/passworddepot"
 	_ "github.com/external-secrets/external-secrets/pkg/provider/passworddepot"
+	_ "github.com/external-secrets/external-secrets/pkg/provider/previder"
 	_ "github.com/external-secrets/external-secrets/pkg/provider/pulumi"
 	_ "github.com/external-secrets/external-secrets/pkg/provider/pulumi"
 	_ "github.com/external-secrets/external-secrets/pkg/provider/scaleway"
 	_ "github.com/external-secrets/external-secrets/pkg/provider/scaleway"
 	_ "github.com/external-secrets/external-secrets/pkg/provider/secretserver"
 	_ "github.com/external-secrets/external-secrets/pkg/provider/secretserver"