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"`
 	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
 	// Base64 encoded certificate for the bitwarden server sdk. The sdk MUST run with HTTPS to make sure no MITM attack
 	// can be performed.
 	// 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 determines which organization this secret store manages.
 	OrganizationID string `json:"organizationID"`
 	OrganizationID string `json:"organizationID"`
 	// ProjectID determines which project this secret store manages.
 	// 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.
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *BitwardenSecretsManagerProvider) DeepCopyInto(out *BitwardenSecretsManagerProvider) {
 func (in *BitwardenSecretsManagerProvider) DeepCopyInto(out *BitwardenSecretsManagerProvider) {
 	*out = *in
 	*out = *in
+	if in.CAProvider != nil {
+		in, out := &in.CAProvider, &out.CAProvider
+		*out = new(CAProvider)
+		(*in).DeepCopyInto(*out)
+	}
 	in.Auth.DeepCopyInto(&out.Auth)
 	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
                           Base64 encoded certificate for the bitwarden server sdk. The sdk MUST run with HTTPS to make sure no MITM attack
                           can be performed.
                           can be performed.
                         type: string
                         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:
                       identityURL:
                         type: string
                         type: string
                       organizationID:
                       organizationID:
@@ -2505,7 +2532,6 @@ spec:
                         type: string
                         type: string
                     required:
                     required:
                     - auth
                     - auth
-                    - caBundle
                     - organizationID
                     - organizationID
                     - projectID
                     - projectID
                     type: object
                     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
                           Base64 encoded certificate for the bitwarden server sdk. The sdk MUST run with HTTPS to make sure no MITM attack
                           can be performed.
                           can be performed.
                         type: string
                         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:
                       identityURL:
                         type: string
                         type: string
                       organizationID:
                       organizationID:
@@ -2505,7 +2532,6 @@ spec:
                         type: string
                         type: string
                     required:
                     required:
                     - auth
                     - auth
-                    - caBundle
                     - organizationID
                     - organizationID
                     - projectID
                     - projectID
                     type: object
                     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
                             Base64 encoded certificate for the bitwarden server sdk. The sdk MUST run with HTTPS to make sure no MITM attack
                             can be performed.
                             can be performed.
                           type: string
                           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:
                         identityURL:
                           type: string
                           type: string
                         organizationID:
                         organizationID:
@@ -2988,7 +3012,6 @@ spec:
                           type: string
                           type: string
                       required:
                       required:
                         - auth
                         - auth
-                        - caBundle
                         - organizationID
                         - organizationID
                         - projectID
                         - projectID
                       type: object
                       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
                             Base64 encoded certificate for the bitwarden server sdk. The sdk MUST run with HTTPS to make sure no MITM attack
                             can be performed.
                             can be performed.
                           type: string
                           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:
                         identityURL:
                           type: string
                           type: string
                         organizationID:
                         organizationID:
@@ -8750,7 +8797,6 @@ spec:
                           type: string
                           type: string
                       required:
                       required:
                         - auth
                         - auth
-                        - caBundle
                         - organizationID
                         - organizationID
                         - projectID
                         - projectID
                       type: object
                       type: object

+ 16 - 0
docs/api/spec.md

@@ -1328,12 +1328,27 @@ string
 </em>
 </em>
 </td>
 </td>
 <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
 <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>
 can be performed.</p>
 </td>
 </td>
 </tr>
 </tr>
 <tr>
 <tr>
 <td>
 <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>
 <code>organizationID</code></br>
 <em>
 <em>
 string
 string
@@ -1407,6 +1422,7 @@ External Secrets meta/v1.SecretKeySelector
 <p>
 <p>
 (<em>Appears on:</em>
 (<em>Appears on:</em>
 <a href="#external-secrets.io/v1beta1.AkeylessProvider">AkeylessProvider</a>, 
 <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.ConjurProvider">ConjurProvider</a>, 
 <a href="#external-secrets.io/v1beta1.KubernetesServer">KubernetesServer</a>, 
 <a href="#external-secrets.io/v1beta1.KubernetesServer">KubernetesServer</a>, 
 <a href="#external-secrets.io/v1beta1.VaultProvider">VaultProvider</a>)
 <a href="#external-secrets.io/v1beta1.VaultProvider">VaultProvider</a>)

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

@@ -16,6 +16,8 @@ package webhook
 
 
 import (
 import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+
+	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 )
 )
 
 
 type Spec struct {
 type Spec struct {
@@ -55,31 +57,7 @@ type Spec struct {
 
 
 	// The provider for the CA bundle to use to validate webhook server certificate.
 	// The provider for the CA bundle to use to validate webhook server certificate.
 	// +optional
 	// +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 {
 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)
 	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{}
 	client := &http.Client{}
 	if provider.Timeout != nil {
 	if provider.Timeout != nil {
 		client.Timeout = provider.Timeout.Duration
 		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
 		// No need to process ca stuff if it is not there
 		return client, nil
 		return client, nil
 	}
 	}
-	caCertPool, err := w.GetCACertPool(provider)
+	caCertPool, err := w.GetCACertPool(ctx, provider)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -230,37 +230,23 @@ func (w *Webhook) GetHTTPClient(provider *Spec) (*http.Client, error) {
 	return client, nil
 	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()
 	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
 	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.wh.Namespace = ns
 	w.url = provider.URL
 	w.url = provider.URL
 	w.wh.Kube = kclient
 	w.wh.Kube = kclient
-	w.wh.HTTP, err = w.wh.GetHTTPClient(provider)
+	w.wh.HTTP, err = w.wh.GetHTTPClient(ctx, provider)
 	if err != nil {
 	if err != nil {
 		return nil, fmt.Errorf("failed to prepare provider http client: %w", err)
 		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"
 	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
 
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	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/find"
 	"github.com/external-secrets/external-secrets/pkg/utils"
 	"github.com/external-secrets/external-secrets/pkg/utils"
-	"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
 )
 )
 
 
 const (
 const (
@@ -180,7 +178,7 @@ func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnin
 	return nil, nil
 	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{
 	akl := &akeylessBase{
 		kube:      kube,
 		kube:      kube,
 		store:     store,
 		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")
 		return nil, fmt.Errorf("missing Auth in store config")
 	}
 	}
 
 
-	client, err := akl.getAkeylessHTTPClient(spec)
+	client, err := akl.getAkeylessHTTPClient(ctx, spec)
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -406,16 +404,29 @@ func (a *Akeyless) GetSecretMap(ctx context.Context, ref esv1beta1.ExternalSecre
 	return secretData, nil
 	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}
 	client := &http.Client{Timeout: 30 * time.Second}
 	if len(provider.CABundle) == 0 && provider.CAProvider == nil {
 	if len(provider.CABundle) == 0 && provider.CAProvider == nil {
 		return client, 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 {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
+	caCertPool := x509.NewCertPool()
+	ok := caCertPool.AppendCertsFromPEM(cert)
+	if !ok {
+		return nil, fmt.Errorf("failed to append caBundle")
+	}
+
 	tlsConf := &tls.Config{
 	tlsConf := &tls.Config{
 		RootCAs:    caCertPool,
 		RootCAs:    caCertPool,
 		MinVersion: tls.VersionTLS12,
 		MinVersion: tls.VersionTLS12,
@@ -423,93 +434,3 @@ func (a *akeylessBase) getAkeylessHTTPClient(provider *esv1beta1.AkeylessProvide
 	client.Transport = &http.Transport{TLSClientConfig: tlsConf}
 	client.Transport = &http.Transport{TLSClientConfig: tlsConf}
 	return client, nil
 	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
 package akeyless
 
 
 import (
 import (
-	"encoding/base64"
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 	"net/http"
 	"net/http"
@@ -109,12 +108,3 @@ func sendReq(url string) string {
 	body, _ := io.ReadAll(resp.Body)
 	body, _ := io.ReadAll(resp.Body)
 	return string(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"
 	"fmt"
 	"io"
 	"io"
 	"net/http"
 	"net/http"
+
+	"sigs.k8s.io/controller-runtime/pkg/client"
+
+	"github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 )
 )
 
 
 // Defined Header Keys.
 // Defined Header Keys.
@@ -100,18 +104,18 @@ type SdkClient struct {
 	client *http.Client
 	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 {
 	if err != nil {
 		return nil, fmt.Errorf("error creating https client: %w", err)
 		return nil, fmt.Errorf("error creating https client: %w", err)
 	}
 	}
 
 
 	return &SdkClient{
 	return &SdkClient{
-		apiURL:                apiURL,
-		identityURL:           identityURL,
+		apiURL:                provider.APIURL,
+		identityURL:           provider.IdentityURL,
+		bitwardenSdkServerURL: provider.BitwardenServerSDKURL,
 		token:                 token,
 		token:                 token,
-		client:                client,
-		bitwardenSdkServerURL: bitwardenURL,
+		client:                httpsClient,
 	}, nil
 	}, nil
 }
 }
 
 

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

@@ -26,10 +26,6 @@ import (
 	"github.com/external-secrets/external-secrets/pkg/utils"
 	"github.com/external-secrets/external-secrets/pkg/utils"
 )
 )
 
 
-var (
-	errBadCertBundle = "caBundle failed to base64 decode: %w"
-)
-
 const (
 const (
 	// NoteMetadataKey defines the note for the pushed secret.
 	// NoteMetadataKey defines the note for the pushed secret.
 	NoteMetadataKey = "note"
 	NoteMetadataKey = "note"
@@ -249,16 +245,6 @@ func (p *Provider) Close(_ context.Context) error {
 	return nil
 	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) {
 func (p *Provider) findSecretByRef(ctx context.Context, key, projectID string) (*SecretResponse, error) {
 	spec := p.store.GetSpec()
 	spec := p.store.GetSpec()
 	if spec == nil || spec.Provider == nil {
 	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"
 	"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
 
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	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"
 	"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)
 		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,
 		token,
-		bundle,
 	)
 	)
 	if err != nil {
 	if err != nil {
 		return nil, fmt.Errorf("could not create SdkClient: %w", err)
 		return nil, fmt.Errorf("could not create SdkClient: %w", err)
@@ -88,17 +84,49 @@ func (p *Provider) Capabilities() esv1beta1.SecretStoreCapabilities {
 }
 }
 
 
 // ValidateStore validates the store.
 // 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
 	return nil, nil
 }
 }
 
 
 // newHTTPSClient creates a new HTTPS client with the given cert.
 // 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()
 	pool := x509.NewCertPool()
 	ok := pool.AppendCertsFromPEM(cert)
 	ok := pool.AppendCertsFromPEM(cert)
 	if !ok {
 	if !ok {
-		return nil, fmt.Errorf("can't append Conjur SSL cert")
+		return nil, fmt.Errorf("failed to append caBundle")
 	}
 	}
+
 	tr := &http.Transport{
 	tr := &http.Transport{
 		TLSClientConfig: &tls.Config{RootCAs: pool, MinVersion: tls.VersionTLS12},
 		TLSClientConfig: &tls.Config{RootCAs: pool, MinVersion: tls.VersionTLS12},
 	}
 	}

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

@@ -17,7 +17,6 @@ package conjur
 import (
 import (
 	"context"
 	"context"
 	"fmt"
 	"fmt"
-	"strings"
 
 
 	"github.com/cyberark/conjur-api-go/conjurapi"
 	"github.com/cyberark/conjur-api-go/conjurapi"
 	"github.com/cyberark/conjur-api-go/conjurapi/authn"
 	"github.com/cyberark/conjur-api-go/conjurapi/authn"
@@ -26,24 +25,17 @@ import (
 	"sigs.k8s.io/controller-runtime/pkg/client"
 	"sigs.k8s.io/controller-runtime/pkg/client"
 
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	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/provider/conjur/util"
 	"github.com/external-secrets/external-secrets/pkg/utils"
 	"github.com/external-secrets/external-secrets/pkg/utils"
 	"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
 	"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
 )
 )
 
 
 var (
 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"
 	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.
 // Client is a provider for Conjur.
@@ -68,14 +60,20 @@ func (c *Client) GetConjurClient(ctx context.Context) (SecretsClient, error) {
 		return nil, err
 		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 {
 	if getCertErr != nil {
 		return nil, getCertErr
 		return nil, getCertErr
 	}
 	}
 
 
 	config := conjurapi.Config{
 	config := conjurapi.Config{
 		ApplianceURL: prov.URL,
 		ApplianceURL: prov.URL,
-		SSLCert:      cert,
+		SSLCert:      string(cert),
 	}
 	}
 
 
 	if prov.Auth.APIKey != nil {
 	if prov.Auth.APIKey != nil {
@@ -151,69 +149,3 @@ func (c *Client) Validate() (esv1beta1.ValidationResult, error) {
 func (c *Client) Close(_ context.Context) error {
 func (c *Client) Close(_ context.Context) error {
 	return nil
 	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"
 	"fmt"
 
 
 	authenticationv1 "k8s.io/api/authentication/v1"
 	authenticationv1 "k8s.io/api/authentication/v1"
-	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
-	"k8s.io/apimachinery/pkg/types"
 	"k8s.io/client-go/rest"
 	"k8s.io/client-go/rest"
 	"k8s.io/client-go/tools/clientcmd"
 	"k8s.io/client-go/tools/clientcmd"
 
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 	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"
 	"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
 )
 )
 
 
 const (
 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) {
 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)
 		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 {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -92,40 +93,6 @@ func (c *Client) getAuth(ctx context.Context) (*rest.Config, error) {
 	}, nil
 	}, 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) {
 func (c *Client) getClientKeyAndCert(ctx context.Context) ([]byte, []byte, error) {
 	var err error
 	var err error
 	cert, err := c.fetchSecretKey(ctx, c.store.Auth.Cert.ClientCert)
 	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
 	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"
 	"context"
 	"testing"
 	"testing"
 
 
-	"github.com/google/go-cmp/cmp"
+	"github.com/stretchr/testify/assert"
 	corev1 "k8s.io/api/core/v1"
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
 	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
@@ -99,7 +99,7 @@ func TestSetAuth(t *testing.T) {
 			fields: fields{
 			fields: fields{
 				store: &esv1beta1.KubernetesProvider{
 				store: &esv1beta1.KubernetesProvider{
 					Server: esv1beta1.KubernetesServer{
 					Server: esv1beta1.KubernetesServer{
-						CABundle: []byte("1234"),
+						CABundle: []byte(caCert),
 					},
 					},
 				},
 				},
 			},
 			},
@@ -116,7 +116,7 @@ func TestSetAuth(t *testing.T) {
 						Namespace: "default",
 						Namespace: "default",
 					},
 					},
 					Data: map[string][]byte{
 					Data: map[string][]byte{
-						"cert":  []byte("1234"),
+						"cert":  []byte(caCert),
 						"token": []byte("mytoken"),
 						"token": []byte("mytoken"),
 					},
 					},
 				}).Build(),
 				}).Build(),
@@ -144,7 +144,7 @@ func TestSetAuth(t *testing.T) {
 				Host:        "https://my.test.tld",
 				Host:        "https://my.test.tld",
 				BearerToken: "mytoken",
 				BearerToken: "mytoken",
 				TLSClientConfig: rest.TLSClientConfig{
 				TLSClientConfig: rest.TLSClientConfig{
-					CAData: []byte("1234"),
+					CAData: []byte(caCert),
 				},
 				},
 			},
 			},
 			wantErr: false,
 			wantErr: false,
@@ -215,7 +215,7 @@ func TestSetAuth(t *testing.T) {
 				store: &esv1beta1.KubernetesProvider{
 				store: &esv1beta1.KubernetesProvider{
 					Server: esv1beta1.KubernetesServer{
 					Server: esv1beta1.KubernetesServer{
 						URL:      "https://my.test.tld",
 						URL:      "https://my.test.tld",
-						CABundle: []byte("1234"),
+						CABundle: []byte(caCert),
 					},
 					},
 					Auth: esv1beta1.KubernetesAuth{
 					Auth: esv1beta1.KubernetesAuth{
 						Token: &esv1beta1.TokenAuth{
 						Token: &esv1beta1.TokenAuth{
@@ -232,7 +232,7 @@ func TestSetAuth(t *testing.T) {
 				Host:        "https://my.test.tld",
 				Host:        "https://my.test.tld",
 				BearerToken: "mytoken",
 				BearerToken: "mytoken",
 				TLSClientConfig: rest.TLSClientConfig{
 				TLSClientConfig: rest.TLSClientConfig{
-					CAData: []byte("1234"),
+					CAData: []byte(caCert),
 				},
 				},
 			},
 			},
 			wantErr: false,
 			wantErr: false,
@@ -262,7 +262,7 @@ func TestSetAuth(t *testing.T) {
 				store: &esv1beta1.KubernetesProvider{
 				store: &esv1beta1.KubernetesProvider{
 					Server: esv1beta1.KubernetesServer{
 					Server: esv1beta1.KubernetesServer{
 						URL:      "https://my.test.tld",
 						URL:      "https://my.test.tld",
-						CABundle: []byte("1234"),
+						CABundle: []byte(caCert),
 					},
 					},
 					Auth: esv1beta1.KubernetesAuth{
 					Auth: esv1beta1.KubernetesAuth{
 						Token: &esv1beta1.TokenAuth{
 						Token: &esv1beta1.TokenAuth{
@@ -289,7 +289,7 @@ func TestSetAuth(t *testing.T) {
 				Host:        "https://my.test.tld",
 				Host:        "https://my.test.tld",
 				BearerToken: "mytoken",
 				BearerToken: "mytoken",
 				TLSClientConfig: rest.TLSClientConfig{
 				TLSClientConfig: rest.TLSClientConfig{
-					CAData:   []byte("1234"),
+					CAData:   []byte(caCert),
 					CertData: []byte("my-cert"),
 					CertData: []byte("my-cert"),
 					KeyData:  []byte("my-key"),
 					KeyData:  []byte("my-key"),
 				},
 				},
@@ -310,7 +310,7 @@ func TestSetAuth(t *testing.T) {
 				store: &esv1beta1.KubernetesProvider{
 				store: &esv1beta1.KubernetesProvider{
 					Server: esv1beta1.KubernetesServer{
 					Server: esv1beta1.KubernetesServer{
 						URL:      "https://my.test.tld",
 						URL:      "https://my.test.tld",
-						CABundle: []byte("1234"),
+						CABundle: []byte(caCert),
 					},
 					},
 					Auth: esv1beta1.KubernetesAuth{
 					Auth: esv1beta1.KubernetesAuth{
 						ServiceAccount: &v1.ServiceAccountSelector{
 						ServiceAccount: &v1.ServiceAccountSelector{
@@ -324,7 +324,7 @@ func TestSetAuth(t *testing.T) {
 				Host:        "https://my.test.tld",
 				Host:        "https://my.test.tld",
 				BearerToken: "my-sa-token",
 				BearerToken: "my-sa-token",
 				TLSClientConfig: rest.TLSClientConfig{
 				TLSClientConfig: rest.TLSClientConfig{
-					CAData: []byte("1234"),
+					CAData: []byte(caCert),
 				},
 				},
 			},
 			},
 			wantErr: false,
 			wantErr: false,
@@ -342,7 +342,7 @@ func TestSetAuth(t *testing.T) {
 				kubeclientset: utilfake.NewCreateTokenMock().WithToken("my-sa-token"),
 				kubeclientset: utilfake.NewCreateTokenMock().WithToken("my-sa-token"),
 				store: &esv1beta1.KubernetesProvider{
 				store: &esv1beta1.KubernetesProvider{
 					Server: esv1beta1.KubernetesServer{
 					Server: esv1beta1.KubernetesServer{
-						CABundle: []byte("1234"),
+						CABundle: []byte(caCert),
 					},
 					},
 					Auth: esv1beta1.KubernetesAuth{
 					Auth: esv1beta1.KubernetesAuth{
 						ServiceAccount: &v1.ServiceAccountSelector{
 						ServiceAccount: &v1.ServiceAccountSelector{
@@ -399,9 +399,7 @@ func TestSetAuth(t *testing.T) {
 			if (err != nil) != tt.wantErr {
 			if (err != nil) != tt.wantErr {
 				t.Errorf("BaseClient.setAuth() error = %v, wantErr %v", err, 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"
 	"fmt"
 	"strings"
 	"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"
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	"github.com/external-secrets/external-secrets/pkg/constants"
 	"github.com/external-secrets/external-secrets/pkg/constants"

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

@@ -25,13 +25,12 @@ import (
 	"github.com/go-logr/logr"
 	"github.com/go-logr/logr"
 	vault "github.com/hashicorp/vault/api"
 	vault "github.com/hashicorp/vault/api"
 	corev1 "k8s.io/api/core/v1"
 	corev1 "k8s.io/api/core/v1"
-	"k8s.io/apimachinery/pkg/types"
 	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
 	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
 	kclient "sigs.k8s.io/controller-runtime/pkg/client"
 	kclient "sigs.k8s.io/controller-runtime/pkg/client"
 
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	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/provider/vault/util"
+	"github.com/external-secrets/external-secrets/pkg/utils"
 	"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
 	"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 {
 	if len(c.store.CABundle) != 0 || c.store.CAProvider != nil {
 		caCertPool := x509.NewCertPool()
 		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 {
 		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
 	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 {
 func (c *client) Close(ctx context.Context) error {
 	// Revoke the token if we have one set, it wasn't sourced from a TokenSecretRef,
 	// Revoke the token if we have one set, it wasn't sourced from a TokenSecretRef,
 	// and token caching isn't enabled
 	// and token caching isn't enabled

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

@@ -43,13 +43,11 @@ var (
 )
 )
 
 
 const (
 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 {
 type Provider struct {

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

@@ -306,7 +306,7 @@ MIIFkTCCA3mgAwIBAgIUBEUg3m/WqAsWHG4Q/II3IePFfuowDQYJKoZIhvcNAQELBQAwWDELMAkGA1UE
 				}),
 				}),
 			},
 			},
 			want: want{
 			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": {
 		"VaultAuthFormatError": {
@@ -419,7 +419,7 @@ MIIFkTCCA3mgAwIBAgIUBEUg3m/WqAsWHG4Q/II3IePFfuowDQYJKoZIhvcNAQELBQAwWDELMAkGA1UE
 				newClientFunc: fake.ClientWithLoginMock,
 				newClientFunc: fake.ClientWithLoginMock,
 			},
 			},
 			want: want{
 			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": {
 		"SuccessfulVaultStoreWithIamAuthSecret": {
@@ -491,7 +491,7 @@ MIIFkTCCA3mgAwIBAgIUBEUg3m/WqAsWHG4Q/II3IePFfuowDQYJKoZIhvcNAQELBQAwWDELMAkGA1UE
 				newClientFunc: fake.ClientWithLoginMock,
 				newClientFunc: fake.ClientWithLoginMock,
 			},
 			},
 			want: want{
 			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": {
 		"GetCertificateFormatError": {
@@ -506,7 +506,7 @@ MIIFkTCCA3mgAwIBAgIUBEUg3m/WqAsWHG4Q/II3IePFfuowDQYJKoZIhvcNAQELBQAwWDELMAkGA1UE
 					},
 					},
 					Data: map[string][]byte{
 					Data: map[string][]byte{
 						tlsKey: secretClientKey,
 						tlsKey: secretClientKey,
-						tlsCrt: []byte("cert with mistak"),
+						tlsCrt: []byte("cert with mistake"),
 					},
 					},
 				}).Build(),
 				}).Build(),
 				newClientFunc: fake.ClientWithLoginMock,
 				newClientFunc: fake.ClientWithLoginMock,

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

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

+ 127 - 1
pkg/utils/utils.go

@@ -16,9 +16,12 @@ package utils
 
 
 import (
 import (
 	"bytes"
 	"bytes"
+	"context"
 	"crypto/md5" //nolint:gosec
 	"crypto/md5" //nolint:gosec
+	"crypto/x509"
 	"encoding/base64"
 	"encoding/base64"
 	"encoding/json"
 	"encoding/json"
+	"encoding/pem"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
 	"net"
 	"net"
@@ -31,12 +34,15 @@ import (
 	"time"
 	"time"
 	"unicode"
 	"unicode"
 
 
+	corev1 "k8s.io/api/core/v1"
 	apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/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"
 	esv1alpha1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1alpha1"
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
 	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/template/v2"
+	"github.com/external-secrets/external-secrets/pkg/utils/resolvers"
 )
 )
 
 
 const (
 const (
@@ -139,7 +145,7 @@ func transform(val string, data map[string][]byte) ([]byte, error) {
 	return buf.Bytes(), nil
 	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) {
 func DecodeMap(strategy esv1beta1.ExternalSecretDecodingStrategy, in map[string][]byte) (map[string][]byte, error) {
 	out := make(map[string][]byte, len(in))
 	out := make(map[string][]byte, len(in))
 	for k, v := range in {
 	for k, v := range in {
@@ -515,3 +521,123 @@ func CompareStringAndByteSlices(valueString *string, valueByte []byte) bool {
 
 
 	return bytes.Equal(valueByte, []byte(*valueString))
 	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
+}