1
0
Fork 0
mirror of https://github.com/external-secrets/external-secrets.git synced 2024-12-14 11:57:59 +00:00

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>
This commit is contained in:
Gergely Brautigam 2024-08-16 12:32:35 +02:00 committed by GitHub
parent 098d03792d
commit 82d419e2ee
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
24 changed files with 418 additions and 461 deletions

View file

@ -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.

View file

@ -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)
}

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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>)

View file

@ -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 {

View file

@ -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
}
ok := caCertPool.AppendCertsFromPEM(ca)
if !ok {
return nil, fmt.Errorf("failed to append cabundle")
}
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")
}
}
return caCertPool, nil
}

View file

@ -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)
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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 {

View file

@ -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},
}

View file

@ -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
}

View file

@ -458,8 +458,21 @@ func TestGetCA(t *testing.T) {
want want
}
certData := "mycertdata"
certDataEncoded := "bXljZXJ0ZGF0YQo="
certData := `-----BEGIN CERTIFICATE-----
MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw
CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp
Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2
MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG
ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS
7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp
0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS
B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49
BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ
LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4
DXZDjC5Ty3zfDBeWUA==
-----END CERTIFICATE-----`
certDataEncoded := "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNHVENDQVorZ0F3SUJBZ0lRQ2VDVFphejMyY2k1UGh3TEJDb3U4ekFLQmdncWhrak9QUVFEQXpCT01Rc3cKQ1FZRFZRUUdFd0pWVXpFWE1CVUdBMVVFQ2hNT1JHbG5hVU5sY25Rc0lFbHVZeTR4SmpBa0JnTlZCQU1USFVScApaMmxEWlhKMElGUk1VeUJGUTBNZ1VETTROQ0JTYjI5MElFYzFNQjRYRFRJeE1ERXhOVEF3TURBd01Gb1hEVFEyCk1ERXhOREl6TlRrMU9Wb3dUakVMTUFrR0ExVUVCaE1DVlZNeEZ6QVZCZ05WQkFvVERrUnBaMmxEWlhKMExDQkoKYm1NdU1TWXdKQVlEVlFRREV4MUVhV2RwUTJWeWRDQlVURk1nUlVORElGQXpPRFFnVW05dmRDQkhOVEIyTUJBRwpCeXFHU000OUFnRUdCU3VCQkFBaUEySUFCTUZFb2M4UmwxQ2EzaU9DTlFmTjBNc1luZEx4ZjNjMVR6dmRsSEpTCjdjSTcrT3o2ZTJ0WUlPeVpyc244YUxOMXVkc0o3TWdUOVU3R0NoMW1NRXk3SDBjS1BHRVFRaWw4cFFnTzRDTHAKMHpWb3pwdGpuNFMxbVUxWW9JNzFWT2VWeWFOQ01FQXdIUVlEVlIwT0JCWUVGTUZSUlZCWnF6N25MRnI2SUNJUwpCNENJZkJGcU1BNEdBMVVkRHdFQi93UUVBd0lCaGpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUFvR0NDcUdTTTQ5CkJBTURBMmdBTUdVQ01RQ0phbzFINSt6OGJsVUQyV2RzSms2RHh2M0oreXNUdkxkNmpMUmwwbWxwWXhOak95WlEKTGdHaGVRYVJuVWkvd3I0Q01FZkRGWHV4b0pHWlNaT29QSHpvUmdhTExQSXhBSlNkWXNpSnZSbUVGT21sK3dHNApEWFpEakM1VHkzemZEQmVXVUE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t"
cases := map[string]testCase{
"UseCABundleSuccess": {

View file

@ -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
}

View file

@ -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)
})
}
}

View file

@ -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"

View file

@ -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"))
}
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 && c.storeKind == esv1beta1.ClusterSecretStoreKind && c.store.CAProvider.Namespace == nil {
return nil, errors.New(errCANamespace)
}
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

View file

@ -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 {

View file

@ -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,

View file

@ -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)

View file

@ -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
}