Browse Source

feat: add CAProvider to Bitwarden provider (#3699)

* feat: add CAProvider to bitwarden

This change introduces a refactor as well since CAProvider
was used by multiple providers with diverging implementations.
The following providers were affected:
- webhook
- akeyless
- vault
- conjur
- kubernetes

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>

* refactored the Kubernetes provider to use create ca

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>

* refactor webhook, vault and kubernetes provider

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>

* rename CreateCACert to FetchCACertFromSource

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>

* addressed comments and autodecoding base64 data

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>

* check if the decoded value is a valid certificate

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>

---------

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
Gergely Brautigam 1 year ago
parent
commit
82d419e2ee

+ 5 - 2
apis/externalsecrets/v1beta1/secretsstore_bitwarden_types.go

@@ -23,8 +23,11 @@ type BitwardenSecretsManagerProvider struct {
 	BitwardenServerSDKURL string `json:"bitwardenServerSDKURL,omitempty"`
 	// Base64 encoded certificate for the bitwarden server sdk. The sdk MUST run with HTTPS to make sure no MITM attack
 	// can be performed.
-	// +required
-	CABundle string `json:"caBundle"`
+	// +optional
+	CABundle string `json:"caBundle,omitempty"`
+	// see: https://external-secrets.io/latest/spec/#external-secrets.io/v1alpha1.CAProvider
+	// +optional
+	CAProvider *CAProvider `json:"caProvider,omitempty"`
 	// OrganizationID determines which organization this secret store manages.
 	OrganizationID string `json:"organizationID"`
 	// ProjectID determines which project this secret store manages.

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

@@ -505,6 +505,11 @@ func (in *BitwardenSecretsManagerAuth) DeepCopy() *BitwardenSecretsManagerAuth {
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *BitwardenSecretsManagerProvider) DeepCopyInto(out *BitwardenSecretsManagerProvider) {
 	*out = *in
+	if in.CAProvider != nil {
+		in, out := &in.CAProvider, &out.CAProvider
+		*out = new(CAProvider)
+		(*in).DeepCopyInto(*out)
+	}
 	in.Auth.DeepCopyInto(&out.Auth)
 }
 

+ 27 - 1
config/crds/bases/external-secrets.io_clustersecretstores.yaml

@@ -2493,6 +2493,33 @@ spec:
                           Base64 encoded certificate for the bitwarden server sdk. The sdk MUST run with HTTPS to make sure no MITM attack
                           can be performed.
                         type: string
+                      caProvider:
+                        description: 'see: https://external-secrets.io/latest/spec/#external-secrets.io/v1alpha1.CAProvider'
+                        properties:
+                          key:
+                            description: The key where the CA certificate can be found
+                              in the Secret or ConfigMap.
+                            type: string
+                          name:
+                            description: The name of the object located at the provider
+                              type.
+                            type: string
+                          namespace:
+                            description: |-
+                              The namespace the Provider type is in.
+                              Can only be defined when used in a ClusterSecretStore.
+                            type: string
+                          type:
+                            description: The type of provider to use such as "Secret",
+                              or "ConfigMap".
+                            enum:
+                            - Secret
+                            - ConfigMap
+                            type: string
+                        required:
+                        - name
+                        - type
+                        type: object
                       identityURL:
                         type: string
                       organizationID:
@@ -2505,7 +2532,6 @@ spec:
                         type: string
                     required:
                     - auth
-                    - caBundle
                     - organizationID
                     - projectID
                     type: object

+ 27 - 1
config/crds/bases/external-secrets.io_secretstores.yaml

@@ -2493,6 +2493,33 @@ spec:
                           Base64 encoded certificate for the bitwarden server sdk. The sdk MUST run with HTTPS to make sure no MITM attack
                           can be performed.
                         type: string
+                      caProvider:
+                        description: 'see: https://external-secrets.io/latest/spec/#external-secrets.io/v1alpha1.CAProvider'
+                        properties:
+                          key:
+                            description: The key where the CA certificate can be found
+                              in the Secret or ConfigMap.
+                            type: string
+                          name:
+                            description: The name of the object located at the provider
+                              type.
+                            type: string
+                          namespace:
+                            description: |-
+                              The namespace the Provider type is in.
+                              Can only be defined when used in a ClusterSecretStore.
+                            type: string
+                          type:
+                            description: The type of provider to use such as "Secret",
+                              or "ConfigMap".
+                            enum:
+                            - Secret
+                            - ConfigMap
+                            type: string
+                        required:
+                        - name
+                        - type
+                        type: object
                       identityURL:
                         type: string
                       organizationID:
@@ -2505,7 +2532,6 @@ spec:
                         type: string
                     required:
                     - auth
-                    - caBundle
                     - organizationID
                     - projectID
                     type: object

+ 48 - 2
deploy/crds/bundle.yaml

@@ -2978,6 +2978,30 @@ spec:
                             Base64 encoded certificate for the bitwarden server sdk. The sdk MUST run with HTTPS to make sure no MITM attack
                             can be performed.
                           type: string
+                        caProvider:
+                          description: 'see: https://external-secrets.io/latest/spec/#external-secrets.io/v1alpha1.CAProvider'
+                          properties:
+                            key:
+                              description: The key where the CA certificate can be found in the Secret or ConfigMap.
+                              type: string
+                            name:
+                              description: The name of the object located at the provider type.
+                              type: string
+                            namespace:
+                              description: |-
+                                The namespace the Provider type is in.
+                                Can only be defined when used in a ClusterSecretStore.
+                              type: string
+                            type:
+                              description: The type of provider to use such as "Secret", or "ConfigMap".
+                              enum:
+                                - Secret
+                                - ConfigMap
+                              type: string
+                          required:
+                            - name
+                            - type
+                          type: object
                         identityURL:
                           type: string
                         organizationID:
@@ -2988,7 +3012,6 @@ spec:
                           type: string
                       required:
                         - auth
-                        - caBundle
                         - organizationID
                         - projectID
                       type: object
@@ -8740,6 +8763,30 @@ spec:
                             Base64 encoded certificate for the bitwarden server sdk. The sdk MUST run with HTTPS to make sure no MITM attack
                             can be performed.
                           type: string
+                        caProvider:
+                          description: 'see: https://external-secrets.io/latest/spec/#external-secrets.io/v1alpha1.CAProvider'
+                          properties:
+                            key:
+                              description: The key where the CA certificate can be found in the Secret or ConfigMap.
+                              type: string
+                            name:
+                              description: The name of the object located at the provider type.
+                              type: string
+                            namespace:
+                              description: |-
+                                The namespace the Provider type is in.
+                                Can only be defined when used in a ClusterSecretStore.
+                              type: string
+                            type:
+                              description: The type of provider to use such as "Secret", or "ConfigMap".
+                              enum:
+                                - Secret
+                                - ConfigMap
+                              type: string
+                          required:
+                            - name
+                            - type
+                          type: object
                         identityURL:
                           type: string
                         organizationID:
@@ -8750,7 +8797,6 @@ spec:
                           type: string
                       required:
                         - auth
-                        - caBundle
                         - organizationID
                         - projectID
                       type: object

+ 16 - 0
docs/api/spec.md

@@ -1328,12 +1328,27 @@ string
 </em>
 </td>
 <td>
+<em>(Optional)</em>
 <p>Base64 encoded certificate for the bitwarden server sdk. The sdk MUST run with HTTPS to make sure no MITM attack
 can be performed.</p>
 </td>
 </tr>
 <tr>
 <td>
+<code>caProvider</code></br>
+<em>
+<a href="#external-secrets.io/v1beta1.CAProvider">
+CAProvider
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>see: <a href="https://external-secrets.io/latest/spec/#external-secrets.io/v1alpha1.CAProvider">https://external-secrets.io/latest/spec/#external-secrets.io/v1alpha1.CAProvider</a></p>
+</td>
+</tr>
+<tr>
+<td>
 <code>organizationID</code></br>
 <em>
 string
@@ -1407,6 +1422,7 @@ External Secrets meta/v1.SecretKeySelector
 <p>
 (<em>Appears on:</em>
 <a href="#external-secrets.io/v1beta1.AkeylessProvider">AkeylessProvider</a>, 
+<a href="#external-secrets.io/v1beta1.BitwardenSecretsManagerProvider">BitwardenSecretsManagerProvider</a>, 
 <a href="#external-secrets.io/v1beta1.ConjurProvider">ConjurProvider</a>, 
 <a href="#external-secrets.io/v1beta1.KubernetesServer">KubernetesServer</a>, 
 <a href="#external-secrets.io/v1beta1.VaultProvider">VaultProvider</a>)

+ 3 - 25
pkg/common/webhook/models.go

@@ -16,6 +16,8 @@ package webhook
 
 import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 )
 
 type Spec struct {
@@ -55,31 +57,7 @@ type Spec struct {
 
 	// The provider for the CA bundle to use to validate webhook server certificate.
 	// +optional
-	CAProvider *CAProvider `json:"caProvider,omitempty"`
-}
-type CAProviderType string
-
-const (
-	CAProviderTypeSecret    CAProviderType = "Secret"
-	CAProviderTypeConfigMap CAProviderType = "ConfigMap"
-)
-
-// Defines a location to fetch the cert for the webhook provider from.
-type CAProvider struct {
-	// The type of provider to use such as "Secret", or "ConfigMap".
-	// +kubebuilder:validation:Enum="Secret";"ConfigMap"
-	Type CAProviderType `json:"type"`
-
-	// The name of the object located at the provider type.
-	Name string `json:"name"`
-
-	// The key the value inside of the provider type to use, only used with "Secret" type
-	// +kubebuilder:validation:Optional
-	Key string `json:"key,omitempty"`
-
-	// The namespace the Provider type is in.
-	// +optional
-	Namespace *string `json:"namespace,omitempty"`
+	CAProvider *esv1beta1.CAProvider `json:"caProvider,omitempty"`
 }
 
 type Result struct {

+ 16 - 30
pkg/common/webhook/webhook.go

@@ -207,7 +207,7 @@ func (w *Webhook) GetWebhookData(ctx context.Context, provider *Spec, ref *esv1b
 	return io.ReadAll(resp.Body)
 }
 
-func (w *Webhook) GetHTTPClient(provider *Spec) (*http.Client, error) {
+func (w *Webhook) GetHTTPClient(ctx context.Context, provider *Spec) (*http.Client, error) {
 	client := &http.Client{}
 	if provider.Timeout != nil {
 		client.Timeout = provider.Timeout.Duration
@@ -216,7 +216,7 @@ func (w *Webhook) GetHTTPClient(provider *Spec) (*http.Client, error) {
 		// No need to process ca stuff if it is not there
 		return client, nil
 	}
-	caCertPool, err := w.GetCACertPool(provider)
+	caCertPool, err := w.GetCACertPool(ctx, provider)
 	if err != nil {
 		return nil, err
 	}
@@ -230,37 +230,23 @@ func (w *Webhook) GetHTTPClient(provider *Spec) (*http.Client, error) {
 	return client, nil
 }
 
-func (w *Webhook) GetCACertPool(provider *Spec) (*x509.CertPool, error) {
+func (w *Webhook) GetCACertPool(ctx context.Context, provider *Spec) (*x509.CertPool, error) {
 	caCertPool := x509.NewCertPool()
-	if len(provider.CABundle) > 0 {
-		ok := caCertPool.AppendCertsFromPEM(provider.CABundle)
-		if !ok {
-			return nil, fmt.Errorf("failed to append cabundle")
-		}
+	ca, err := utils.FetchCACertFromSource(ctx, utils.CreateCertOpts{
+		CABundle:   provider.CABundle,
+		CAProvider: provider.CAProvider,
+		StoreKind:  w.StoreKind,
+		Namespace:  w.Namespace,
+		Client:     w.Kube,
+	})
+	if err != nil {
+		return nil, err
 	}
-
-	if provider.CAProvider != nil {
-		var cert []byte
-		var err error
-
-		switch provider.CAProvider.Type {
-		case CAProviderTypeSecret:
-			cert, err = w.GetCertFromSecret(provider)
-		case CAProviderTypeConfigMap:
-			cert, err = w.GetCertFromConfigMap(provider)
-		default:
-			err = fmt.Errorf("unknown caprovider type: %s", provider.CAProvider.Type)
-		}
-
-		if err != nil {
-			return nil, err
-		}
-
-		ok := caCertPool.AppendCertsFromPEM(cert)
-		if !ok {
-			return nil, fmt.Errorf("failed to append cabundle")
-		}
+	ok := caCertPool.AppendCertsFromPEM(ca)
+	if !ok {
+		return nil, fmt.Errorf("failed to append cabundle")
 	}
+
 	return caCertPool, nil
 }
 

+ 1 - 1
pkg/generator/webhook/webhook.go

@@ -42,7 +42,7 @@ func (w *Webhook) Generate(ctx context.Context, jsonSpec *apiextensions.JSON, kc
 	w.wh.Namespace = ns
 	w.url = provider.URL
 	w.wh.Kube = kclient
-	w.wh.HTTP, err = w.wh.GetHTTPClient(provider)
+	w.wh.HTTP, err = w.wh.GetHTTPClient(ctx, provider)
 	if err != nil {
 		return nil, fmt.Errorf("failed to prepare provider http client: %w", err)
 	}

+ 17 - 96
pkg/provider/akeyless/akeyless.go

@@ -37,10 +37,8 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
-	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 	"github.com/external-secrets/external-secrets/pkg/find"
 	"github.com/external-secrets/external-secrets/pkg/utils"
-	"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
 )
 
 const (
@@ -180,7 +178,7 @@ func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnin
 	return nil, nil
 }
 
-func newClient(_ context.Context, store esv1beta1.GenericStore, kube client.Client, corev1 typedcorev1.CoreV1Interface, namespace string) (esv1beta1.SecretsClient, error) {
+func newClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, corev1 typedcorev1.CoreV1Interface, namespace string) (esv1beta1.SecretsClient, error) {
 	akl := &akeylessBase{
 		kube:      kube,
 		store:     store,
@@ -202,7 +200,7 @@ func newClient(_ context.Context, store esv1beta1.GenericStore, kube client.Clie
 		return nil, fmt.Errorf("missing Auth in store config")
 	}
 
-	client, err := akl.getAkeylessHTTPClient(spec)
+	client, err := akl.getAkeylessHTTPClient(ctx, spec)
 	if err != nil {
 		return nil, err
 	}
@@ -406,16 +404,29 @@ func (a *Akeyless) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecre
 	return secretData, nil
 }
 
-func (a *akeylessBase) getAkeylessHTTPClient(provider *esv1beta1.AkeylessProvider) (*http.Client, error) {
+func (a *akeylessBase) getAkeylessHTTPClient(ctx context.Context, provider *esv1beta1.AkeylessProvider) (*http.Client, error) {
 	client := &http.Client{Timeout: 30 * time.Second}
 	if len(provider.CABundle) == 0 && provider.CAProvider == nil {
 		return client, nil
 	}
-	caCertPool, err := a.getCACertPool(provider)
+
+	cert, err := utils.FetchCACertFromSource(ctx, utils.CreateCertOpts{
+		StoreKind:  a.storeKind,
+		Client:     a.kube,
+		Namespace:  a.namespace,
+		CABundle:   provider.CABundle,
+		CAProvider: provider.CAProvider,
+	})
 	if err != nil {
 		return nil, err
 	}
 
+	caCertPool := x509.NewCertPool()
+	ok := caCertPool.AppendCertsFromPEM(cert)
+	if !ok {
+		return nil, fmt.Errorf("failed to append caBundle")
+	}
+
 	tlsConf := &tls.Config{
 		RootCAs:    caCertPool,
 		MinVersion: tls.VersionTLS12,
@@ -423,93 +434,3 @@ func (a *akeylessBase) getAkeylessHTTPClient(provider *esv1beta1.AkeylessProvide
 	client.Transport = &http.Transport{TLSClientConfig: tlsConf}
 	return client, nil
 }
-
-func (a *akeylessBase) getCACertPool(provider *esv1beta1.AkeylessProvider) (*x509.CertPool, error) {
-	caCertPool := x509.NewCertPool()
-	if len(provider.CABundle) > 0 {
-		pem, err := base64decode(provider.CABundle)
-		if err != nil {
-			pem = provider.CABundle
-		}
-		ok := caCertPool.AppendCertsFromPEM(pem)
-		if !ok {
-			return nil, fmt.Errorf("failed to append caBundle")
-		}
-	}
-
-	if provider.CAProvider != nil &&
-		a.storeKind == esv1beta1.ClusterSecretStoreKind &&
-		provider.CAProvider.Namespace == nil {
-		return nil, fmt.Errorf("missing namespace on caProvider secret")
-	}
-
-	if provider.CAProvider != nil {
-		var cert []byte
-		var err error
-
-		switch provider.CAProvider.Type {
-		case esv1beta1.CAProviderTypeSecret:
-			cert, err = a.getCertFromSecret(provider)
-		case esv1beta1.CAProviderTypeConfigMap:
-			cert, err = a.getCertFromConfigMap(provider)
-		default:
-			err = fmt.Errorf("unknown CAProvider type: %s", provider.CAProvider.Type)
-		}
-
-		if err != nil {
-			return nil, err
-		}
-		pem, err := base64decode(cert)
-		if err != nil {
-			pem = cert
-		}
-		ok := caCertPool.AppendCertsFromPEM(pem)
-		if !ok {
-			return nil, fmt.Errorf("failed to append caBundle")
-		}
-	}
-	return caCertPool, nil
-}
-
-func (a *akeylessBase) getCertFromSecret(provider *esv1beta1.AkeylessProvider) ([]byte, error) {
-	secretRef := esmeta.SecretKeySelector{
-		Name: provider.CAProvider.Name,
-		Key:  provider.CAProvider.Key,
-	}
-
-	if provider.CAProvider.Namespace != nil {
-		secretRef.Namespace = provider.CAProvider.Namespace
-	}
-
-	ctx := context.Background()
-	cert, err := resolvers.SecretKeyRef(ctx, a.kube, a.storeKind, a.namespace, &secretRef)
-	if err != nil {
-		return nil, err
-	}
-
-	return []byte(cert), nil
-}
-
-func (a *akeylessBase) getCertFromConfigMap(provider *esv1beta1.AkeylessProvider) ([]byte, error) {
-	objKey := client.ObjectKey{
-		Name: provider.CAProvider.Name,
-	}
-
-	if provider.CAProvider.Namespace != nil {
-		objKey.Namespace = *provider.CAProvider.Namespace
-	}
-
-	configMapRef := &corev1.ConfigMap{}
-	ctx := context.Background()
-	err := a.kube.Get(ctx, objKey, configMapRef)
-	if err != nil {
-		return nil, fmt.Errorf("failed to get caProvider secret %s: %w", objKey.Name, err)
-	}
-
-	val, ok := configMapRef.Data[provider.CAProvider.Key]
-	if !ok {
-		return nil, fmt.Errorf("failed to get caProvider configMap %s -> %s", objKey.Name, provider.CAProvider.Key)
-	}
-
-	return []byte(val), nil
-}

+ 0 - 10
pkg/provider/akeyless/utils.go

@@ -15,7 +15,6 @@ limitations under the License.
 package akeyless
 
 import (
-	"encoding/base64"
 	"fmt"
 	"io"
 	"net/http"
@@ -109,12 +108,3 @@ func sendReq(url string) string {
 	body, _ := io.ReadAll(resp.Body)
 	return string(body)
 }
-
-func base64decode(in []byte) ([]byte, error) {
-	out := make([]byte, len(in))
-	l, err := base64.StdEncoding.Decode(out, in)
-	if err != nil {
-		return nil, err
-	}
-	return out[:l], nil
-}

+ 10 - 6
pkg/provider/bitwarden/bitwarden_sdk.go

@@ -21,6 +21,10 @@ import (
 	"fmt"
 	"io"
 	"net/http"
+
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	"github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 )
 
 // Defined Header Keys.
@@ -100,18 +104,18 @@ type SdkClient struct {
 	client *http.Client
 }
 
-func NewSdkClient(apiURL, identityURL, bitwardenURL, token string, caBundle []byte) (*SdkClient, error) {
-	client, err := newHTTPSClient(caBundle)
+func NewSdkClient(ctx context.Context, c client.Client, storeKind, namespace string, provider *v1beta1.BitwardenSecretsManagerProvider, token string) (*SdkClient, error) {
+	httpsClient, err := newHTTPSClient(ctx, c, storeKind, namespace, provider)
 	if err != nil {
 		return nil, fmt.Errorf("error creating https client: %w", err)
 	}
 
 	return &SdkClient{
-		apiURL:                apiURL,
-		identityURL:           identityURL,
+		apiURL:                provider.APIURL,
+		identityURL:           provider.IdentityURL,
+		bitwardenSdkServerURL: provider.BitwardenServerSDKURL,
 		token:                 token,
-		client:                client,
-		bitwardenSdkServerURL: bitwardenURL,
+		client:                httpsClient,
 	}, nil
 }
 

+ 0 - 14
pkg/provider/bitwarden/client.go

@@ -26,10 +26,6 @@ import (
 	"github.com/external-secrets/external-secrets/pkg/utils"
 )
 
-var (
-	errBadCertBundle = "caBundle failed to base64 decode: %w"
-)
-
 const (
 	// NoteMetadataKey defines the note for the pushed secret.
 	NoteMetadataKey = "note"
@@ -249,16 +245,6 @@ func (p *Provider) Close(_ context.Context) error {
 	return nil
 }
 
-// getCABundle try retrieve the CA bundle from the provider CABundle.
-func (p *Provider) getCABundle(provider *esv1beta1.BitwardenSecretsManagerProvider) ([]byte, error) {
-	certBytes, decodeErr := utils.Decode(esv1beta1.ExternalSecretDecodeBase64, []byte(provider.CABundle))
-	if decodeErr != nil {
-		return nil, fmt.Errorf(errBadCertBundle, decodeErr)
-	}
-
-	return certBytes, nil
-}
-
 func (p *Provider) findSecretByRef(ctx context.Context, key, projectID string) (*SecretResponse, error) {
 	spec := p.store.GetSpec()
 	if spec == nil || spec.Provider == nil {

+ 41 - 13
pkg/provider/bitwarden/provider.go

@@ -26,6 +26,7 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
+	"github.com/external-secrets/external-secrets/pkg/utils"
 	"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
 )
 
@@ -58,17 +59,12 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore,
 		return nil, fmt.Errorf("could not resolve auth credentials: %w", err)
 	}
 
-	bundle, err := p.getCABundle(storeSpec.Provider.BitwardenSecretsManager)
-	if err != nil {
-		return nil, fmt.Errorf("could not resolve caBundle: %w", err)
-	}
-
-	sdkClient, err := NewSdkClient(
-		storeSpec.Provider.BitwardenSecretsManager.APIURL,
-		storeSpec.Provider.BitwardenSecretsManager.IdentityURL,
-		storeSpec.Provider.BitwardenSecretsManager.BitwardenServerSDKURL,
+	sdkClient, err := NewSdkClient(ctx,
+		kube,
+		store.GetKind(),
+		namespace,
+		storeSpec.Provider.BitwardenSecretsManager,
 		token,
-		bundle,
 	)
 	if err != nil {
 		return nil, fmt.Errorf("could not create SdkClient: %w", err)
@@ -88,17 +84,49 @@ func (p *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
 }
 
 // ValidateStore validates the store.
-func (p *Provider) ValidateStore(_ esv1beta1.GenericStore) (admission.Warnings, error) {
+func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnings, error) {
+	storeSpec := store.GetSpec()
+	if storeSpec == nil {
+		return admission.Warnings{}, fmt.Errorf("no store type or wrong store type")
+	}
+
+	if storeSpec.Provider == nil {
+		return admission.Warnings{}, fmt.Errorf("provider not configured")
+	}
+
+	bitwardenSpec := storeSpec.Provider.BitwardenSecretsManager
+	if bitwardenSpec == nil {
+		return admission.Warnings{}, fmt.Errorf("bitwarden spec not configured")
+	}
+
+	if bitwardenSpec.CAProvider == nil && bitwardenSpec.CABundle == "" {
+		return admission.Warnings{
+			"Neither CA nor CA bundle is configured; user is expected to provide certificate information via volume mount.",
+		}, nil
+	}
+
 	return nil, nil
 }
 
 // newHTTPSClient creates a new HTTPS client with the given cert.
-func newHTTPSClient(cert []byte) (*http.Client, error) {
+func newHTTPSClient(ctx context.Context, c client.Client, storeKind, namespace string, provider *esv1beta1.BitwardenSecretsManagerProvider) (*http.Client, error) {
+	cert, err := utils.FetchCACertFromSource(ctx, utils.CreateCertOpts{
+		CABundle:   []byte(provider.CABundle),
+		CAProvider: provider.CAProvider,
+		StoreKind:  storeKind,
+		Namespace:  namespace,
+		Client:     c,
+	})
+	if err != nil {
+		return nil, err
+	}
+
 	pool := x509.NewCertPool()
 	ok := pool.AppendCertsFromPEM(cert)
 	if !ok {
-		return nil, fmt.Errorf("can't append Conjur SSL cert")
+		return nil, fmt.Errorf("failed to append caBundle")
 	}
+
 	tr := &http.Transport{
 		TLSClientConfig: &tls.Config{RootCAs: pool, MinVersion: tls.VersionTLS12},
 	}

+ 12 - 80
pkg/provider/conjur/client.go

@@ -17,7 +17,6 @@ package conjur
 import (
 	"context"
 	"fmt"
-	"strings"
 
 	"github.com/cyberark/conjur-api-go/conjurapi"
 	"github.com/cyberark/conjur-api-go/conjurapi/authn"
@@ -26,24 +25,17 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
-	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 	"github.com/external-secrets/external-secrets/pkg/provider/conjur/util"
 	"github.com/external-secrets/external-secrets/pkg/utils"
 	"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
 )
 
 var (
-	errConjurClient     = "cannot setup new Conjur client: %w"
-	errBadCertBundle    = "caBundle failed to base64 decode: %w"
-	errBadServiceUser   = "could not get Auth.Apikey.UserRef: %w"
-	errBadServiceAPIKey = "could not get Auth.Apikey.ApiKeyRef: %w"
-
+	errConjurClient          = "cannot setup new Conjur client: %w"
+	errBadServiceUser        = "could not get Auth.Apikey.UserRef: %w"
+	errBadServiceAPIKey      = "could not get Auth.Apikey.ApiKeyRef: %w"
 	errGetKubeSATokenRequest = "cannot request Kubernetes service account token for service account %q: %w"
-
-	errUnableToFetchCAProviderCM     = "unable to fetch Server.CAProvider ConfigMap: %w"
-	errUnableToFetchCAProviderSecret = "unable to fetch Server.CAProvider Secret: %w"
-
-	errSecretKeyFmt = "cannot find secret data for key: %q"
+	errSecretKeyFmt          = "cannot find secret data for key: %q"
 )
 
 // Client is a provider for Conjur.
@@ -68,14 +60,20 @@ func (c *Client) GetConjurClient(ctx context.Context) (SecretsClient, error) {
 		return nil, err
 	}
 
-	cert, getCertErr := c.getCA(ctx, prov)
+	cert, getCertErr := utils.FetchCACertFromSource(ctx, utils.CreateCertOpts{
+		CABundle:   []byte(prov.CABundle),
+		CAProvider: prov.CAProvider,
+		StoreKind:  c.store.GetKind(),
+		Namespace:  c.namespace,
+		Client:     c.kube,
+	})
 	if getCertErr != nil {
 		return nil, getCertErr
 	}
 
 	config := conjurapi.Config{
 		ApplianceURL: prov.URL,
-		SSLCert:      cert,
+		SSLCert:      string(cert),
 	}
 
 	if prov.Auth.APIKey != nil {
@@ -151,69 +149,3 @@ func (c *Client) Validate() (esv1beta1.ValidationResult, error) {
 func (c *Client) Close(_ context.Context) error {
 	return nil
 }
-
-// configMapKeyRef returns the value of a key in a ConfigMap.
-func (c *Client) configMapKeyRef(ctx context.Context, cmRef *esmeta.SecretKeySelector) (string, error) {
-	configMap := &corev1.ConfigMap{}
-	ref := client.ObjectKey{
-		Namespace: c.namespace,
-		Name:      cmRef.Name,
-	}
-	if (c.StoreKind == esv1beta1.ClusterSecretStoreKind) &&
-		(cmRef.Namespace != nil) {
-		ref.Namespace = *cmRef.Namespace
-	}
-	err := c.kube.Get(ctx, ref, configMap)
-	if err != nil {
-		return "", err
-	}
-
-	keyBytes, ok := configMap.Data[cmRef.Key]
-	if !ok {
-		return "", err
-	}
-
-	valueStr := strings.TrimSpace(keyBytes)
-	return valueStr, nil
-}
-
-// getCA try retrieve the CA bundle from the provider CABundle or from the CAProvider.
-func (c *Client) getCA(ctx context.Context, provider *esv1beta1.ConjurProvider) (string, error) {
-	if provider.CAProvider != nil {
-		var ca string
-		var err error
-		switch provider.CAProvider.Type {
-		case esv1beta1.CAProviderTypeConfigMap:
-			keySelector := esmeta.SecretKeySelector{
-				Name:      provider.CAProvider.Name,
-				Namespace: provider.CAProvider.Namespace,
-				Key:       provider.CAProvider.Key,
-			}
-			ca, err = c.configMapKeyRef(ctx, &keySelector)
-			if err != nil {
-				return "", fmt.Errorf(errUnableToFetchCAProviderCM, err)
-			}
-		case esv1beta1.CAProviderTypeSecret:
-			keySelector := esmeta.SecretKeySelector{
-				Name:      provider.CAProvider.Name,
-				Namespace: provider.CAProvider.Namespace,
-				Key:       provider.CAProvider.Key,
-			}
-			ca, err = resolvers.SecretKeyRef(
-				ctx,
-				c.kube,
-				c.StoreKind,
-				c.namespace,
-				&keySelector)
-			if err != nil {
-				return "", fmt.Errorf(errUnableToFetchCAProviderSecret, err)
-			}
-		}
-		return ca, nil
-	}
-	certBytes, decodeErr := utils.Decode(esv1beta1.ExternalSecretDecodeBase64, []byte(provider.CABundle))
-	if decodeErr != nil {
-		return "", fmt.Errorf(errBadCertBundle, decodeErr)
-	}
-	return string(certBytes), nil
-}

File diff suppressed because it is too large
+ 15 - 2
pkg/provider/conjur/provider_test.go


+ 9 - 69
pkg/provider/kubernetes/auth.go

@@ -19,23 +19,18 @@ import (
 	"fmt"
 
 	authenticationv1 "k8s.io/api/authentication/v1"
-	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/client-go/rest"
 	"k8s.io/client-go/tools/clientcmd"
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
+	"github.com/external-secrets/external-secrets/pkg/utils"
 	"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
 )
 
 const (
-	errInvalidClusterStoreMissingNamespace = "missing namespace"
-	errFetchCredentials                    = "could not fetch credentials: %w"
-	errMissingCredentials                  = "missing credentials: \"%s\""
-	errEmptyKey                            = "key %s found but empty"
-	errUnableCreateToken                   = "cannot create service account token: %q"
+	errUnableCreateToken = "cannot create service account token: %q"
 )
 
 func (c *Client) getAuth(ctx context.Context) (*rest.Config, error) {
@@ -48,7 +43,13 @@ func (c *Client) getAuth(ctx context.Context) (*rest.Config, error) {
 		return clientcmd.RESTConfigFromKubeConfig(cfg)
 	}
 
-	ca, err := c.getCA(ctx)
+	ca, err := utils.FetchCACertFromSource(ctx, utils.CreateCertOpts{
+		CABundle:   c.store.Server.CABundle,
+		CAProvider: c.store.Server.CAProvider,
+		StoreKind:  c.storeKind,
+		Namespace:  c.namespace,
+		Client:     c.ctrlClient,
+	})
 	if err != nil {
 		return nil, err
 	}
@@ -92,40 +93,6 @@ func (c *Client) getAuth(ctx context.Context) (*rest.Config, error) {
 	}, nil
 }
 
-func (c *Client) getCA(ctx context.Context) ([]byte, error) {
-	if c.store.Server.CABundle != nil {
-		return c.store.Server.CABundle, nil
-	}
-	if c.store.Server.CAProvider != nil {
-		var ca []byte
-		var err error
-		switch c.store.Server.CAProvider.Type {
-		case esv1beta1.CAProviderTypeConfigMap:
-			keySelector := esmeta.SecretKeySelector{
-				Name:      c.store.Server.CAProvider.Name,
-				Namespace: c.store.Server.CAProvider.Namespace,
-				Key:       c.store.Server.CAProvider.Key,
-			}
-			ca, err = c.fetchConfigMapKey(ctx, keySelector)
-			if err != nil {
-				return nil, fmt.Errorf("unable to fetch Server.CAProvider ConfigMap: %w", err)
-			}
-		case esv1beta1.CAProviderTypeSecret:
-			keySelector := esmeta.SecretKeySelector{
-				Name:      c.store.Server.CAProvider.Name,
-				Namespace: c.store.Server.CAProvider.Namespace,
-				Key:       c.store.Server.CAProvider.Key,
-			}
-			ca, err = c.fetchSecretKey(ctx, keySelector)
-			if err != nil {
-				return nil, fmt.Errorf("unable to fetch Server.CAProvider Secret: %w", err)
-			}
-		}
-		return ca, nil
-	}
-	return nil, fmt.Errorf("no Certificate Authority provided")
-}
-
 func (c *Client) getClientKeyAndCert(ctx context.Context) ([]byte, []byte, error) {
 	var err error
 	cert, err := c.fetchSecretKey(ctx, c.store.Auth.Cert.ClientCert)
@@ -171,30 +138,3 @@ func (c *Client) fetchSecretKey(ctx context.Context, ref esmeta.SecretKeySelecto
 	}
 	return []byte(secret), nil
 }
-
-func (c *Client) fetchConfigMapKey(ctx context.Context, key esmeta.SecretKeySelector) ([]byte, error) {
-	configMap := &corev1.ConfigMap{}
-	objectKey := types.NamespacedName{
-		Name:      key.Name,
-		Namespace: c.namespace,
-	}
-	// only ClusterStore is allowed to set namespace (and then it's required)
-	if c.storeKind == esv1beta1.ClusterSecretStoreKind {
-		if key.Namespace == nil {
-			return nil, fmt.Errorf(errInvalidClusterStoreMissingNamespace)
-		}
-		objectKey.Namespace = *key.Namespace
-	}
-	err := c.ctrlClient.Get(ctx, objectKey, configMap)
-	if err != nil {
-		return nil, fmt.Errorf(errFetchCredentials, err)
-	}
-	val, ok := configMap.Data[key.Key]
-	if !ok {
-		return nil, fmt.Errorf(errMissingCredentials, key.Key)
-	}
-	if val == "" {
-		return nil, fmt.Errorf(errEmptyKey, key.Key)
-	}
-	return []byte(val), nil
-}

+ 12 - 14
pkg/provider/kubernetes/auth_test.go

@@ -18,7 +18,7 @@ import (
 	"context"
 	"testing"
 
-	"github.com/google/go-cmp/cmp"
+	"github.com/stretchr/testify/assert"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
@@ -99,7 +99,7 @@ func TestSetAuth(t *testing.T) {
 			fields: fields{
 				store: &esv1beta1.KubernetesProvider{
 					Server: esv1beta1.KubernetesServer{
-						CABundle: []byte("1234"),
+						CABundle: []byte(caCert),
 					},
 				},
 			},
@@ -116,7 +116,7 @@ func TestSetAuth(t *testing.T) {
 						Namespace: "default",
 					},
 					Data: map[string][]byte{
-						"cert":  []byte("1234"),
+						"cert":  []byte(caCert),
 						"token": []byte("mytoken"),
 					},
 				}).Build(),
@@ -144,7 +144,7 @@ func TestSetAuth(t *testing.T) {
 				Host:        "https://my.test.tld",
 				BearerToken: "mytoken",
 				TLSClientConfig: rest.TLSClientConfig{
-					CAData: []byte("1234"),
+					CAData: []byte(caCert),
 				},
 			},
 			wantErr: false,
@@ -215,7 +215,7 @@ func TestSetAuth(t *testing.T) {
 				store: &esv1beta1.KubernetesProvider{
 					Server: esv1beta1.KubernetesServer{
 						URL:      "https://my.test.tld",
-						CABundle: []byte("1234"),
+						CABundle: []byte(caCert),
 					},
 					Auth: esv1beta1.KubernetesAuth{
 						Token: &esv1beta1.TokenAuth{
@@ -232,7 +232,7 @@ func TestSetAuth(t *testing.T) {
 				Host:        "https://my.test.tld",
 				BearerToken: "mytoken",
 				TLSClientConfig: rest.TLSClientConfig{
-					CAData: []byte("1234"),
+					CAData: []byte(caCert),
 				},
 			},
 			wantErr: false,
@@ -262,7 +262,7 @@ func TestSetAuth(t *testing.T) {
 				store: &esv1beta1.KubernetesProvider{
 					Server: esv1beta1.KubernetesServer{
 						URL:      "https://my.test.tld",
-						CABundle: []byte("1234"),
+						CABundle: []byte(caCert),
 					},
 					Auth: esv1beta1.KubernetesAuth{
 						Token: &esv1beta1.TokenAuth{
@@ -289,7 +289,7 @@ func TestSetAuth(t *testing.T) {
 				Host:        "https://my.test.tld",
 				BearerToken: "mytoken",
 				TLSClientConfig: rest.TLSClientConfig{
-					CAData:   []byte("1234"),
+					CAData:   []byte(caCert),
 					CertData: []byte("my-cert"),
 					KeyData:  []byte("my-key"),
 				},
@@ -310,7 +310,7 @@ func TestSetAuth(t *testing.T) {
 				store: &esv1beta1.KubernetesProvider{
 					Server: esv1beta1.KubernetesServer{
 						URL:      "https://my.test.tld",
-						CABundle: []byte("1234"),
+						CABundle: []byte(caCert),
 					},
 					Auth: esv1beta1.KubernetesAuth{
 						ServiceAccount: &v1.ServiceAccountSelector{
@@ -324,7 +324,7 @@ func TestSetAuth(t *testing.T) {
 				Host:        "https://my.test.tld",
 				BearerToken: "my-sa-token",
 				TLSClientConfig: rest.TLSClientConfig{
-					CAData: []byte("1234"),
+					CAData: []byte(caCert),
 				},
 			},
 			wantErr: false,
@@ -342,7 +342,7 @@ func TestSetAuth(t *testing.T) {
 				kubeclientset: utilfake.NewCreateTokenMock().WithToken("my-sa-token"),
 				store: &esv1beta1.KubernetesProvider{
 					Server: esv1beta1.KubernetesServer{
-						CABundle: []byte("1234"),
+						CABundle: []byte(caCert),
 					},
 					Auth: esv1beta1.KubernetesAuth{
 						ServiceAccount: &v1.ServiceAccountSelector{
@@ -399,9 +399,7 @@ func TestSetAuth(t *testing.T) {
 			if (err != nil) != tt.wantErr {
 				t.Errorf("BaseClient.setAuth() error = %v, wantErr %v", err, tt.wantErr)
 			}
-			if !cmp.Equal(cfg, tt.want) {
-				t.Errorf("unexpected value: expected %#v, got %#v", tt.want, cfg)
-			}
+			assert.Equal(t, tt.want, cfg)
 		})
 	}
 }

+ 1 - 1
pkg/provider/vault/auth_approle.go

@@ -19,7 +19,7 @@ import (
 	"fmt"
 	"strings"
 
-	approle "github.com/hashicorp/vault/api/auth/approle"
+	"github.com/hashicorp/vault/api/auth/approle"
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	"github.com/external-secrets/external-secrets/pkg/constants"

+ 13 - 78
pkg/provider/vault/client.go

@@ -25,13 +25,12 @@ import (
 	"github.com/go-logr/logr"
 	vault "github.com/hashicorp/vault/api"
 	corev1 "k8s.io/api/core/v1"
-	"k8s.io/apimachinery/pkg/types"
 	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
 	kclient "sigs.k8s.io/controller-runtime/pkg/client"
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
-	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 	"github.com/external-secrets/external-secrets/pkg/provider/vault/util"
+	"github.com/external-secrets/external-secrets/pkg/utils"
 	"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
 )
 
@@ -56,39 +55,19 @@ func (c *client) newConfig(ctx context.Context) (*vault.Config, error) {
 
 	if len(c.store.CABundle) != 0 || c.store.CAProvider != nil {
 		caCertPool := x509.NewCertPool()
-
-		if len(c.store.CABundle) > 0 {
-			ok := caCertPool.AppendCertsFromPEM(c.store.CABundle)
-			if !ok {
-				return nil, fmt.Errorf(errVaultCert, errors.New("failed to parse certificates from CertPool"))
-			}
-		}
-
-		if c.store.CAProvider != nil && c.storeKind == esv1beta1.ClusterSecretStoreKind && c.store.CAProvider.Namespace == nil {
-			return nil, errors.New(errCANamespace)
+		ca, err := utils.FetchCACertFromSource(ctx, utils.CreateCertOpts{
+			CABundle:   c.store.CABundle,
+			CAProvider: c.store.CAProvider,
+			StoreKind:  c.storeKind,
+			Namespace:  c.namespace,
+			Client:     c.kube,
+		})
+		if err != nil {
+			return nil, err
 		}
-
-		if c.store.CAProvider != nil {
-			var cert []byte
-			var err error
-
-			switch c.store.CAProvider.Type {
-			case esv1beta1.CAProviderTypeSecret:
-				cert, err = getCertFromSecret(c)
-			case esv1beta1.CAProviderTypeConfigMap:
-				cert, err = getCertFromConfigMap(c)
-			default:
-				return nil, errors.New(errUnknownCAProvider)
-			}
-
-			if err != nil {
-				return nil, err
-			}
-
-			ok := caCertPool.AppendCertsFromPEM(cert)
-			if !ok {
-				return nil, fmt.Errorf(errVaultCert, errors.New("failed to parse certificates from CertPool"))
-			}
+		ok := caCertPool.AppendCertsFromPEM(ca)
+		if !ok {
+			return nil, fmt.Errorf(errVaultCert, errors.New("failed to parse certificates from CertPool"))
 		}
 
 		if transport, ok := cfg.HttpClient.Transport.(*http.Transport); ok {
@@ -138,50 +117,6 @@ func (c *client) configureClientTLS(ctx context.Context, cfg *vault.Config) erro
 	return nil
 }
 
-func getCertFromSecret(v *client) ([]byte, error) {
-	secretRef := esmeta.SecretKeySelector{
-		Name:      v.store.CAProvider.Name,
-		Namespace: &v.namespace,
-		Key:       v.store.CAProvider.Key,
-	}
-
-	if v.store.CAProvider.Namespace != nil {
-		secretRef.Namespace = v.store.CAProvider.Namespace
-	}
-
-	ctx := context.Background()
-	res, err := resolvers.SecretKeyRef(ctx, v.kube, v.storeKind, v.namespace, &secretRef)
-	if err != nil {
-		return nil, fmt.Errorf(errVaultCert, err)
-	}
-
-	return []byte(res), nil
-}
-
-func getCertFromConfigMap(v *client) ([]byte, error) {
-	objKey := types.NamespacedName{
-		Name:      v.store.CAProvider.Name,
-		Namespace: v.namespace,
-	}
-
-	if v.store.CAProvider.Namespace != nil {
-		objKey.Namespace = *v.store.CAProvider.Namespace
-	}
-
-	configMapRef := &corev1.ConfigMap{}
-	ctx := context.Background()
-	err := v.kube.Get(ctx, objKey, configMapRef)
-	if err != nil {
-		return nil, fmt.Errorf(errVaultCert, err)
-	}
-
-	val, ok := configMapRef.Data[v.store.CAProvider.Key]
-	if !ok {
-		return nil, fmt.Errorf(errConfigMapFmt, v.store.CAProvider.Key)
-	}
-	return []byte(val), nil
-}
-
 func (c *client) Close(ctx context.Context) error {
 	// Revoke the token if we have one set, it wasn't sourced from a TokenSecretRef,
 	// and token caching isn't enabled

+ 5 - 7
pkg/provider/vault/provider.go

@@ -43,13 +43,11 @@ var (
 )
 
 const (
-	errVaultStore        = "received invalid Vault SecretStore resource: %w"
-	errVaultClient       = "cannot setup new vault client: %w"
-	errVaultCert         = "cannot set Vault CA certificate: %w"
-	errConfigMapFmt      = "cannot find config map data for key: %q"
-	errClientTLSAuth     = "error from Client TLS Auth: %q"
-	errUnknownCAProvider = "unknown caProvider type given"
-	errCANamespace       = "cannot read secret for CAProvider due to missing namespace on kind ClusterSecretStore"
+	errVaultStore    = "received invalid Vault SecretStore resource: %w"
+	errVaultClient   = "cannot setup new vault client: %w"
+	errVaultCert     = "cannot set Vault CA certificate: %w"
+	errClientTLSAuth = "error from Client TLS Auth: %q"
+	errCANamespace   = "missing namespace on caProvider secret"
 )
 
 type Provider struct {

+ 4 - 4
pkg/provider/vault/provider_test.go

@@ -306,7 +306,7 @@ MIIFkTCCA3mgAwIBAgIUBEUg3m/WqAsWHG4Q/II3IePFfuowDQYJKoZIhvcNAQELBQAwWDELMAkGA1UE
 				}),
 			},
 			want: want{
-				err: fmt.Errorf(errVaultCert, errors.New("failed to parse certificates from CertPool")),
+				err: fmt.Errorf("failed to decode ca bundle: %w", errors.New("failed to parse the new certificate, not valid pem data")),
 			},
 		},
 		"VaultAuthFormatError": {
@@ -419,7 +419,7 @@ MIIFkTCCA3mgAwIBAgIUBEUg3m/WqAsWHG4Q/II3IePFfuowDQYJKoZIhvcNAQELBQAwWDELMAkGA1UE
 				newClientFunc: fake.ClientWithLoginMock,
 			},
 			want: want{
-				err: fmt.Errorf(errVaultCert, errors.New(`cannot find secret data for key: "cert"`)),
+				err: fmt.Errorf("failed to get cert from secret: %w", fmt.Errorf("failed to resolve secret key ref: %w", errors.New("cannot find secret data for key: \"cert\""))),
 			},
 		},
 		"SuccessfulVaultStoreWithIamAuthSecret": {
@@ -491,7 +491,7 @@ MIIFkTCCA3mgAwIBAgIUBEUg3m/WqAsWHG4Q/II3IePFfuowDQYJKoZIhvcNAQELBQAwWDELMAkGA1UE
 				newClientFunc: fake.ClientWithLoginMock,
 			},
 			want: want{
-				err: fmt.Errorf(errConfigMapFmt, "cert"),
+				err: fmt.Errorf("failed to get cert from configmap: %w", errors.New("failed to get caProvider configMap vault-cert -> cert")),
 			},
 		},
 		"GetCertificateFormatError": {
@@ -506,7 +506,7 @@ MIIFkTCCA3mgAwIBAgIUBEUg3m/WqAsWHG4Q/II3IePFfuowDQYJKoZIhvcNAQELBQAwWDELMAkGA1UE
 					},
 					Data: map[string][]byte{
 						tlsKey: secretClientKey,
-						tlsCrt: []byte("cert with mistak"),
+						tlsCrt: []byte("cert with mistake"),
 					},
 				}).Build(),
 				newClientFunc: fake.ClientWithLoginMock,

+ 4 - 4
pkg/provider/webhook/webhook.go

@@ -60,7 +60,7 @@ func (p *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
 	return esv1beta1.SecretStoreReadOnly
 }
 
-func (p *Provider) NewClient(_ context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
+func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, namespace string) (esv1beta1.SecretsClient, error) {
 	wh := webhook.Webhook{
 		Kube:      kube,
 		Namespace: namespace,
@@ -80,7 +80,7 @@ func (p *Provider) NewClient(_ context.Context, store esv1beta1.GenericStore, ku
 	}
 	whClient.url = provider.URL
 
-	whClient.wh.HTTP, err = whClient.wh.GetHTTPClient(provider)
+	whClient.wh.HTTP, err = whClient.wh.GetHTTPClient(ctx, provider)
 	if err != nil {
 		return nil, err
 	}
@@ -113,12 +113,12 @@ func (w *WebHook) SecretExists(_ context.Context, _ esv1beta1.PushSecretRemoteRe
 	return false, fmt.Errorf(errNotImplemented)
 }
 
-// Not Implemented PushSecret.
+// PushSecret not implement.
 func (w *WebHook) PushSecret(_ context.Context, _ *corev1.Secret, _ esv1beta1.PushSecretData) error {
 	return fmt.Errorf(errNotImplemented)
 }
 
-// Empty GetAllSecrets.
+// GetAllSecrets Empty .
 func (w *WebHook) GetAllSecrets(_ context.Context, _ esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
 	// TO be implemented
 	return nil, fmt.Errorf(errNotImplemented)

+ 127 - 1
pkg/utils/utils.go

@@ -16,9 +16,12 @@ package utils
 
 import (
 	"bytes"
+	"context"
 	"crypto/md5" //nolint:gosec
+	"crypto/x509"
 	"encoding/base64"
 	"encoding/json"
+	"encoding/pem"
 	"errors"
 	"fmt"
 	"net"
@@ -31,12 +34,15 @@ import (
 	"time"
 	"unicode"
 
+	corev1 "k8s.io/api/core/v1"
 	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
+	"sigs.k8s.io/controller-runtime/pkg/client"
 
 	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 	"github.com/external-secrets/external-secrets/pkg/template/v2"
+	"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
 )
 
 const (
@@ -139,7 +145,7 @@ func transform(val string, data map[string][]byte) ([]byte, error) {
 	return buf.Bytes(), nil
 }
 
-// DecodeValues decodes values from a secretMap.
+// DecodeMap decodes values from a secretMap.
 func DecodeMap(strategy esv1beta1.ExternalSecretDecodingStrategy, in map[string][]byte) (map[string][]byte, error) {
 	out := make(map[string][]byte, len(in))
 	for k, v := range in {
@@ -515,3 +521,123 @@ func CompareStringAndByteSlices(valueString *string, valueByte []byte) bool {
 
 	return bytes.Equal(valueByte, []byte(*valueString))
 }
+
+// CreateCertOpts contains options for a cert pool creation.
+type CreateCertOpts struct {
+	CABundle   []byte
+	CAProvider *esv1beta1.CAProvider
+	StoreKind  string
+	Namespace  string
+	Client     client.Client
+}
+
+// FetchCACertFromSource creates a CertPool using either a CABundle directly, or
+// a ConfigMap / Secret.
+func FetchCACertFromSource(ctx context.Context, opts CreateCertOpts) ([]byte, error) {
+	if len(opts.CABundle) == 0 && opts.CAProvider == nil {
+		return nil, nil
+	}
+
+	if len(opts.CABundle) > 0 {
+		pem, err := base64decode(opts.CABundle)
+		if err != nil {
+			return nil, fmt.Errorf("failed to decode ca bundle: %w", err)
+		}
+
+		return pem, nil
+	}
+
+	if opts.CAProvider != nil &&
+		opts.StoreKind == esv1beta1.ClusterSecretStoreKind &&
+		opts.CAProvider.Namespace == nil {
+		return nil, fmt.Errorf("missing namespace on caProvider secret")
+	}
+
+	switch opts.CAProvider.Type {
+	case esv1beta1.CAProviderTypeSecret:
+		cert, err := getCertFromSecret(ctx, opts.Client, opts.CAProvider, opts.StoreKind, opts.Namespace)
+		if err != nil {
+			return nil, fmt.Errorf("failed to get cert from secret: %w", err)
+		}
+
+		return cert, nil
+	case esv1beta1.CAProviderTypeConfigMap:
+		cert, err := getCertFromConfigMap(ctx, opts.Namespace, opts.Client, opts.CAProvider)
+		if err != nil {
+			return nil, fmt.Errorf("failed to get cert from configmap: %w", err)
+		}
+
+		return cert, nil
+	}
+
+	return nil, fmt.Errorf("unsupported CA provider type: %s", opts.CAProvider.Type)
+}
+
+func base64decode(cert []byte) ([]byte, error) {
+	if c, err := parseCertificateBytes(cert); err == nil {
+		return c, nil
+	}
+
+	// try decoding and test for validity again...
+	certificate, err := Decode(esv1beta1.ExternalSecretDecodeAuto, cert)
+	if err != nil {
+		return nil, fmt.Errorf("failed to decode base64: %w", err)
+	}
+
+	return parseCertificateBytes(certificate)
+}
+
+func parseCertificateBytes(certBytes []byte) ([]byte, error) {
+	block, _ := pem.Decode(certBytes)
+	if block == nil {
+		return nil, errors.New("failed to parse the new certificate, not valid pem data")
+	}
+
+	if _, err := x509.ParseCertificate(block.Bytes); err != nil {
+		return nil, fmt.Errorf("failed to validate certificate: %w", err)
+	}
+
+	return certBytes, nil
+}
+
+func getCertFromSecret(ctx context.Context, c client.Client, provider *esv1beta1.CAProvider, storeKind, namespace string) ([]byte, error) {
+	secretRef := esmeta.SecretKeySelector{
+		Name: provider.Name,
+		Key:  provider.Key,
+	}
+
+	if provider.Namespace != nil {
+		secretRef.Namespace = provider.Namespace
+	}
+
+	cert, err := resolvers.SecretKeyRef(ctx, c, storeKind, namespace, &secretRef)
+	if err != nil {
+		return nil, fmt.Errorf("failed to resolve secret key ref: %w", err)
+	}
+
+	return []byte(cert), nil
+}
+
+func getCertFromConfigMap(ctx context.Context, namespace string, c client.Client, provider *esv1beta1.CAProvider) ([]byte, error) {
+	objKey := client.ObjectKey{
+		Name:      provider.Name,
+		Namespace: namespace,
+	}
+
+	if provider.Namespace != nil {
+		objKey.Namespace = *provider.Namespace
+	}
+
+	configMapRef := &corev1.ConfigMap{}
+	err := c.Get(ctx, objKey, configMapRef)
+	if err != nil {
+		return nil, fmt.Errorf("failed to get caProvider secret %s: %w", objKey.Name, err)
+	}
+
+	val, ok := configMapRef.Data[provider.Key]
+	if !ok {
+		return nil, fmt.Errorf("failed to get caProvider configMap %s -> %s", objKey.Name, provider.Key)
+	}
+
+	return []byte(val), nil
+}