diff --git a/apis/externalsecrets/v1beta1/secretstore_conjur_types.go b/apis/externalsecrets/v1beta1/secretstore_conjur_types.go index f0043da23..517552adb 100644 --- a/apis/externalsecrets/v1beta1/secretstore_conjur_types.go +++ b/apis/externalsecrets/v1beta1/secretstore_conjur_types.go @@ -27,12 +27,12 @@ type ConjurProvider struct { type ConjurAuth struct { // +optional - Apikey *ConjurApikey `json:"apikey,omitempty"` + APIKey *ConjurAPIKey `json:"apikey,omitempty"` // +optional Jwt *ConjurJWT `json:"jwt,omitempty"` } -type ConjurApikey struct { +type ConjurAPIKey struct { Account string `json:"account"` UserRef *esmeta.SecretKeySelector `json:"userRef"` APIKeyRef *esmeta.SecretKeySelector `json:"apiKeyRef"` diff --git a/apis/externalsecrets/v1beta1/zz_generated.deepcopy.go b/apis/externalsecrets/v1beta1/zz_generated.deepcopy.go index 2cbaae24a..87abd5d5c 100644 --- a/apis/externalsecrets/v1beta1/zz_generated.deepcopy.go +++ b/apis/externalsecrets/v1beta1/zz_generated.deepcopy.go @@ -654,7 +654,7 @@ func (in *ClusterSecretStoreList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ConjurApikey) DeepCopyInto(out *ConjurApikey) { +func (in *ConjurAPIKey) DeepCopyInto(out *ConjurAPIKey) { *out = *in if in.UserRef != nil { in, out := &in.UserRef, &out.UserRef @@ -668,12 +668,12 @@ func (in *ConjurApikey) DeepCopyInto(out *ConjurApikey) { } } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConjurApikey. -func (in *ConjurApikey) DeepCopy() *ConjurApikey { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConjurAPIKey. +func (in *ConjurAPIKey) DeepCopy() *ConjurAPIKey { if in == nil { return nil } - out := new(ConjurApikey) + out := new(ConjurAPIKey) in.DeepCopyInto(out) return out } @@ -681,9 +681,9 @@ func (in *ConjurApikey) DeepCopy() *ConjurApikey { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ConjurAuth) DeepCopyInto(out *ConjurAuth) { *out = *in - if in.Apikey != nil { - in, out := &in.Apikey, &out.Apikey - *out = new(ConjurApikey) + if in.APIKey != nil { + in, out := &in.APIKey, &out.APIKey + *out = new(ConjurAPIKey) (*in).DeepCopyInto(*out) } if in.Jwt != nil { diff --git a/docs/api/spec.md b/docs/api/spec.md index 9b9350a71..479819bbb 100644 --- a/docs/api/spec.md +++ b/docs/api/spec.md @@ -1691,7 +1691,7 @@ Kubernetes meta/v1.LabelSelector -

ConjurApikey +

ConjurAPIKey

(Appears on: @@ -1763,8 +1763,8 @@ External Secrets meta/v1.SecretKeySelector apikey
- -ConjurApikey + +ConjurAPIKey diff --git a/pkg/generator/gcr/gcr.go b/pkg/generator/gcr/gcr.go index 770646b03..8264b84e1 100644 --- a/pkg/generator/gcr/gcr.go +++ b/pkg/generator/gcr/gcr.go @@ -27,6 +27,7 @@ import ( esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" genv1alpha1 "github.com/external-secrets/external-secrets/apis/generators/v1alpha1" "github.com/external-secrets/external-secrets/pkg/provider/gcp/secretmanager" + "github.com/external-secrets/external-secrets/pkg/utils/resolvers" ) type Generator struct{} @@ -65,7 +66,7 @@ func (g *Generator) generate( ts, err := tokenSource(ctx, esv1beta1.GCPSMAuth{ SecretRef: (*esv1beta1.GCPSMAuthSecretRef)(res.Spec.Auth.SecretRef), WorkloadIdentity: (*esv1beta1.GCPWorkloadIdentity)(res.Spec.Auth.WorkloadIdentity), - }, res.Spec.ProjectID, false, kube, namespace) + }, res.Spec.ProjectID, resolvers.EmptyStoreKind, kube, namespace) if err != nil { return nil, err } @@ -81,7 +82,7 @@ func (g *Generator) generate( }, nil } -type tokenSourceFunc func(ctx context.Context, auth esv1beta1.GCPSMAuth, projectID string, isClusterKind bool, kube client.Client, namespace string) (oauth2.TokenSource, error) +type tokenSourceFunc func(ctx context.Context, auth esv1beta1.GCPSMAuth, projectID string, storeKind string, kube client.Client, namespace string) (oauth2.TokenSource, error) func parseSpec(data []byte) (*genv1alpha1.GCRAccessToken, error) { var spec genv1alpha1.GCRAccessToken diff --git a/pkg/generator/gcr/gcr_test.go b/pkg/generator/gcr/gcr_test.go index 5a937f8e7..e7aeb786d 100644 --- a/pkg/generator/gcr/gcr_test.go +++ b/pkg/generator/gcr/gcr_test.go @@ -65,7 +65,7 @@ func TestGenerate(t *testing.T) { "foo": []byte("bar"), }, }).Build(), - fakeTokenSource: func(ctx context.Context, auth v1beta1.GCPSMAuth, projectID string, isClusterKind bool, kube client.Client, namespace string) (oauth2.TokenSource, error) { + fakeTokenSource: func(ctx context.Context, auth v1beta1.GCPSMAuth, projectID string, storeKind string, kube client.Client, namespace string) (oauth2.TokenSource, error) { return oauth2.StaticTokenSource(&oauth2.Token{ AccessToken: "1234", Expiry: time.Unix(5555, 0), diff --git a/pkg/provider/akeyless/akeyless.go b/pkg/provider/akeyless/akeyless.go index 589c8f2d3..48d5b8177 100644 --- a/pkg/provider/akeyless/akeyless.go +++ b/pkg/provider/akeyless/akeyless.go @@ -39,6 +39,7 @@ import ( 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 ( @@ -426,12 +427,14 @@ func (a *akeylessBase) getCACertPool(provider *esv1beta1.AkeylessProvider) (*x50 } ok := caCertPool.AppendCertsFromPEM(pem) if !ok { - return nil, fmt.Errorf("failed to append cabundle") + 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 && + a.storeKind == esv1beta1.ClusterSecretStoreKind && + provider.CAProvider.Namespace == nil { + return nil, fmt.Errorf("missing namespace on caProvider secret") } if provider.CAProvider != nil { @@ -444,7 +447,7 @@ func (a *akeylessBase) getCACertPool(provider *esv1beta1.AkeylessProvider) (*x50 case esv1beta1.CAProviderTypeConfigMap: cert, err = a.getCertFromConfigMap(provider) default: - err = fmt.Errorf("unknown caprovider type: %s", provider.CAProvider.Type) + err = fmt.Errorf("unknown CAProvider type: %s", provider.CAProvider.Type) } if err != nil { @@ -456,7 +459,7 @@ func (a *akeylessBase) getCACertPool(provider *esv1beta1.AkeylessProvider) (*x50 } ok := caCertPool.AppendCertsFromPEM(pem) if !ok { - return nil, fmt.Errorf("failed to append cabundle") + return nil, fmt.Errorf("failed to append caBundle") } } return caCertPool, nil @@ -473,12 +476,12 @@ func (a *akeylessBase) getCertFromSecret(provider *esv1beta1.AkeylessProvider) ( } ctx := context.Background() - res, err := a.secretKeyRef(ctx, &secretRef) + cert, err := resolvers.SecretKeyRef(ctx, a.kube, a.storeKind, a.namespace, &secretRef) if err != nil { return nil, err } - return []byte(res), nil + return []byte(cert), nil } func (a *akeylessBase) getCertFromConfigMap(provider *esv1beta1.AkeylessProvider) ([]byte, error) { @@ -494,12 +497,12 @@ func (a *akeylessBase) getCertFromConfigMap(provider *esv1beta1.AkeylessProvider 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) + 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 nil, fmt.Errorf("failed to get caProvider configMap %s -> %s", objKey.Name, provider.CAProvider.Key) } return []byte(val), nil diff --git a/pkg/provider/akeyless/akeyless_api.go b/pkg/provider/akeyless/akeyless_api.go index 66d610715..080cdce41 100644 --- a/pkg/provider/akeyless/akeyless_api.go +++ b/pkg/provider/akeyless/akeyless_api.go @@ -35,6 +35,7 @@ import ( 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/resolvers" ) var apiErr akeyless.GenericOpenAPIError @@ -335,7 +336,7 @@ func (a *akeylessBase) getK8SServiceAccountJWT(ctx context.Context, kubernetesAu tokenRef = kubernetesAuth.SecretRef.DeepCopy() tokenRef.Key = "token" } - jwt, err := a.secretKeyRef(ctx, tokenRef) + jwt, err := resolvers.SecretKeyRef(ctx, a.kube, a.storeKind, a.namespace, tokenRef) if err != nil { return "", err } @@ -363,7 +364,7 @@ func (a *akeylessBase) getJWTFromServiceAccount(ctx context.Context, serviceAcco return "", fmt.Errorf(errGetKubeSASecrets, ref.Name) } for _, tokenRef := range serviceAccount.Secrets { - retval, err := a.secretKeyRef(ctx, &esmeta.SecretKeySelector{ + token, err := resolvers.SecretKeyRef(ctx, a.kube, a.storeKind, a.namespace, &esmeta.SecretKeySelector{ Name: tokenRef.Name, Namespace: &ref.Namespace, Key: "token", @@ -372,36 +373,11 @@ func (a *akeylessBase) getJWTFromServiceAccount(ctx context.Context, serviceAcco continue } - return retval, nil + return token, nil } return "", fmt.Errorf(errGetKubeSANoToken, ref.Name) } -func (a *akeylessBase) secretKeyRef(ctx context.Context, secretRef *esmeta.SecretKeySelector) (string, error) { - secret := &corev1.Secret{} - ref := types.NamespacedName{ - Namespace: a.namespace, - Name: secretRef.Name, - } - if (a.storeKind == esv1beta1.ClusterSecretStoreKind) && - (secretRef.Namespace != nil) { - ref.Namespace = *secretRef.Namespace - } - err := a.kube.Get(ctx, ref, secret) - if err != nil { - return "", fmt.Errorf(errGetKubeSecret, ref.Name, err) - } - - keyBytes, ok := secret.Data[secretRef.Key] - if !ok { - return "", fmt.Errorf(errSecretKeyFmt, secretRef.Key) - } - - value := string(keyBytes) - valueStr := strings.TrimSpace(value) - return valueStr, nil -} - func (a *akeylessBase) getJWTfromServiceAccountToken(ctx context.Context, serviceAccountRef esmeta.ServiceAccountSelector, additionalAud []string, expirationSeconds int64) (string, error) { audiences := serviceAccountRef.Audiences if len(additionalAud) > 0 { diff --git a/pkg/provider/akeyless/auth.go b/pkg/provider/akeyless/auth.go index 420ea1457..fac21bc9e 100644 --- a/pkg/provider/akeyless/auth.go +++ b/pkg/provider/akeyless/auth.go @@ -18,19 +18,15 @@ import ( "context" "fmt" - v1 "k8s.io/api/core/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - - esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" + "github.com/external-secrets/external-secrets/pkg/utils/resolvers" ) const ( - errInvalidClusterStoreMissingAKIDNamespace = "invalid ClusterSecretStore: missing Akeyless AccessID Namespace" - errInvalidClusterStoreMissingSAKNamespace = "invalid ClusterSecretStore: missing Akeyless AccessType Namespace" - errFetchAKIDSecret = "could not fetch accessID secret: %w" - errFetchSAKSecret = "could not fetch AccessType secret: %w" - errMissingSAK = "missing SecretAccessKey" - errMissingAKID = "missing AccessKeyID" + errFetchAccessIDSecret = "could not fetch accessID secret: %w" + errFetchAccessTypeSecret = "could not fetch AccessType secret: %w" + errFetchAccessTypeParamSecret = "could not fetch AccessTypeParam secret: %w" + errMissingSAK = "missing SecretAccessKey" + errMissingAKID = "missing AccessKeyID" ) func (a *akeylessBase) TokenFromSecretRef(ctx context.Context) (string, error) { @@ -44,58 +40,36 @@ func (a *akeylessBase) TokenFromSecretRef(ctx context.Context) (string, error) { return a.GetToken(auth.AccessID, "k8s", auth.K8sConfName, auth) } - ke := client.ObjectKey{ - Name: prov.Auth.SecretRef.AccessID.Name, - Namespace: a.namespace, // default to ExternalSecret namespace - } - // only ClusterStore is allowed to set namespace (and then it's required) - if a.store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind { - if prov.Auth.SecretRef.AccessID.Namespace == nil { - return "", fmt.Errorf(errInvalidClusterStoreMissingAKIDNamespace) - } - ke.Namespace = *prov.Auth.SecretRef.AccessID.Namespace - } - accessIDSecret := v1.Secret{} - err = a.kube.Get(ctx, ke, &accessIDSecret) + accessID, err := resolvers.SecretKeyRef( + ctx, + a.kube, + a.storeKind, + a.namespace, + &prov.Auth.SecretRef.AccessID, + ) if err != nil { - return "", fmt.Errorf(errFetchAKIDSecret, err) + return "", fmt.Errorf(errFetchAccessIDSecret, err) } - ke = client.ObjectKey{ - Name: prov.Auth.SecretRef.AccessType.Name, - Namespace: a.namespace, // default to ExternalSecret namespace - } - // only ClusterStore is allowed to set namespace (and then it's required) - if a.store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind { - if prov.Auth.SecretRef.AccessType.Namespace == nil { - return "", fmt.Errorf(errInvalidClusterStoreMissingSAKNamespace) - } - ke.Namespace = *prov.Auth.SecretRef.AccessType.Namespace - } - accessTypeSecret := v1.Secret{} - err = a.kube.Get(ctx, ke, &accessTypeSecret) + accessType, err := resolvers.SecretKeyRef( + ctx, + a.kube, + a.storeKind, + a.namespace, + &prov.Auth.SecretRef.AccessType, + ) if err != nil { - return "", fmt.Errorf(errFetchSAKSecret, err) + return "", fmt.Errorf(errFetchAccessTypeSecret, err) } - - ke = client.ObjectKey{ - Name: prov.Auth.SecretRef.AccessTypeParam.Name, - Namespace: a.namespace, // default to ExternalSecret namespace - } - // only ClusterStore is allowed to set namespace (and then it's required) - if a.store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind { - if prov.Auth.SecretRef.AccessType.Namespace == nil { - return "", fmt.Errorf(errInvalidClusterStoreMissingSAKNamespace) - } - ke.Namespace = *prov.Auth.SecretRef.AccessType.Namespace - } - accessTypeParamSecret := v1.Secret{} - err = a.kube.Get(ctx, ke, &accessTypeParamSecret) + accessTypeParam, err := resolvers.SecretKeyRef( + ctx, + a.kube, + a.storeKind, + a.namespace, + &prov.Auth.SecretRef.AccessTypeParam, + ) if err != nil { - return "", fmt.Errorf(errFetchSAKSecret, err) + return "", fmt.Errorf(errFetchAccessTypeParamSecret, err) } - accessID := string(accessIDSecret.Data[prov.Auth.SecretRef.AccessID.Key]) - accessType := string(accessTypeSecret.Data[prov.Auth.SecretRef.AccessType.Key]) - accessTypeParam := string(accessTypeSecret.Data[prov.Auth.SecretRef.AccessTypeParam.Key]) if accessID == "" { return "", fmt.Errorf(errMissingSAK) diff --git a/pkg/provider/alibaba/kms.go b/pkg/provider/alibaba/kms.go index 7c944e0cc..cdf059e98 100644 --- a/pkg/provider/alibaba/kms.go +++ b/pkg/provider/alibaba/kms.go @@ -26,22 +26,18 @@ import ( "github.com/avast/retry-go/v4" "github.com/tidwall/gjson" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" kclient "sigs.k8s.io/controller-runtime/pkg/client" 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" ) const ( - errAlibabaClient = "cannot setup new Alibaba client: %w" - errAlibabaCredSecretName = "invalid Alibaba SecretStore resource: missing Alibaba APIKey" - errUninitalizedAlibabaProvider = "provider Alibaba is not initialized" - errInvalidClusterStoreMissingAKIDNamespace = "invalid ClusterStore, missing AccessKeyID namespace" - errInvalidClusterStoreMissingSKNamespace = "invalid ClusterStore, missing namespace" - errFetchAKIDSecret = "could not fetch AccessKeyID secret: %w" - errMissingSAK = "missing AccessSecretKey" - errMissingAKID = "missing AccessKeyID" + errAlibabaClient = "cannot setup new Alibaba client: %w" + errUninitalizedAlibabaProvider = "provider Alibaba is not initialized" + errFetchAccessKeyID = "could not fetch AccessKeyID secret: %w" + errFetchAccessKeySecret = "could not fetch AccessKeySecret secret: %w" ) // https://github.com/external-secrets/external-secrets/issues/644 @@ -222,54 +218,17 @@ func newAccessKeyAuth(ctx context.Context, kube kclient.Client, store esv1beta1. storeSpec := store.GetSpec() alibabaSpec := storeSpec.Provider.Alibaba storeKind := store.GetObjectKind().GroupVersionKind().Kind - - credentialsSecret := &corev1.Secret{} - credentialsSecretName := alibabaSpec.Auth.SecretRef.AccessKeyID.Name - if credentialsSecretName == "" { - return nil, fmt.Errorf(errAlibabaCredSecretName) - } - objectKey := types.NamespacedName{ - Name: credentialsSecretName, - Namespace: namespace, - } - - // only ClusterStore is allowed to set namespace (and then it's required) - if storeKind == esv1beta1.ClusterSecretStoreKind { - if alibabaSpec.Auth.SecretRef.AccessKeyID.Namespace == nil { - return nil, fmt.Errorf(errInvalidClusterStoreMissingAKIDNamespace) - } - objectKey.Namespace = *alibabaSpec.Auth.SecretRef.AccessKeyID.Namespace - } - - err := kube.Get(ctx, objectKey, credentialsSecret) + accessKeyID, err := resolvers.SecretKeyRef(ctx, kube, storeKind, namespace, &alibabaSpec.Auth.SecretRef.AccessKeyID) if err != nil { - return nil, fmt.Errorf(errFetchAKIDSecret, err) + return nil, fmt.Errorf(errFetchAccessKeyID, err) } - - objectKey = types.NamespacedName{ - Name: alibabaSpec.Auth.SecretRef.AccessKeySecret.Name, - Namespace: namespace, + accessKeySecret, err := resolvers.SecretKeyRef(ctx, kube, storeKind, namespace, &alibabaSpec.Auth.SecretRef.AccessKeySecret) + if err != nil { + return nil, fmt.Errorf(errFetchAccessKeySecret, err) } - if storeKind == esv1beta1.ClusterSecretStoreKind { - if alibabaSpec.Auth.SecretRef.AccessKeySecret.Namespace == nil { - return nil, fmt.Errorf(errInvalidClusterStoreMissingSKNamespace) - } - objectKey.Namespace = *alibabaSpec.Auth.SecretRef.AccessKeySecret.Namespace - } - - accessKeyID := credentialsSecret.Data[alibabaSpec.Auth.SecretRef.AccessKeyID.Key] - if (accessKeyID == nil) || (len(accessKeyID) == 0) { - return nil, fmt.Errorf(errMissingAKID) - } - - accessKeySecret := credentialsSecret.Data[alibabaSpec.Auth.SecretRef.AccessKeySecret.Key] - if (accessKeySecret == nil) || (len(accessKeySecret) == 0) { - return nil, fmt.Errorf(errMissingSAK) - } - credentialConfig := &credential.Config{ - AccessKeyId: utils.Ptr(string(accessKeyID)), - AccessKeySecret: utils.Ptr(string(accessKeySecret)), + AccessKeyId: utils.Ptr(accessKeyID), + AccessKeySecret: utils.Ptr(accessKeySecret), Type: utils.Ptr("access_key"), ConnectTimeout: utils.Ptr(30), Timeout: utils.Ptr(60), diff --git a/pkg/provider/aws/auth/auth.go b/pkg/provider/aws/auth/auth.go index 85c448270..83e66770d 100644 --- a/pkg/provider/aws/auth/auth.go +++ b/pkg/provider/aws/auth/auth.go @@ -38,6 +38,7 @@ import ( "github.com/external-secrets/external-secrets/pkg/cache" "github.com/external-secrets/external-secrets/pkg/feature" "github.com/external-secrets/external-secrets/pkg/provider/aws/util" + "github.com/external-secrets/external-secrets/pkg/utils/resolvers" ) // Config contains configuration to create a new AWS provider. @@ -58,13 +59,9 @@ const ( audienceAnnotation = "eks.amazonaws.com/audience" defaultTokenAudience = "sts.amazonaws.com" - errInvalidClusterStoreMissingAKIDNamespace = "invalid ClusterSecretStore: missing AWS AccessKeyID Namespace" - errInvalidClusterStoreMissingSAKNamespace = "invalid ClusterSecretStore: missing AWS SecretAccessKey Namespace" - errFetchAKIDSecret = "could not fetch accessKeyID secret: %w" - errFetchSAKSecret = "could not fetch SecretAccessKey secret: %w" - errFetchSTSecret = "could not fetch SessionToken secret: %w" - errMissingSAK = "missing SecretAccessKey" - errMissingAKID = "missing AccessKeyID" + errFetchAKIDSecret = "could not fetch accessKeyID secret: %w" + errFetchSAKSecret = "could not fetch SecretAccessKey secret: %w" + errFetchSTSecret = "could not fetch SessionToken secret: %w" ) func init() { @@ -98,11 +95,11 @@ func New(ctx context.Context, store esv1beta1.GenericStore, kube client.Client, } } - // use credentials from sercretRef + // use credentials from secretRef secretRef := prov.Auth.SecretRef if secretRef != nil { log.V(1).Info("using credentials from secretRef") - creds, err = credsFromSecretRef(ctx, prov.Auth, isClusterKind, kube, namespace) + creds, err = credsFromSecretRef(ctx, prov.Auth, store.GetKind(), kube, namespace) if err != nil { return nil, err } @@ -176,11 +173,11 @@ func NewGeneratorSession(ctx context.Context, auth esv1beta1.AWSAuth, role, regi } } - // use credentials from sercretRef + // use credentials from secretRef secretRef := auth.SecretRef if secretRef != nil { log.V(1).Info("using credentials from secretRef") - creds, err = credsFromSecretRef(ctx, auth, false, kube, namespace) + creds, err = credsFromSecretRef(ctx, auth, "", kube, namespace) if err != nil { return nil, err } @@ -211,55 +208,22 @@ func NewGeneratorSession(ctx context.Context, auth esv1beta1.AWSAuth, role, regi // construct a aws.Credentials object // The namespace of the external secret is used if the ClusterSecretStore does not specify a namespace (referentAuth) // If the ClusterSecretStore defines a namespace it will take precedence. -func credsFromSecretRef(ctx context.Context, auth esv1beta1.AWSAuth, isClusterKind bool, kube client.Client, namespace string) (*credentials.Credentials, error) { - ke := client.ObjectKey{ - Name: auth.SecretRef.AccessKeyID.Name, - Namespace: namespace, - } - if isClusterKind && auth.SecretRef.AccessKeyID.Namespace != nil { - ke.Namespace = *auth.SecretRef.AccessKeyID.Namespace - } - akSecret := v1.Secret{} - err := kube.Get(ctx, ke, &akSecret) - if err != nil { - return nil, fmt.Errorf(errFetchAKIDSecret, err) - } - ke = client.ObjectKey{ - Name: auth.SecretRef.SecretAccessKey.Name, - Namespace: namespace, - } - if isClusterKind && auth.SecretRef.SecretAccessKey.Namespace != nil { - ke.Namespace = *auth.SecretRef.SecretAccessKey.Namespace - } - sakSecret := v1.Secret{} - err = kube.Get(ctx, ke, &sakSecret) +func credsFromSecretRef(ctx context.Context, auth esv1beta1.AWSAuth, storeKind string, kube client.Client, namespace string) (*credentials.Credentials, error) { + sak, err := resolvers.SecretKeyRef(ctx, kube, storeKind, namespace, &auth.SecretRef.SecretAccessKey) if err != nil { return nil, fmt.Errorf(errFetchSAKSecret, err) } - sak := string(sakSecret.Data[auth.SecretRef.SecretAccessKey.Key]) - aks := string(akSecret.Data[auth.SecretRef.AccessKeyID.Key]) - if sak == "" { - return nil, fmt.Errorf(errMissingSAK) - } - if aks == "" { - return nil, fmt.Errorf(errMissingAKID) + aks, err := resolvers.SecretKeyRef(ctx, kube, storeKind, namespace, &auth.SecretRef.AccessKeyID) + if err != nil { + return nil, fmt.Errorf(errFetchAKIDSecret, err) } var sessionToken string if auth.SecretRef.SessionToken != nil { - ke = client.ObjectKey{ - Name: auth.SecretRef.SessionToken.Name, - Namespace: namespace, - } - if isClusterKind && auth.SecretRef.SessionToken.Namespace != nil { - ke.Namespace = *auth.SecretRef.SessionToken.Namespace - } - stSecret := v1.Secret{} - err = kube.Get(ctx, ke, &stSecret) + sessionToken, err = resolvers.SecretKeyRef(ctx, kube, storeKind, namespace, auth.SecretRef.SessionToken) if err != nil { return nil, fmt.Errorf(errFetchSTSecret, err) } - sessionToken = string(stSecret.Data[auth.SecretRef.SessionToken.Key]) } return credentials.NewStaticCredentials(aks, sak, sessionToken), err diff --git a/pkg/provider/aws/auth/auth_test.go b/pkg/provider/aws/auth/auth_test.go index d61694dd6..9453248aa 100644 --- a/pkg/provider/aws/auth/auth_test.go +++ b/pkg/provider/aws/auth/auth_test.go @@ -224,7 +224,7 @@ func TestNewSession(t *testing.T) { Data: map[string][]byte{}, }, }, - expectErr: "missing SecretAccessKey", + expectErr: "could not fetch SecretAccessKey secret: cannot find secret data for key: \"two\"", }, { name: "should not be able to access secrets from different namespace", diff --git a/pkg/provider/azure/keyvault/keyvault.go b/pkg/provider/azure/keyvault/keyvault.go index d66b48c18..09cae40d2 100644 --- a/pkg/provider/azure/keyvault/keyvault.go +++ b/pkg/provider/azure/keyvault/keyvault.go @@ -48,10 +48,10 @@ import ( ctrlcfg "sigs.k8s.io/controller-runtime/pkg/client/config" esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" - smmeta "github.com/external-secrets/external-secrets/apis/meta/v1" "github.com/external-secrets/external-secrets/pkg/constants" "github.com/external-secrets/external-secrets/pkg/metrics" "github.com/external-secrets/external-secrets/pkg/utils" + "github.com/external-secrets/external-secrets/pkg/utils/resolvers" ) const ( @@ -886,46 +886,28 @@ func (a *Azure) authorizerForServicePrincipal(ctx context.Context) (autorest.Aut if a.provider.AuthSecretRef.ClientID == nil || a.provider.AuthSecretRef.ClientSecret == nil { return nil, fmt.Errorf(errMissingClientIDSecret) } - clusterScoped := false - if a.store.GetKind() == esv1beta1.ClusterSecretStoreKind { - clusterScoped = true - } - cid, err := a.secretKeyRef(ctx, a.namespace, *a.provider.AuthSecretRef.ClientID, clusterScoped) + clientID, err := resolvers.SecretKeyRef( + ctx, + a.crClient, + a.store.GetKind(), + a.namespace, a.provider.AuthSecretRef.ClientID) if err != nil { return nil, err } - csec, err := a.secretKeyRef(ctx, a.namespace, *a.provider.AuthSecretRef.ClientSecret, clusterScoped) + clientSecret, err := resolvers.SecretKeyRef( + ctx, + a.crClient, + a.store.GetKind(), + a.namespace, a.provider.AuthSecretRef.ClientSecret) if err != nil { return nil, err } - clientCredentialsConfig := kvauth.NewClientCredentialsConfig(cid, csec, *a.provider.TenantID) + clientCredentialsConfig := kvauth.NewClientCredentialsConfig(clientID, clientSecret, *a.provider.TenantID) clientCredentialsConfig.Resource = kvResourceForProviderConfig(a.provider.EnvironmentType) clientCredentialsConfig.AADEndpoint = AadEndpointForType(a.provider.EnvironmentType) return clientCredentialsConfig.Authorizer() } -// secretKeyRef fetch a secret key. -func (a *Azure) secretKeyRef(ctx context.Context, namespace string, secretRef smmeta.SecretKeySelector, clusterScoped bool) (string, error) { - var secret corev1.Secret - ref := types.NamespacedName{ - Name: secretRef.Name, - Namespace: namespace, - } - if clusterScoped && secretRef.Namespace != nil { - ref.Namespace = *secretRef.Namespace - } - err := a.crClient.Get(ctx, ref, &secret) - if err != nil { - return "", fmt.Errorf(errFindSecret, ref.Namespace, ref.Name, err) - } - keyBytes, ok := secret.Data[secretRef.Key] - if !ok { - return "", fmt.Errorf(errFindDataKey, secretRef.Key, secretRef.Name, namespace) - } - value := strings.TrimSpace(string(keyBytes)) - return value, nil -} - func (a *Azure) Close(_ context.Context) error { return nil } diff --git a/pkg/provider/azure/keyvault/keyvault_auth_test.go b/pkg/provider/azure/keyvault/keyvault_auth_test.go index 807fdb00b..76a13f501 100644 --- a/pkg/provider/azure/keyvault/keyvault_auth_test.go +++ b/pkg/provider/azure/keyvault/keyvault_auth_test.go @@ -244,7 +244,7 @@ func TestAuth(t *testing.T) { }, { name: "bad config: missing secret", - expErr: "could not find secret default/password: secrets \"password\" not found", + expErr: "cannot get Kubernetes secret \"password\": secrets \"password\" not found", store: &defaultStore, provider: &esv1beta1.AzureKVProvider{ AuthType: &authType, @@ -258,7 +258,7 @@ func TestAuth(t *testing.T) { }, { name: "cluster secret store", - expErr: "could not find secret foo/password: secrets \"password\" not found", + expErr: "cannot get Kubernetes secret \"password\": secrets \"password\" not found", store: &esv1beta1.ClusterSecretStore{ TypeMeta: metav1.TypeMeta{ Kind: esv1beta1.ClusterSecretStoreKind, diff --git a/pkg/provider/conjur/auth_jwt.go b/pkg/provider/conjur/auth_jwt.go index 762cc377f..545bfc805 100644 --- a/pkg/provider/conjur/auth_jwt.go +++ b/pkg/provider/conjur/auth_jwt.go @@ -27,6 +27,7 @@ import ( 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/resolvers" ) const JwtLifespan = 600 // 10 minutes @@ -46,7 +47,12 @@ func (p *Client) getJWTToken(ctx context.Context, conjurJWTConfig *esv1beta1.Con tokenRef = conjurJWTConfig.SecretRef.DeepCopy() tokenRef.Key = "token" } - jwtToken, err := p.secretKeyRef(ctx, tokenRef) + jwtToken, err := resolvers.SecretKeyRef( + ctx, + p.kube, + p.StoreKind, + p.namespace, + tokenRef) if err != nil { return "", err } diff --git a/pkg/provider/conjur/provider.go b/pkg/provider/conjur/provider.go index 1627d53ee..a3f5a26aa 100644 --- a/pkg/provider/conjur/provider.go +++ b/pkg/provider/conjur/provider.go @@ -32,6 +32,7 @@ import ( 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 ( @@ -109,13 +110,22 @@ func (p *Client) GetConjurClient(ctx context.Context) (SecretsClient, error) { SSLCert: cert, } - if prov.Auth.Apikey != nil { - config.Account = prov.Auth.Apikey.Account - conjUser, secErr := p.secretKeyRef(ctx, prov.Auth.Apikey.UserRef) + if prov.Auth.APIKey != nil { + config.Account = prov.Auth.APIKey.Account + conjUser, secErr := resolvers.SecretKeyRef( + ctx, + p.kube, + p.StoreKind, + p.namespace, prov.Auth.APIKey.UserRef) if secErr != nil { return nil, fmt.Errorf(errBadServiceUser, secErr) } - conjAPIKey, secErr := p.secretKeyRef(ctx, prov.Auth.Apikey.APIKeyRef) + conjAPIKey, secErr := resolvers.SecretKeyRef( + ctx, + p.kube, + p.StoreKind, + p.namespace, + prov.Auth.APIKey.APIKeyRef) if secErr != nil { return nil, fmt.Errorf(errBadServiceAPIKey, secErr) } @@ -224,20 +234,20 @@ func (c *Provider) ValidateStore(store esv1beta1.GenericStore) error { if prov.URL == "" { return fmt.Errorf("conjur URL cannot be empty") } - if prov.Auth.Apikey != nil { - if prov.Auth.Apikey.Account == "" { + if prov.Auth.APIKey != nil { + if prov.Auth.APIKey.Account == "" { return fmt.Errorf("missing Auth.ApiKey.Account") } - if prov.Auth.Apikey.UserRef == nil { + if prov.Auth.APIKey.UserRef == nil { return fmt.Errorf("missing Auth.Apikey.UserRef") } - if prov.Auth.Apikey.APIKeyRef == nil { + if prov.Auth.APIKey.APIKeyRef == nil { return fmt.Errorf("missing Auth.Apikey.ApiKeyRef") } - if err := utils.ValidateReferentSecretSelector(store, *prov.Auth.Apikey.UserRef); err != nil { + if err := utils.ValidateReferentSecretSelector(store, *prov.Auth.APIKey.UserRef); err != nil { return fmt.Errorf("invalid Auth.Apikey.UserRef: %w", err) } - if err := utils.ValidateReferentSecretSelector(store, *prov.Auth.Apikey.APIKeyRef); err != nil { + if err := utils.ValidateReferentSecretSelector(store, *prov.Auth.APIKey.APIKeyRef); err != nil { return fmt.Errorf("invalid Auth.Apikey.ApiKeyRef: %w", err) } } @@ -265,7 +275,7 @@ func (c *Provider) ValidateStore(store esv1beta1.GenericStore) error { } // At least one auth must be configured - if prov.Auth.Apikey == nil && prov.Auth.Jwt == nil { + if prov.Auth.APIKey == nil && prov.Auth.Jwt == nil { return fmt.Errorf("missing Auth.* configuration") } @@ -277,32 +287,7 @@ func (c *Provider) Capabilities() esv1beta1.SecretStoreCapabilities { return esv1beta1.SecretStoreReadOnly } -func (p *Client) secretKeyRef(ctx context.Context, secretRef *esmeta.SecretKeySelector) (string, error) { - secret := &corev1.Secret{} - ref := client.ObjectKey{ - Namespace: p.namespace, - Name: secretRef.Name, - } - if (p.StoreKind == esv1beta1.ClusterSecretStoreKind) && - (secretRef.Namespace != nil) { - ref.Namespace = *secretRef.Namespace - } - err := p.kube.Get(ctx, ref, secret) - if err != nil { - return "", err - } - - keyBytes, ok := secret.Data[secretRef.Key] - if !ok { - return "", err - } - - value := string(keyBytes) - valueStr := strings.TrimSpace(value) - return valueStr, nil -} - -// configMapKeyRef returns the value of a key in a configmap. +// configMapKeyRef returns the value of a key in a ConfigMap. func (p *Client) configMapKeyRef(ctx context.Context, cmRef *esmeta.SecretKeySelector) (string, error) { configMap := &corev1.ConfigMap{} ref := client.ObjectKey{ @@ -349,7 +334,12 @@ func (p *Client) getCA(ctx context.Context, provider *esv1beta1.ConjurProvider) Namespace: provider.CAProvider.Namespace, Key: provider.CAProvider.Key, } - ca, err = p.secretKeyRef(ctx, &keySelector) + ca, err = resolvers.SecretKeyRef( + ctx, + p.kube, + p.StoreKind, + p.namespace, + &keySelector) if err != nil { return "", fmt.Errorf(errUnableToFetchCAProviderSecret, err) } diff --git a/pkg/provider/conjur/provider_test.go b/pkg/provider/conjur/provider_test.go index 1b49efb66..3f62d2b7e 100644 --- a/pkg/provider/conjur/provider_test.go +++ b/pkg/provider/conjur/provider_test.go @@ -351,7 +351,7 @@ func makeAPIKeySecretStore(svcURL, svcUser, svcApikey, svcAccount string) *esv1b Conjur: &esv1beta1.ConjurProvider{ URL: svcURL, Auth: esv1beta1.ConjurAuth{ - Apikey: &esv1beta1.ConjurApikey{ + APIKey: &esv1beta1.ConjurAPIKey{ Account: svcAccount, UserRef: uref, APIKeyRef: aref, @@ -465,7 +465,7 @@ func makeFakeCASource(kind, caData string) kclient.Object { Namespace: "default", }, Data: map[string][]byte{ - "conjur-cert": []byte(caData), + "ca": []byte(caData), }, } } diff --git a/pkg/provider/delinea/provider.go b/pkg/provider/delinea/provider.go index 68e809cb3..5bafca41b 100644 --- a/pkg/provider/delinea/provider.go +++ b/pkg/provider/delinea/provider.go @@ -17,14 +17,13 @@ package delinea import ( "context" "errors" - "fmt" "github.com/DelineaXPM/dsv-sdk-go/v2/vault" - corev1 "k8s.io/api/core/v1" kubeClient "sigs.k8s.io/controller-runtime/pkg/client" 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" ) var ( @@ -38,8 +37,6 @@ var ( errMissingSecretName = errors.New("must specify a secret name") errMissingSecretKey = errors.New("must specify a secret key") errClusterStoreRequiresNamespace = errors.New("when using a ClusterSecretStore, namespaces must be explicitly set") - - errNoSuchKeyFmt = "no such key in secret: %q" ) type Provider struct{} @@ -62,12 +59,12 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, return nil, errClusterStoreRequiresNamespace } - clientID, err := loadConfigSecret(ctx, cfg.ClientID, kube, namespace) + clientID, err := loadConfigSecret(ctx, store.GetKind(), cfg.ClientID, kube, namespace) if err != nil { return nil, err } - clientSecret, err := loadConfigSecret(ctx, cfg.ClientSecret, kube, namespace) + clientSecret, err := loadConfigSecret(ctx, store.GetKind(), cfg.ClientSecret, kube, namespace) if err != nil { return nil, err } @@ -90,33 +87,19 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, }, nil } -func loadConfigSecret(ctx context.Context, ref *esv1beta1.DelineaProviderSecretRef, kube kubeClient.Client, defaultNamespace string) (string, error) { +func loadConfigSecret( + ctx context.Context, + storeKind string, + ref *esv1beta1.DelineaProviderSecretRef, + kube kubeClient.Client, + namespace string) (string, error) { if ref.SecretRef == nil { return ref.Value, nil } - if err := validateSecretRef(ref); err != nil { return "", err } - - namespace := defaultNamespace - if ref.SecretRef.Namespace != nil { - namespace = *ref.SecretRef.Namespace - } - - objKey := kubeClient.ObjectKey{Namespace: namespace, Name: ref.SecretRef.Name} - secret := corev1.Secret{} - err := kube.Get(ctx, objKey, &secret) - if err != nil { - return "", err - } - - value, ok := secret.Data[ref.SecretRef.Key] - if !ok { - return "", fmt.Errorf(errNoSuchKeyFmt, ref.SecretRef.Key) - } - - return string(value), nil + return resolvers.SecretKeyRef(ctx, kube, storeKind, namespace, ref.SecretRef) } func validateStoreSecretRef(store esv1beta1.GenericStore, ref *esv1beta1.DelineaProviderSecretRef) error { diff --git a/pkg/provider/delinea/provider_test.go b/pkg/provider/delinea/provider_test.go index cc779ba5d..861584499 100644 --- a/pkg/provider/delinea/provider_test.go +++ b/pkg/provider/delinea/provider_test.go @@ -15,7 +15,6 @@ package delinea import ( "context" - "fmt" "testing" "github.com/DelineaXPM/dsv-sdk-go/v2/vault" @@ -283,7 +282,7 @@ func TestNewClient(t *testing.T) { }, kube: clientfake.NewClientBuilder().WithObjects(clientSecret).Build(), errCheck: func(t *testing.T, err error) { - assert.EqualError(t, err, fmt.Sprintf(errNoSuchKeyFmt, "typo")) + assert.EqualError(t, err, "cannot find secret data for key: \"typo\"") }, }, "valid secret refs": { diff --git a/pkg/provider/doppler/client.go b/pkg/provider/doppler/client.go index e24fa60aa..08db81973 100644 --- a/pkg/provider/doppler/client.go +++ b/pkg/provider/doppler/client.go @@ -23,13 +23,13 @@ import ( "time" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" kclient "sigs.k8s.io/controller-runtime/pkg/client" esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" "github.com/external-secrets/external-secrets/pkg/find" dClient "github.com/external-secrets/external-secrets/pkg/provider/doppler/client" "github.com/external-secrets/external-secrets/pkg/utils" + "github.com/external-secrets/external-secrets/pkg/utils/resolvers" ) const ( @@ -41,8 +41,6 @@ const ( secretsDownloadFileKey = "DOPPLER_SECRETS_FILE" errDopplerTokenSecretName = "missing auth.secretRef.dopplerToken.name" errInvalidClusterStoreMissingDopplerTokenNamespace = "missing auth.secretRef.dopplerToken.namespace" - errFetchDopplerTokenSecret = "unable to find find DopplerToken secret: %w" - errMissingDopplerToken = "auth.secretRef.dopplerToken.key '%s' not found in secret '%s'" ) type Client struct { @@ -68,35 +66,16 @@ type SecretsClientInterface interface { } func (c *Client) setAuth(ctx context.Context) error { - credentialsSecret := &corev1.Secret{} - credentialsSecretName := c.store.Auth.SecretRef.DopplerToken.Name - if credentialsSecretName == "" { - return fmt.Errorf(errDopplerTokenSecretName) - } - objectKey := types.NamespacedName{ - Name: credentialsSecretName, - Namespace: c.namespace, - } - // only ClusterStore is allowed to set namespace (and then it's required) - if c.storeKind == esv1beta1.ClusterSecretStoreKind { - if c.store.Auth.SecretRef.DopplerToken.Namespace == nil { - return fmt.Errorf(errInvalidClusterStoreMissingDopplerTokenNamespace) - } - objectKey.Namespace = *c.store.Auth.SecretRef.DopplerToken.Namespace - } - - err := c.kube.Get(ctx, objectKey, credentialsSecret) + token, err := resolvers.SecretKeyRef( + ctx, + c.kube, + c.storeKind, + c.namespace, + &c.store.Auth.SecretRef.DopplerToken) if err != nil { - return fmt.Errorf(errFetchDopplerTokenSecret, err) + return err } - - dopplerToken := credentialsSecret.Data[c.store.Auth.SecretRef.DopplerToken.Key] - if (dopplerToken == nil) || (len(dopplerToken) == 0) { - return fmt.Errorf(errMissingDopplerToken, c.store.Auth.SecretRef.DopplerToken.Key, credentialsSecretName) - } - - c.dopplerToken = string(dopplerToken) - + c.dopplerToken = token return nil } diff --git a/pkg/provider/gcp/secretmanager/auth.go b/pkg/provider/gcp/secretmanager/auth.go index 2b80fff13..d7da8ec1b 100644 --- a/pkg/provider/gcp/secretmanager/auth.go +++ b/pkg/provider/gcp/secretmanager/auth.go @@ -19,15 +19,14 @@ import ( "golang.org/x/oauth2" "golang.org/x/oauth2/google" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" kclient "sigs.k8s.io/controller-runtime/pkg/client" esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" + "github.com/external-secrets/external-secrets/pkg/utils/resolvers" ) -func NewTokenSource(ctx context.Context, auth esv1beta1.GCPSMAuth, projectID string, isClusterKind bool, kube kclient.Client, namespace string) (oauth2.TokenSource, error) { - ts, err := serviceAccountTokenSource(ctx, auth, isClusterKind, kube, namespace) +func NewTokenSource(ctx context.Context, auth esv1beta1.GCPSMAuth, projectID, storeKind string, kube kclient.Client, namespace string) (oauth2.TokenSource, error) { + ts, err := serviceAccountTokenSource(ctx, auth, storeKind, kube, namespace) if ts != nil || err != nil { return ts, err } @@ -36,6 +35,7 @@ func NewTokenSource(ctx context.Context, auth esv1beta1.GCPSMAuth, projectID str return nil, fmt.Errorf("unable to initialize workload identity") } defer wi.Close() + isClusterKind := storeKind == esv1beta1.ClusterSecretStoreKind ts, err = wi.TokenSource(ctx, auth, isClusterKind, kube, namespace) if ts != nil || err != nil { return ts, err @@ -43,29 +43,21 @@ func NewTokenSource(ctx context.Context, auth esv1beta1.GCPSMAuth, projectID str return google.DefaultTokenSource(ctx, CloudPlatformRole) } -func serviceAccountTokenSource(ctx context.Context, auth esv1beta1.GCPSMAuth, isClusterKind bool, kube kclient.Client, namespace string) (oauth2.TokenSource, error) { +func serviceAccountTokenSource(ctx context.Context, auth esv1beta1.GCPSMAuth, storeKind string, kube kclient.Client, namespace string) (oauth2.TokenSource, error) { sr := auth.SecretRef if sr == nil { return nil, nil } - credentialsSecret := &v1.Secret{} - credentialsSecretName := sr.SecretAccessKey.Name - objectKey := types.NamespacedName{ - Name: credentialsSecretName, - Namespace: namespace, - } - if isClusterKind && sr.SecretAccessKey.Namespace != nil { - objectKey.Namespace = *sr.SecretAccessKey.Namespace - } - err := kube.Get(ctx, objectKey, credentialsSecret) + credentials, err := resolvers.SecretKeyRef( + ctx, + kube, + storeKind, + namespace, + &auth.SecretRef.SecretAccessKey) if err != nil { - return nil, fmt.Errorf(errFetchSAKSecret, err) + return nil, err } - credentials := credentialsSecret.Data[sr.SecretAccessKey.Key] - if (credentials == nil) || (len(credentials) == 0) { - return nil, fmt.Errorf(errMissingSAK) - } - config, err := google.JWTConfigFromJSON(credentials, CloudPlatformRole) + config, err := google.JWTConfigFromJSON([]byte(credentials), CloudPlatformRole) if err != nil { return nil, fmt.Errorf(errUnableProcessJSONCredentials, err) } diff --git a/pkg/provider/gcp/secretmanager/client.go b/pkg/provider/gcp/secretmanager/client.go index 04d3c1530..6d49776bf 100644 --- a/pkg/provider/gcp/secretmanager/client.go +++ b/pkg/provider/gcp/secretmanager/client.go @@ -50,7 +50,6 @@ const ( errClientClose = "unable to close SecretManager client: %w" errMissingStoreSpec = "invalid: missing store spec" errFetchSAKSecret = "could not fetch SecretAccessKey secret: %w" - errMissingSAK = "missing SecretAccessKey" errUnableProcessJSONCredentials = "failed to process the provided JSON credentials: %w" errUnableCreateGCPSMClient = "failed to create GCP secretmanager client: %w" errUninitalizedGCPProvider = "provider GCP is not initialized" diff --git a/pkg/provider/gcp/secretmanager/provider.go b/pkg/provider/gcp/secretmanager/provider.go index fd346a1b0..ac92bfb29 100644 --- a/pkg/provider/gcp/secretmanager/provider.go +++ b/pkg/provider/gcp/secretmanager/provider.go @@ -90,7 +90,7 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, return client, nil } - ts, err := NewTokenSource(ctx, gcpStore.Auth, clusterProjectID, isClusterKind, kube, namespace) + ts, err := NewTokenSource(ctx, gcpStore.Auth, clusterProjectID, store.GetKind(), kube, namespace) if err != nil { return nil, fmt.Errorf(errUnableCreateGCPSMClient, err) } diff --git a/pkg/provider/gitlab/gitlab.go b/pkg/provider/gitlab/gitlab.go index 844f95022..987f65884 100644 --- a/pkg/provider/gitlab/gitlab.go +++ b/pkg/provider/gitlab/gitlab.go @@ -25,7 +25,6 @@ import ( "github.com/tidwall/gjson" "github.com/xanzy/go-gitlab" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" @@ -33,13 +32,13 @@ import ( "github.com/external-secrets/external-secrets/pkg/find" "github.com/external-secrets/external-secrets/pkg/metrics" "github.com/external-secrets/external-secrets/pkg/utils" + "github.com/external-secrets/external-secrets/pkg/utils/resolvers" ) const ( errGitlabCredSecretName = "credentials are empty" errInvalidClusterStoreMissingSAKNamespace = "invalid clusterStore missing SAK namespace" errFetchSAKSecret = "couldn't find secret on cluster: %w" - errMissingSAK = "missing credentials while setting auth" errList = "could not verify whether the gilabClient is valid: %w" errProjectAuth = "gitlabClient is not allowed to get secrets for project id [%s]" errGroupAuth = "gitlabClient is not allowed to get secrets for group id [%s]" @@ -78,34 +77,13 @@ func (a ProjectGroupPathSorter) Less(i, j int) bool { return len(a[i].FullPath) var log = ctrl.Log.WithName("provider").WithName("gitlab") // Set gitlabBase credentials to Access Token. -func (g *gitlabBase) getAuth(ctx context.Context) ([]byte, error) { - credentialsSecret := &corev1.Secret{} - credentialsSecretName := g.store.Auth.SecretRef.AccessToken.Name - if credentialsSecretName == "" { - return nil, fmt.Errorf(errGitlabCredSecretName) - } - objectKey := types.NamespacedName{ - Name: credentialsSecretName, - Namespace: g.namespace, - } - // only ClusterStore is allowed to set namespace (and then it's required) - if g.storeKind == esv1beta1.ClusterSecretStoreKind { - if g.store.Auth.SecretRef.AccessToken.Namespace == nil { - return nil, fmt.Errorf(errInvalidClusterStoreMissingSAKNamespace) - } - objectKey.Namespace = *g.store.Auth.SecretRef.AccessToken.Namespace - } - - err := g.kube.Get(ctx, objectKey, credentialsSecret) - if err != nil { - return nil, fmt.Errorf(errFetchSAKSecret, err) - } - - credentials := credentialsSecret.Data[g.store.Auth.SecretRef.AccessToken.Key] - if len(credentials) == 0 { - return nil, fmt.Errorf(errMissingSAK) - } - return credentials, nil +func (g *gitlabBase) getAuth(ctx context.Context) (string, error) { + return resolvers.SecretKeyRef( + ctx, + g.kube, + g.storeKind, + g.namespace, + &g.store.Auth.SecretRef.AccessToken) } func (g *gitlabBase) DeleteSecret(_ context.Context, _ esv1beta1.PushSecretRemoteRef) error { diff --git a/pkg/provider/gitlab/gitlab_test.go b/pkg/provider/gitlab/gitlab_test.go index c10fb34f9..dc788a6d0 100644 --- a/pkg/provider/gitlab/gitlab_test.go +++ b/pkg/provider/gitlab/gitlab_test.go @@ -46,7 +46,7 @@ const ( groupvalue = "groupvalue" groupid = "groupId" defaultErrorMessage = "[%d] unexpected error: [%s], expected: [%s]" - errMissingCredentials = "credentials are empty" + errMissingCredentials = "cannot get Kubernetes secret \"\": secrets \"\" not found" testKey = "testKey" findTestPrefix = "test.*" ) @@ -351,7 +351,7 @@ func TestNewClient(t *testing.T) { store.Spec.Provider.Gitlab.Auth.SecretRef.AccessToken.Name = authorizedKeySecretName store.Spec.Provider.Gitlab.Auth.SecretRef.AccessToken.Key = authorizedKeySecretKey secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace) - tassert.EqualError(t, err, "couldn't find secret on cluster: secrets \"authorizedKeySecretName\" not found") + tassert.EqualError(t, err, "cannot get Kubernetes secret \"authorizedKeySecretName\": secrets \"authorizedKeySecretName\" not found") tassert.Nil(t, secretClient) err = createK8sSecret(ctx, t, k8sClient, namespace, authorizedKeySecretName, authorizedKeySecretKey, toJSON(t, newFakeAuthorizedKey())) diff --git a/pkg/provider/gitlab/provider.go b/pkg/provider/gitlab/provider.go index 279cf85e3..005db3c4f 100644 --- a/pkg/provider/gitlab/provider.go +++ b/pkg/provider/gitlab/provider.go @@ -86,7 +86,7 @@ func (g *gitlabBase) getClient(ctx context.Context, provider *esv1beta1.GitlabPr // in a similar way to extend functionality of the provider // Create a new GitLab Client using credentials and options - client, err := gitlab.NewClient(string(credentials), opts...) + client, err := gitlab.NewClient(credentials, opts...) if err != nil { return nil, err } diff --git a/pkg/provider/ibm/provider.go b/pkg/provider/ibm/provider.go index 2a0fdbc54..baaf3ed94 100644 --- a/pkg/provider/ibm/provider.go +++ b/pkg/provider/ibm/provider.go @@ -26,13 +26,13 @@ import ( "github.com/google/uuid" "github.com/tidwall/gjson" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" kclient "sigs.k8s.io/controller-runtime/pkg/client" 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/metrics" "github.com/external-secrets/external-secrets/pkg/utils" + "github.com/external-secrets/external-secrets/pkg/utils/resolvers" ) const ( @@ -51,15 +51,13 @@ const ( payloadConst = "payload" smAPIKeyConst = "api_key" - errIBMClient = "cannot setup new ibm client: %w" - errIBMCredSecretName = "invalid IBM SecretStore resource: missing IBM APIKey" - errUninitalizedIBMProvider = "provider IBM is not initialized" - errInvalidClusterStoreMissingSKNamespace = "invalid ClusterStore, missing namespace" - errFetchSAKSecret = "could not fetch SecretAccessKey secret: %w" - errMissingSAK = "missing SecretAccessKey" - errJSONSecretUnmarshal = "unable to unmarshal secret: %w" - errJSONSecretMarshal = "unable to marshal secret: %w" - errExtractingSecret = "unable to extract the fetched secret %s of type %s while performing %s" + errIBMClient = "cannot setup new ibm client: %w" + errIBMCredSecretName = "invalid IBM SecretStore resource: missing IBM APIKey" + errUninitalizedIBMProvider = "provider IBM is not initialized" + errFetchSAKSecret = "could not fetch SecretAccessKey secret: %w" + errJSONSecretUnmarshal = "unable to unmarshal secret: %w" + errJSONSecretMarshal = "unable to marshal secret: %w" + errExtractingSecret = "unable to extract the fetched secret %s of type %s while performing %s" defaultCacheSize = 100 defaultCacheExpiry = 1 * time.Hour @@ -93,33 +91,11 @@ type client struct { } func (c *client) setAuth(ctx context.Context) error { - credentialsSecret := &corev1.Secret{} - credentialsSecretName := c.store.Auth.SecretRef.SecretAPIKey.Name - if credentialsSecretName == "" { - return fmt.Errorf(errIBMCredSecretName) - } - objectKey := types.NamespacedName{ - Name: credentialsSecretName, - Namespace: c.namespace, - } - - // only ClusterStore is allowed to set namespace (and then it's required) - if c.storeKind == esv1beta1.ClusterSecretStoreKind { - if c.store.Auth.SecretRef.SecretAPIKey.Namespace == nil { - return fmt.Errorf(errInvalidClusterStoreMissingSKNamespace) - } - objectKey.Namespace = *c.store.Auth.SecretRef.SecretAPIKey.Namespace - } - - err := c.kube.Get(ctx, objectKey, credentialsSecret) + apiKey, err := resolvers.SecretKeyRef(ctx, c.kube, c.storeKind, c.namespace, &c.store.Auth.SecretRef.SecretAPIKey) if err != nil { - return fmt.Errorf(errFetchSAKSecret, err) - } - - c.credentials = credentialsSecret.Data[c.store.Auth.SecretRef.SecretAPIKey.Key] - if (c.credentials == nil) || (len(c.credentials) == 0) { - return fmt.Errorf(errMissingSAK) + return err } + c.credentials = []byte(apiKey) return nil } diff --git a/pkg/provider/keepersecurity/provider.go b/pkg/provider/keepersecurity/provider.go index fc1b87fb5..b7ed5a556 100644 --- a/pkg/provider/keepersecurity/provider.go +++ b/pkg/provider/keepersecurity/provider.go @@ -19,12 +19,11 @@ import ( ksm "github.com/keeper-security/secrets-manager-go/core" "github.com/keeper-security/secrets-manager-go/core/logger" - v1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" kclient "sigs.k8s.io/controller-runtime/pkg/client" 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" ) const ( @@ -66,8 +65,7 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, keeperStore := storeSpec.Provider.KeeperSecurity - isClusterKind := store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind - clientConfig, err := getKeeperSecurityAuth(ctx, keeperStore, kube, isClusterKind, namespace) + clientConfig, err := getKeeperSecurityAuth(ctx, keeperStore, kube, store.GetKind(), namespace) if err != nil { return nil, fmt.Errorf(errKeeperSecurityUnableToCreateConfig, err) } @@ -112,33 +110,11 @@ func (p *Provider) ValidateStore(store esv1beta1.GenericStore) error { return nil } -func getKeeperSecurityAuth(ctx context.Context, store *esv1beta1.KeeperSecurityProvider, kube kclient.Client, isClusterKind bool, namespace string) (string, error) { - auth := store.Auth - - credentialsSecret := &v1.Secret{} - credentialsSecretName := auth.Name - objectKey := types.NamespacedName{ - Name: credentialsSecretName, - Namespace: namespace, - } - - // only ClusterStore is allowed to set namespace (and then it's required) - if isClusterKind { - if credentialsSecretName != "" && auth.Namespace == nil { - return "", fmt.Errorf(errInvalidClusterStoreMissingK8sSecretNamespace) - } else if credentialsSecretName != "" { - objectKey.Namespace = *auth.Namespace - } - } - - err := kube.Get(ctx, objectKey, credentialsSecret) - if err != nil { - return "", fmt.Errorf(errFetchK8sSecret, err) - } - data := credentialsSecret.Data[auth.Key] - if (data == nil) || (len(data) == 0) { - return "", fmt.Errorf(errMissingK8sSecretKey, auth.Key) - } - - return string(data), nil +func getKeeperSecurityAuth(ctx context.Context, store *esv1beta1.KeeperSecurityProvider, kube kclient.Client, storeKind, namespace string) (string, error) { + return resolvers.SecretKeyRef( + ctx, + kube, + storeKind, + namespace, + &store.Auth) } diff --git a/pkg/provider/kubernetes/auth.go b/pkg/provider/kubernetes/auth.go index 4a760dc5e..a4118de67 100644 --- a/pkg/provider/kubernetes/auth.go +++ b/pkg/provider/kubernetes/auth.go @@ -25,6 +25,7 @@ import ( 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/resolvers" ) const ( @@ -128,31 +129,18 @@ func (c *Client) serviceAccountToken(ctx context.Context, serviceAccountRef *esm return []byte(tr.Status.Token), nil } -func (c *Client) fetchSecretKey(ctx context.Context, key esmeta.SecretKeySelector) ([]byte, error) { - keySecret := &corev1.Secret{} - 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, keySecret) +func (c *Client) fetchSecretKey(ctx context.Context, ref esmeta.SecretKeySelector) ([]byte, error) { + secret, err := resolvers.SecretKeyRef( + ctx, + c.ctrlClient, + c.storeKind, + c.namespace, + &ref, + ) if err != nil { - return nil, fmt.Errorf(errFetchCredentials, err) + return nil, err } - val, ok := keySecret.Data[key.Key] - if !ok { - return nil, fmt.Errorf(errMissingCredentials, key.Key) - } - if len(val) == 0 { - return nil, fmt.Errorf(errEmptyKey, key.Key) - } - return val, nil + return []byte(secret), nil } func (c *Client) fetchConfigMapKey(ctx context.Context, key esmeta.SecretKeySelector) ([]byte, error) { diff --git a/pkg/provider/onepassword/onepassword.go b/pkg/provider/onepassword/onepassword.go index 2e42420e0..936beedd1 100644 --- a/pkg/provider/onepassword/onepassword.go +++ b/pkg/provider/onepassword/onepassword.go @@ -23,12 +23,12 @@ import ( "github.com/1Password/connect-sdk-go/connect" "github.com/1Password/connect-sdk-go/onepassword" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" kclient "sigs.k8s.io/controller-runtime/pkg/client" esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" "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 ( @@ -95,29 +95,18 @@ func (provider *ProviderOnePassword) Capabilities() esv1beta1.SecretStoreCapabil // NewClient constructs a 1Password Provider. func (provider *ProviderOnePassword) NewClient(ctx context.Context, store esv1beta1.GenericStore, kube kclient.Client, namespace string) (esv1beta1.SecretsClient, error) { config := store.GetSpec().Provider.OnePassword - - credentialsSecret := &corev1.Secret{} - objectKey := types.NamespacedName{ - Name: config.Auth.SecretRef.ConnectToken.Name, - Namespace: namespace, - } - - // only ClusterSecretStore is allowed to set namespace (and then it's required) - if store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind { - objectKey.Namespace = *config.Auth.SecretRef.ConnectToken.Namespace - } - - err := kube.Get(ctx, objectKey, credentialsSecret) + token, err := resolvers.SecretKeyRef( + ctx, + kube, + store.GetKind(), + namespace, + &config.Auth.SecretRef.ConnectToken, + ) if err != nil { - return nil, fmt.Errorf(errFetchK8sSecret, err) + return nil, err } - token := credentialsSecret.Data[config.Auth.SecretRef.ConnectToken.Key] - if (token == nil) || (len(token) == 0) { - return nil, fmt.Errorf(errMissingToken) - } - provider.client = connect.NewClientWithUserAgent(config.ConnectHost, string(token), userAgent) + provider.client = connect.NewClientWithUserAgent(config.ConnectHost, token, userAgent) provider.vaults = config.Vaults - return provider, nil } diff --git a/pkg/provider/oracle/oracle.go b/pkg/provider/oracle/oracle.go index 5a0cd4838..4940ba826 100644 --- a/pkg/provider/oracle/oracle.go +++ b/pkg/provider/oracle/oracle.go @@ -32,7 +32,6 @@ import ( "github.com/oracle/oci-go-sdk/v65/vault" "github.com/tidwall/gjson" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" kclient "sigs.k8s.io/controller-runtime/pkg/client" ctrlcfg "sigs.k8s.io/controller-runtime/pkg/client/config" @@ -40,23 +39,23 @@ import ( 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 ( - errOracleClient = "cannot setup new oracle client: %w" - errORACLECredSecretName = "invalid oracle SecretStore resource: missing oracle APIKey" - errUninitalizedOracleProvider = "provider oracle is not initialized" - errInvalidClusterStoreMissingSKNamespace = "invalid ClusterStore, missing namespace" - errFetchSAKSecret = "could not fetch SecretAccessKey secret: %w" - errMissingPK = "missing PrivateKey" - errMissingUser = "missing User ID" - errMissingTenancy = "missing Tenancy ID" - errMissingRegion = "missing Region" - errMissingFingerprint = "missing Fingerprint" - errMissingVault = "missing Vault" - errJSONSecretUnmarshal = "unable to unmarshal secret: %w" - errMissingKey = "missing Key in secret: %s" - errUnexpectedContent = "unexpected secret bundle content" + errOracleClient = "cannot setup new oracle client: %w" + errORACLECredSecretName = "invalid oracle SecretStore resource: missing oracle APIKey" + errUninitalizedOracleProvider = "provider oracle is not initialized" + errFetchSAKSecret = "could not fetch SecretAccessKey secret: %w" + errMissingPK = "missing PrivateKey" + errMissingUser = "missing User ID" + errMissingTenancy = "missing Tenancy ID" + errMissingRegion = "missing Region" + errMissingFingerprint = "missing Fingerprint" + errMissingVault = "missing Vault" + errJSONSecretUnmarshal = "unable to unmarshal secret: %w" + errMissingKey = "missing Key in secret: %s" + errUnexpectedContent = "unexpected secret bundle content" ) // https://github.com/external-secrets/external-secrets/issues/644 @@ -398,27 +397,17 @@ func getSecretData(ctx context.Context, kube kclient.Client, namespace, storeKin if secretRef.Name == "" { return "", fmt.Errorf(errORACLECredSecretName) } - - objectKey := types.NamespacedName{ - Name: secretRef.Name, - Namespace: namespace, - } - - // only ClusterStore is allowed to set namespace (and then it's required) - if storeKind == esv1beta1.ClusterSecretStoreKind { - if secretRef.Namespace == nil { - return "", fmt.Errorf(errInvalidClusterStoreMissingSKNamespace) - } - objectKey.Namespace = *secretRef.Namespace - } - - secret := corev1.Secret{} - err := kube.Get(ctx, objectKey, &secret) + secret, err := resolvers.SecretKeyRef( + ctx, + kube, + storeKind, + namespace, + &secretRef, + ) if err != nil { return "", fmt.Errorf(errFetchSAKSecret, err) } - - return string(secret.Data[secretRef.Key]), nil + return secret, nil } func getUserAuthConfigurationProvider(ctx context.Context, kube kclient.Client, store *esv1beta1.OracleProvider, namespace, storeKind, region string) (common.ConfigurationProvider, error) { diff --git a/pkg/provider/oracle/oracle_test.go b/pkg/provider/oracle/oracle_test.go index 6bc209528..c46702a47 100644 --- a/pkg/provider/oracle/oracle_test.go +++ b/pkg/provider/oracle/oracle_test.go @@ -440,7 +440,7 @@ func TestVaultManagementService_NewClient(t *testing.T) { }, }, }, - expectedErr: `could not fetch SecretAccessKey secret: secrets "non-existing-secret"`, + expectedErr: `cannot get Kubernetes secret "non-existing-secret": secrets "non-existing-secret" not found`, }, { desc: "invalid retry interval", diff --git a/pkg/provider/scaleway/provider.go b/pkg/provider/scaleway/provider.go index 1ba131b6a..404d98f1f 100644 --- a/pkg/provider/scaleway/provider.go +++ b/pkg/provider/scaleway/provider.go @@ -21,12 +21,12 @@ import ( smapi "github.com/scaleway/scaleway-sdk-go/api/secret/v1alpha1" "github.com/scaleway/scaleway-sdk-go/scw" "github.com/scaleway/scaleway-sdk-go/validation" - corev1 "k8s.io/api/core/v1" ctrl "sigs.k8s.io/controller-runtime" kubeClient "sigs.k8s.io/controller-runtime/pkg/client" 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" ) var ( @@ -52,12 +52,12 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, return nil, fmt.Errorf("when using a ClusterSecretStore, namespaces must be explicitly set") } - accessKey, err := loadConfigSecret(ctx, cfg.AccessKey, kube, namespace) + accessKey, err := loadConfigSecret(ctx, cfg.AccessKey, kube, namespace, store.GetKind()) if err != nil { return nil, err } - secretKey, err := loadConfigSecret(ctx, cfg.SecretKey, kube, namespace) + secretKey, err := loadConfigSecret(ctx, cfg.SecretKey, kube, namespace, store.GetKind()) if err != nil { return nil, err } @@ -80,42 +80,17 @@ func (p *Provider) NewClient(ctx context.Context, store esv1beta1.GenericStore, }, nil } -func loadConfigSecret(ctx context.Context, ref *esv1beta1.ScalewayProviderSecretRef, kube kubeClient.Client, defaultNamespace string) (string, error) { +func loadConfigSecret(ctx context.Context, ref *esv1beta1.ScalewayProviderSecretRef, kube kubeClient.Client, defaultNamespace, storeKind string) (string, error) { if ref.SecretRef == nil { return ref.Value, nil } - - namespace := defaultNamespace - if ref.SecretRef.Namespace != nil { - namespace = *ref.SecretRef.Namespace - } - - if ref.SecretRef.Name == "" { - return "", fmt.Errorf("must specify a value or a reference to a secret") - } - - if ref.SecretRef.Key == "" { - return "", fmt.Errorf("must specify a secret key") - } - - objKey := kubeClient.ObjectKey{ - Namespace: namespace, - Name: ref.SecretRef.Name, - } - - secret := corev1.Secret{} - - err := kube.Get(ctx, objKey, &secret) - if err != nil { - return "", err - } - - value, ok := secret.Data[ref.SecretRef.Key] - if !ok { - return "", fmt.Errorf("no such key in secret: %v", ref.SecretRef.Key) - } - - return string(value), nil + return resolvers.SecretKeyRef( + ctx, + kube, + storeKind, + defaultNamespace, + ref.SecretRef, + ) } func validateSecretRef(store esv1beta1.GenericStore, ref *esv1beta1.ScalewayProviderSecretRef) error { diff --git a/pkg/provider/senhasegura/auth/iso.go b/pkg/provider/senhasegura/auth/iso.go index 8478d1c72..7c2713436 100644 --- a/pkg/provider/senhasegura/auth/iso.go +++ b/pkg/provider/senhasegura/auth/iso.go @@ -28,6 +28,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" + "github.com/external-secrets/external-secrets/pkg/utils/resolvers" ) type ISOInterface interface { @@ -76,12 +77,18 @@ func Authenticate(ctx context.Context, store esv1beta1.GenericStore, provider *e IsoSessionFromSecretRef initialize an ISO OAuth2 flow with .spec.provider.senhasegura.auth.isoSecretRef parameters. */ func (s *SenhaseguraIsoSession) IsoSessionFromSecretRef(ctx context.Context, provider *esv1beta1.SenhaseguraProvider, store esv1beta1.GenericStore, kube client.Client, namespace string) (*SenhaseguraIsoSession, error) { - clientSecret, err := getKubernetesSecret(ctx, provider.Auth.ClientSecret, store, kube, namespace) + secret, err := resolvers.SecretKeyRef( + ctx, + kube, + store.GetKind(), + namespace, + &provider.Auth.ClientSecret, + ) if err != nil { return &SenhaseguraIsoSession{}, err } - isoToken, err := s.GetIsoToken(provider.Auth.ClientID, clientSecret, provider.URL, provider.IgnoreSslCertificate) + isoToken, err := s.GetIsoToken(provider.Auth.ClientID, secret, provider.URL, provider.IgnoreSslCertificate) if err != nil { return &SenhaseguraIsoSession{}, err } diff --git a/pkg/provider/senhasegura/auth/kubernetes_secret.go b/pkg/provider/senhasegura/auth/kubernetes_secret.go deleted file mode 100644 index 59747047c..000000000 --- a/pkg/provider/senhasegura/auth/kubernetes_secret.go +++ /dev/null @@ -1,57 +0,0 @@ -/* -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package auth - -import ( - "context" - "fmt" - - v1 "k8s.io/api/core/v1" - "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" -) - -const ( - errRequiredNamespaceNotFound = "invalid ClusterSecretStore: missing namespace in %s" - errCannotFetchKubernetesSecret = "could not fetch Kubernetes secret %s" -) - -/* -getKubernetesSecret get Kubernetes Secret based on object parameter in namespace where ESO is installed or another, if ClusterSecretStore is used. -*/ -func getKubernetesSecret(ctx context.Context, object esmeta.SecretKeySelector, store esv1beta1.GenericStore, kube client.Client, namespace string) (string, error) { - ke := client.ObjectKey{ - Name: object.Name, - Namespace: namespace, // Default to ExternalSecret namespace - } - - // Only ClusterStore is allowed to set namespace (and then it's required) - if store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind { - if object.Namespace == nil { - return "", fmt.Errorf(errRequiredNamespaceNotFound, object.Key) - } - ke.Namespace = *object.Namespace - } - - secret := v1.Secret{} - err := kube.Get(ctx, ke, &secret) - if err != nil { - return "", fmt.Errorf(errCannotFetchKubernetesSecret, object.Name) - } - - return string(secret.Data[object.Key]), nil -} diff --git a/pkg/provider/vault/iamauth/iamauth.go b/pkg/provider/vault/iamauth/iamauth.go index e26631834..74192db1f 100644 --- a/pkg/provider/vault/iamauth/iamauth.go +++ b/pkg/provider/vault/iamauth/iamauth.go @@ -41,6 +41,7 @@ import ( esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1" "github.com/external-secrets/external-secrets/pkg/provider/vault/util" + "github.com/external-secrets/external-secrets/pkg/utils/resolvers" ) var ( @@ -54,14 +55,6 @@ const ( STSEndpointEnv = "AWS_STS_ENDPOINT" AWSWebIdentityTokenFileEnvVar = "AWS_WEB_IDENTITY_TOKEN_FILE" - - errInvalidClusterStoreMissingAKIDNamespace = "invalid ClusterSecretStore: missing AWS AccessKeyID Namespace" - errInvalidClusterStoreMissingSAKNamespace = "invalid ClusterSecretStore: missing AWS SecretAccessKey Namespace" - errFetchAKIDSecret = "could not fetch accessKeyID secret: %w" - errFetchSAKSecret = "could not fetch SecretAccessKey secret: %w" - errFetchSTSecret = "could not fetch SessionToken secret: %w" - errMissingSAK = "missing SecretAccessKey" - errMissingAKID = "missing AccessKeyID" ) // DefaultJWTProvider returns a credentials.Provider that calls the AssumeRoleWithWebidentity @@ -232,58 +225,37 @@ func CredsFromControllerServiceAccount(ctx context.Context, saname, ns, region s // construct a aws.Credentials object // The namespace of the external secret is used if the ClusterSecretStore does not specify a namespace (referentAuth) // If the ClusterSecretStore defines a namespace it will take precedence. -func CredsFromSecretRef(ctx context.Context, auth esv1beta1.VaultIamAuth, isClusterKind bool, kube kclient.Client, namespace string) (*credentials.Credentials, error) { - ke := kclient.ObjectKey{ - Name: auth.SecretRef.AccessKeyID.Name, - Namespace: namespace, - } - if isClusterKind && auth.SecretRef.AccessKeyID.Namespace != nil { - ke.Namespace = *auth.SecretRef.AccessKeyID.Namespace - } - akSecret := v1.Secret{} - err := kube.Get(ctx, ke, &akSecret) +func CredsFromSecretRef(ctx context.Context, auth esv1beta1.VaultIamAuth, storeKind string, kube kclient.Client, namespace string) (*credentials.Credentials, error) { + akid, err := resolvers.SecretKeyRef( + ctx, + kube, + storeKind, + namespace, + &auth.SecretRef.AccessKeyID, + ) if err != nil { - return nil, fmt.Errorf(errFetchAKIDSecret, err) + return nil, err } - ke = kclient.ObjectKey{ - Name: auth.SecretRef.SecretAccessKey.Name, - Namespace: namespace, - } - if isClusterKind && auth.SecretRef.SecretAccessKey.Namespace != nil { - ke.Namespace = *auth.SecretRef.SecretAccessKey.Namespace - } - sakSecret := v1.Secret{} - err = kube.Get(ctx, ke, &sakSecret) + sak, err := resolvers.SecretKeyRef( + ctx, + kube, + storeKind, + namespace, + &auth.SecretRef.SecretAccessKey, + ) if err != nil { - return nil, fmt.Errorf(errFetchSAKSecret, err) - } - sak := string(sakSecret.Data[auth.SecretRef.SecretAccessKey.Key]) - aks := string(akSecret.Data[auth.SecretRef.AccessKeyID.Key]) - if sak == "" { - return nil, fmt.Errorf(errMissingSAK) - } - if aks == "" { - return nil, fmt.Errorf(errMissingAKID) + return nil, err } - var sessionToken string - if auth.SecretRef.SessionToken != nil { - ke = kclient.ObjectKey{ - Name: auth.SecretRef.SessionToken.Name, - Namespace: namespace, - } - if isClusterKind && auth.SecretRef.SessionToken.Namespace != nil { - ke.Namespace = *auth.SecretRef.SessionToken.Namespace - } - stSecret := v1.Secret{} - err = kube.Get(ctx, ke, &stSecret) - if err != nil { - return nil, fmt.Errorf(errFetchSTSecret, err) - } - sessionToken = string(stSecret.Data[auth.SecretRef.SessionToken.Key]) - } - - return credentials.NewStaticCredentials(aks, sak, sessionToken), err + // session token is optional + sessionToken, _ := resolvers.SecretKeyRef( + ctx, + kube, + storeKind, + namespace, + auth.SecretRef.SessionToken, + ) + return credentials.NewStaticCredentials(akid, sak, sessionToken), err } type STSProvider func(*session.Session) stsiface.STSAPI diff --git a/pkg/provider/vault/vault.go b/pkg/provider/vault/vault.go index ada6020bb..13e66d889 100644 --- a/pkg/provider/vault/vault.go +++ b/pkg/provider/vault/vault.go @@ -40,7 +40,7 @@ import ( authuserpass "github.com/hashicorp/vault/api/auth/userpass" "github.com/spf13/pflag" "github.com/tidwall/gjson" - authenticationv1 "k8s.io/api/authentication/v1" + authv1 "k8s.io/api/authentication/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -60,6 +60,7 @@ import ( vaultiamauth "github.com/external-secrets/external-secrets/pkg/provider/vault/iamauth" "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" ) var ( @@ -1088,7 +1089,7 @@ func (v *client) configureClientTLS(ctx context.Context, cfg *vault.Config) erro if clientTLS.KeySecretRef.Key == "" { clientTLS.KeySecretRef.Key = corev1.TLSPrivateKeyKey } - clientKey, err := v.secretKeyRef(ctx, clientTLS.KeySecretRef) + clientKey, err := resolvers.SecretKeyRef(ctx, v.kube, v.storeKind, v.namespace, clientTLS.KeySecretRef) if err != nil { return err } @@ -1096,7 +1097,7 @@ func (v *client) configureClientTLS(ctx context.Context, cfg *vault.Config) erro if clientTLS.CertSecretRef.Key == "" { clientTLS.CertSecretRef.Key = corev1.TLSCertKey } - clientCert, err := v.secretKeyRef(ctx, clientTLS.CertSecretRef) + clientCert, err := resolvers.SecretKeyRef(ctx, v.kube, v.storeKind, v.namespace, clientTLS.CertSecretRef) if err != nil { return err } @@ -1125,7 +1126,7 @@ func getCertFromSecret(v *client) ([]byte, error) { } ctx := context.Background() - res, err := v.secretKeyRef(ctx, &secretRef) + res, err := resolvers.SecretKeyRef(ctx, v.kube, v.storeKind, v.namespace, &secretRef) if err != nil { return nil, fmt.Errorf(errVaultCert, err) } @@ -1226,7 +1227,7 @@ func (v *client) setAuth(ctx context.Context, cfg *vault.Config) error { func setSecretKeyToken(ctx context.Context, v *client) (bool, error) { tokenRef := v.store.Auth.TokenSecretRef if tokenRef != nil { - token, err := v.secretKeyRef(ctx, tokenRef) + token, err := resolvers.SecretKeyRef(ctx, v.kube, v.storeKind, v.namespace, tokenRef) if err != nil { return true, err } @@ -1339,71 +1340,19 @@ func (v *client) secretKeyRefForServiceAccount(ctx context.Context, serviceAccou return "", fmt.Errorf(errGetKubeSASecrets, ref.Name) } for _, tokenRef := range serviceAccount.Secrets { - retval, err := v.secretKeyRef(ctx, &esmeta.SecretKeySelector{ + token, err := resolvers.SecretKeyRef(ctx, v.kube, v.storeKind, v.namespace, &esmeta.SecretKeySelector{ Name: tokenRef.Name, Namespace: &ref.Namespace, Key: "token", }) - if err != nil { continue } - - return retval, nil + return token, nil } return "", fmt.Errorf(errGetKubeSANoToken, ref.Name) } -func (v *client) secretKeyRef(ctx context.Context, secretRef *esmeta.SecretKeySelector) (string, error) { - secret := &corev1.Secret{} - ref := types.NamespacedName{ - Namespace: v.namespace, - Name: secretRef.Name, - } - if (v.storeKind == esv1beta1.ClusterSecretStoreKind) && - (secretRef.Namespace != nil) { - ref.Namespace = *secretRef.Namespace - } - err := v.kube.Get(ctx, ref, secret) - if err != nil { - return "", fmt.Errorf(errGetKubeSecret, ref.Name, ref.Namespace, err) - } - - keyBytes, ok := secret.Data[secretRef.Key] - if !ok { - return "", fmt.Errorf(errSecretKeyFmt, secretRef.Key) - } - - value := string(keyBytes) - valueStr := strings.TrimSpace(value) - return valueStr, nil -} - -func (v *client) serviceAccountToken(ctx context.Context, serviceAccountRef esmeta.ServiceAccountSelector, additionalAud []string, expirationSeconds int64) (string, error) { - audiences := serviceAccountRef.Audiences - if len(additionalAud) > 0 { - audiences = append(audiences, additionalAud...) - } - tokenRequest := &authenticationv1.TokenRequest{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: v.namespace, - }, - Spec: authenticationv1.TokenRequestSpec{ - Audiences: audiences, - ExpirationSeconds: &expirationSeconds, - }, - } - if (v.storeKind == esv1beta1.ClusterSecretStoreKind) && - (serviceAccountRef.Namespace != nil) { - tokenRequest.Namespace = *serviceAccountRef.Namespace - } - tokenResponse, err := v.corev1.ServiceAccounts(tokenRequest.Namespace).CreateToken(ctx, serviceAccountRef.Name, tokenRequest, metav1.CreateOptions{}) - if err != nil { - return "", fmt.Errorf(errGetKubeSATokenRequest, serviceAccountRef.Name, err) - } - return tokenResponse.Status.Token, nil -} - // checkToken does a lookup and checks if the provided token exists. func checkToken(ctx context.Context, token util.Token) (bool, error) { // https://www.vaultproject.io/api-docs/auth/token#lookup-a-token-self @@ -1447,7 +1396,7 @@ func (v *client) requestTokenWithAppRoleRef(ctx context.Context, appRole *esv1be if appRole.RoleID != "" { // use roleId from CRD, if configured roleID = strings.TrimSpace(appRole.RoleID) } else if appRole.RoleRef != nil { // use RoleID from Secret, if configured - roleID, err = v.secretKeyRef(ctx, appRole.RoleRef) + roleID, err = resolvers.SecretKeyRef(ctx, v.kube, v.storeKind, v.namespace, appRole.RoleRef) if err != nil { return err } @@ -1455,7 +1404,7 @@ func (v *client) requestTokenWithAppRoleRef(ctx context.Context, appRole *esv1be return fmt.Errorf(errInvalidAppRoleID) } - secretID, err := v.secretKeyRef(ctx, &appRole.SecretRef) + secretID, err := resolvers.SecretKeyRef(ctx, v.kube, v.storeKind, v.namespace, &appRole.SecretRef) if err != nil { return err } @@ -1503,7 +1452,14 @@ func getJwtString(ctx context.Context, v *client, kubernetesAuth *esv1beta1.Vaul // Kubernetes >=v1.24: fetch token via TokenRequest API // note: this is a massive change from vault perspective: the `iss` claim will very likely change. // Vault 1.9 deprecated issuer validation by default, and authentication with Vault clusters <1.9 will likely fail. - jwt, err = v.serviceAccountToken(ctx, *kubernetesAuth.ServiceAccountRef, nil, 600) + jwt, err = createServiceAccountToken( + ctx, + v.corev1, + v.storeKind, + v.namespace, + *kubernetesAuth.ServiceAccountRef, + nil, + 600) if err != nil { return "", err } @@ -1514,7 +1470,7 @@ func getJwtString(ctx context.Context, v *client, kubernetesAuth *esv1beta1.Vaul tokenRef = kubernetesAuth.SecretRef.DeepCopy() tokenRef.Key = "token" } - jwt, err := v.secretKeyRef(ctx, tokenRef) + jwt, err := resolvers.SecretKeyRef(ctx, v.kube, v.storeKind, v.namespace, tokenRef) if err != nil { return "", err } @@ -1536,8 +1492,7 @@ func getJwtString(ctx context.Context, v *client, kubernetesAuth *esv1beta1.Vaul func (v *client) requestTokenWithLdapAuth(ctx context.Context, ldapAuth *esv1beta1.VaultLdapAuth) error { username := strings.TrimSpace(ldapAuth.Username) - - password, err := v.secretKeyRef(ctx, &ldapAuth.SecretRef) + password, err := resolvers.SecretKeyRef(ctx, v.kube, v.storeKind, v.namespace, &ldapAuth.SecretRef) if err != nil { return err } @@ -1556,8 +1511,7 @@ func (v *client) requestTokenWithLdapAuth(ctx context.Context, ldapAuth *esv1bet func (v *client) requestTokenWithUserPassAuth(ctx context.Context, userPassAuth *esv1beta1.VaultUserPassAuth) error { username := strings.TrimSpace(userPassAuth.Username) - - password, err := v.secretKeyRef(ctx, &userPassAuth.SecretRef) + password, err := resolvers.SecretKeyRef(ctx, v.kube, v.storeKind, v.namespace, &userPassAuth.SecretRef) if err != nil { return err } @@ -1579,7 +1533,7 @@ func (v *client) requestTokenWithJwtAuth(ctx context.Context, jwtAuth *esv1beta1 var jwt string var err error if jwtAuth.SecretRef != nil { - jwt, err = v.secretKeyRef(ctx, jwtAuth.SecretRef) + jwt, err = resolvers.SecretKeyRef(ctx, v.kube, v.storeKind, v.namespace, jwtAuth.SecretRef) } else if k8sServiceAccountToken := jwtAuth.KubernetesServiceAccountToken; k8sServiceAccountToken != nil { audiences := k8sServiceAccountToken.Audiences if audiences == nil { @@ -1590,7 +1544,14 @@ func (v *client) requestTokenWithJwtAuth(ctx context.Context, jwtAuth *esv1beta1 tmp := int64(600) expirationSeconds = &tmp } - jwt, err = v.serviceAccountToken(ctx, k8sServiceAccountToken.ServiceAccountRef, *audiences, *expirationSeconds) + jwt, err = createServiceAccountToken( + ctx, + v.corev1, + v.storeKind, + v.namespace, + k8sServiceAccountToken.ServiceAccountRef, + *audiences, + *expirationSeconds) } else { err = fmt.Errorf(errJwtNoTokenSource) } @@ -1618,12 +1579,12 @@ func (v *client) requestTokenWithJwtAuth(ctx context.Context, jwtAuth *esv1beta1 } func (v *client) requestTokenWithCertAuth(ctx context.Context, certAuth *esv1beta1.VaultCertAuth, cfg *vault.Config) error { - clientKey, err := v.secretKeyRef(ctx, &certAuth.SecretRef) + clientKey, err := resolvers.SecretKeyRef(ctx, v.kube, v.storeKind, v.namespace, &certAuth.SecretRef) if err != nil { return err } - clientCert, err := v.secretKeyRef(ctx, &certAuth.ClientCert) + clientCert, err := resolvers.SecretKeyRef(ctx, v.kube, v.storeKind, v.namespace, &certAuth.ClientCert) if err != nil { return err } @@ -1651,7 +1612,40 @@ func (v *client) requestTokenWithCertAuth(ctx context.Context, certAuth *esv1bet return nil } -func (v *client) requestTokenWithIamAuth(ctx context.Context, iamAuth *esv1beta1.VaultIamAuth, ick bool, k kclient.Client, n string, jwtProvider util.JwtProviderFactory, assumeRoler vaultiamauth.STSProvider) error { +func createServiceAccountToken( + ctx context.Context, + corev1Client typedcorev1.CoreV1Interface, + storeKind string, + namespace string, + serviceAccountRef esmeta.ServiceAccountSelector, + additionalAud []string, + expirationSeconds int64) (string, error) { + audiences := serviceAccountRef.Audiences + if len(additionalAud) > 0 { + audiences = append(audiences, additionalAud...) + } + tokenRequest := &authv1.TokenRequest{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: namespace, + }, + Spec: authv1.TokenRequestSpec{ + Audiences: audiences, + ExpirationSeconds: &expirationSeconds, + }, + } + if (storeKind == esv1beta1.ClusterSecretStoreKind) && + (serviceAccountRef.Namespace != nil) { + tokenRequest.Namespace = *serviceAccountRef.Namespace + } + tokenResponse, err := corev1Client.ServiceAccounts(tokenRequest.Namespace). + CreateToken(ctx, serviceAccountRef.Name, tokenRequest, metav1.CreateOptions{}) + if err != nil { + return "", fmt.Errorf(errGetKubeSATokenRequest, serviceAccountRef.Name, err) + } + return tokenResponse.Status.Token, nil +} + +func (v *client) requestTokenWithIamAuth(ctx context.Context, iamAuth *esv1beta1.VaultIamAuth, isClusterKind bool, k kclient.Client, n string, jwtProvider util.JwtProviderFactory, assumeRoler vaultiamauth.STSProvider) error { jwtAuth := iamAuth.JWTAuth secretRefAuth := iamAuth.SecretRef regionAWS := defaultAWSRegion @@ -1665,13 +1659,13 @@ func (v *client) requestTokenWithIamAuth(ctx context.Context, iamAuth *esv1beta1 var creds *credentials.Credentials var err error if jwtAuth != nil { // use credentials from a sa explicitly defined and referenced. Highest preference is given to this method/configuration. - creds, err = vaultiamauth.CredsFromServiceAccount(ctx, *iamAuth, regionAWS, ick, k, n, jwtProvider) + creds, err = vaultiamauth.CredsFromServiceAccount(ctx, *iamAuth, regionAWS, isClusterKind, k, n, jwtProvider) if err != nil { return err } } else if secretRefAuth != nil { // if jwtAuth is not defined, check if secretRef is defined. Second preference. logger.V(1).Info("using credentials from secretRef") - creds, err = vaultiamauth.CredsFromSecretRef(ctx, *iamAuth, ick, k, n) + creds, err = vaultiamauth.CredsFromSecretRef(ctx, *iamAuth, v.storeKind, k, n) if err != nil { return err } diff --git a/pkg/provider/vault/vault_test.go b/pkg/provider/vault/vault_test.go index 1c7302f6f..770c1c668 100644 --- a/pkg/provider/vault/vault_test.go +++ b/pkg/provider/vault/vault_test.go @@ -332,7 +332,7 @@ MIIFkTCCA3mgAwIBAgIUBEUg3m/WqAsWHG4Q/II3IePFfuowDQYJKoZIhvcNAQELBQAwWDELMAkGA1UE kube: clientfake.NewClientBuilder().Build(), }, want: want{ - err: fmt.Errorf(errGetKubeSecret, "vault-secret", "default", errors.New("secrets \"vault-secret\" not found")), + err: fmt.Errorf(`cannot get Kubernetes secret "vault-secret": %w`, errors.New(`secrets "vault-secret" not found`)), }, }, "SuccessfulVaultStoreWithCertAuth": { diff --git a/pkg/provider/webhook/webhook.go b/pkg/provider/webhook/webhook.go index 0d3ad605f..c60e6b16a 100644 --- a/pkg/provider/webhook/webhook.go +++ b/pkg/provider/webhook/webhook.go @@ -25,7 +25,6 @@ import ( "net/http" "net/url" "strconv" - "strings" tpl "text/template" "time" @@ -39,6 +38,7 @@ import ( "github.com/external-secrets/external-secrets/pkg/metrics" "github.com/external-secrets/external-secrets/pkg/template/v2" "github.com/external-secrets/external-secrets/pkg/utils" + "github.com/external-secrets/external-secrets/pkg/utils/resolvers" ) // https://github.com/external-secrets/external-secrets/issues/644 @@ -383,8 +383,9 @@ func (w *WebHook) getCACertPool(provider *esv1beta1.WebhookProvider) (*x509.Cert func (w *WebHook) getCertFromSecret(provider *esv1beta1.WebhookProvider) ([]byte, error) { secretRef := esmeta.SecretKeySelector{ - Name: provider.CAProvider.Name, - Key: provider.CAProvider.Key, + Name: provider.CAProvider.Name, + Namespace: &w.namespace, + Key: provider.CAProvider.Key, } if provider.CAProvider.Namespace != nil { @@ -392,37 +393,18 @@ func (w *WebHook) getCertFromSecret(provider *esv1beta1.WebhookProvider) ([]byte } ctx := context.Background() - res, err := w.secretKeyRef(ctx, &secretRef) + cert, err := resolvers.SecretKeyRef( + ctx, + w.kube, + w.storeKind, + w.namespace, + &secretRef, + ) if err != nil { return nil, err } - return []byte(res), nil -} - -func (w *WebHook) secretKeyRef(ctx context.Context, secretRef *esmeta.SecretKeySelector) (string, error) { - secret := &corev1.Secret{} - ref := client.ObjectKey{ - Namespace: w.namespace, - Name: secretRef.Name, - } - if (w.storeKind == esv1beta1.ClusterSecretStoreKind) && - (secretRef.Namespace != nil) { - ref.Namespace = *secretRef.Namespace - } - err := w.kube.Get(ctx, ref, secret) - if err != nil { - return "", err - } - - keyBytes, ok := secret.Data[secretRef.Key] - if !ok { - return "", err - } - - value := string(keyBytes) - valueStr := strings.TrimSpace(value) - return valueStr, nil + return []byte(cert), nil } func (w *WebHook) getCertFromConfigMap(provider *esv1beta1.WebhookProvider) ([]byte, error) { diff --git a/pkg/provider/yandex/certificatemanager/certificatemanager_test.go b/pkg/provider/yandex/certificatemanager/certificatemanager_test.go index f4a7c248a..2c263a99b 100644 --- a/pkg/provider/yandex/certificatemanager/certificatemanager_test.go +++ b/pkg/provider/yandex/certificatemanager/certificatemanager_test.go @@ -80,7 +80,7 @@ func TestNewClient(t *testing.T) { store.Spec.Provider.YandexCertificateManager.Auth.AuthorizedKey.Name = authorizedKeySecretName store.Spec.Provider.YandexCertificateManager.Auth.AuthorizedKey.Key = authorizedKeySecretKey secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace) - tassert.EqualError(t, err, "could not fetch AuthorizedKey secret: secrets \"authorizedKeySecretName\" not found") + tassert.EqualError(t, err, "cannot get Kubernetes secret \"authorizedKeySecretName\": secrets \"authorizedKeySecretName\" not found") tassert.Nil(t, secretClient) err = createK8sSecret(ctx, t, k8sClient, namespace, authorizedKeySecretName, authorizedKeySecretKey, toJSON(t, newFakeAuthorizedKey())) @@ -95,7 +95,7 @@ func TestNewClient(t *testing.T) { }, } secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace) - tassert.EqualError(t, err, "could not fetch CA certificate secret: secrets \"caCertificateSecretName\" not found") + tassert.EqualError(t, err, "cannot get Kubernetes secret \"caCertificateSecretName\": secrets \"caCertificateSecretName\" not found") tassert.Nil(t, secretClient) err = createK8sSecret(ctx, t, k8sClient, namespace, caCertificateSecretName, caCertificateSecretKey, []byte("it-is-not-a-certificate")) diff --git a/pkg/provider/yandex/common/provider.go b/pkg/provider/yandex/common/provider.go index 974d1fb97..156304fea 100644 --- a/pkg/provider/yandex/common/provider.go +++ b/pkg/provider/yandex/common/provider.go @@ -24,13 +24,12 @@ import ( "github.com/go-logr/logr" "github.com/yandex-cloud/go-sdk/iamkey" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" 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" clock2 "github.com/external-secrets/external-secrets/pkg/provider/yandex/common/clock" + "github.com/external-secrets/external-secrets/pkg/utils/resolvers" ) const maxSecretsClientLifetime = 5 * time.Minute // supposed SecretsClient lifetime is quite short @@ -115,61 +114,36 @@ func (p *YandexCloudProvider) NewClient(ctx context.Context, store esv1beta1.Gen return nil, err } - objectKey := types.NamespacedName{ - Name: input.AuthorizedKey.Name, - Namespace: namespace, - } - - // only ClusterStore is allowed to set namespace (and then it's required) - if store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind { - if input.AuthorizedKey.Namespace == nil { - return nil, fmt.Errorf("invalid ClusterSecretStore: missing AuthorizedKey Namespace") - } - objectKey.Namespace = *input.AuthorizedKey.Namespace - } - - authorizedKeySecret := &corev1.Secret{} - err = kube.Get(ctx, objectKey, authorizedKeySecret) + key, err := resolvers.SecretKeyRef( + ctx, + kube, + store.GetKind(), + namespace, + &input.AuthorizedKey, + ) if err != nil { - return nil, fmt.Errorf("could not fetch AuthorizedKey secret: %w", err) - } - - authorizedKeySecretData := authorizedKeySecret.Data[input.AuthorizedKey.Key] - if (authorizedKeySecretData == nil) || (len(authorizedKeySecretData) == 0) { - return nil, fmt.Errorf("missing AuthorizedKey") + return nil, err } var authorizedKey iamkey.Key - err = json.Unmarshal(authorizedKeySecretData, &authorizedKey) + err = json.Unmarshal([]byte(key), &authorizedKey) if err != nil { return nil, fmt.Errorf("unable to unmarshal authorized key: %w", err) } var caCertificateData []byte - if input.CACertificate != nil { - certObjectKey := types.NamespacedName{ - Name: input.CACertificate.Name, - Namespace: namespace, - } - - if store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind { - if input.CACertificate.Namespace == nil { - return nil, fmt.Errorf("invalid ClusterSecretStore: missing CA certificate Namespace") - } - certObjectKey.Namespace = *input.CACertificate.Namespace - } - - caCertificateSecret := &corev1.Secret{} - err := kube.Get(ctx, certObjectKey, caCertificateSecret) + caCert, err := resolvers.SecretKeyRef( + ctx, + kube, + store.GetKind(), + namespace, + input.CACertificate, + ) if err != nil { - return nil, fmt.Errorf("could not fetch CA certificate secret: %w", err) - } - - caCertificateData = caCertificateSecret.Data[input.CACertificate.Key] - if (caCertificateData == nil) || (len(caCertificateData) == 0) { - return nil, fmt.Errorf("missing CA Certificate") + return nil, err } + caCertificateData = []byte(caCert) } secretGetter, err := p.getOrCreateSecretGetter(ctx, input.APIEndpoint, &authorizedKey, caCertificateData) diff --git a/pkg/provider/yandex/lockbox/lockbox_test.go b/pkg/provider/yandex/lockbox/lockbox_test.go index 25794c9a1..fa8f70cd1 100644 --- a/pkg/provider/yandex/lockbox/lockbox_test.go +++ b/pkg/provider/yandex/lockbox/lockbox_test.go @@ -80,7 +80,7 @@ func TestNewClient(t *testing.T) { store.Spec.Provider.YandexLockbox.Auth.AuthorizedKey.Name = authorizedKeySecretName store.Spec.Provider.YandexLockbox.Auth.AuthorizedKey.Key = authorizedKeySecretKey secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace) - tassert.EqualError(t, err, "could not fetch AuthorizedKey secret: secrets \"authorizedKeySecretName\" not found") + tassert.EqualError(t, err, "cannot get Kubernetes secret \"authorizedKeySecretName\": secrets \"authorizedKeySecretName\" not found") tassert.Nil(t, secretClient) err = createK8sSecret(ctx, t, k8sClient, namespace, authorizedKeySecretName, authorizedKeySecretKey, toJSON(t, newFakeAuthorizedKey())) @@ -95,7 +95,7 @@ func TestNewClient(t *testing.T) { }, } secretClient, err = provider.NewClient(context.Background(), store, k8sClient, namespace) - tassert.EqualError(t, err, "could not fetch CA certificate secret: secrets \"caCertificateSecretName\" not found") + tassert.EqualError(t, err, "cannot get Kubernetes secret \"caCertificateSecretName\": secrets \"caCertificateSecretName\" not found") tassert.Nil(t, secretClient) err = createK8sSecret(ctx, t, k8sClient, namespace, caCertificateSecretName, caCertificateSecretKey, []byte("it-is-not-a-certificate")) diff --git a/pkg/utils/resolvers/secret_ref.go b/pkg/utils/resolvers/secret_ref.go new file mode 100644 index 000000000..5f7843f71 --- /dev/null +++ b/pkg/utils/resolvers/secret_ref.go @@ -0,0 +1,71 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package resolvers + +import ( + "context" + "fmt" + + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "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" +) + +const ( + + // This is used to determine if a store is cluster-scoped or not. + // The EmptyStoreKind is not cluster-scoped, hence resources + // cannot be resolved across namespaces. + // TODO: when we implement cluster-scoped generators + // we can remove this and replace it with a interface. + EmptyStoreKind = "EmptyStoreKind" + + errGetKubeSecret = "cannot get Kubernetes secret %q: %w" + errSecretKeyFmt = "cannot find secret data for key: %q" + errGetKubeSATokenRequest = "cannot request Kubernetes service account token for service account %q: %w" +) + +// SecretKeyRef resolves a metav1.SecretKeySelector and returns the value of the secret it points to. +// A user must pass the namespace of the originating ExternalSecret, as this may differ +// from the namespace defined in the SecretKeySelector. +// This func ensures that only a ClusterSecretStore is able to request secrets across namespaces. +func SecretKeyRef( + ctx context.Context, + c client.Client, + storeKind string, + esNamespace string, + ref *esmeta.SecretKeySelector) (string, error) { + key := types.NamespacedName{ + Namespace: esNamespace, + Name: ref.Name, + } + if (storeKind == esv1beta1.ClusterSecretStoreKind) && + (ref.Namespace != nil) { + key.Namespace = *ref.Namespace + } + secret := &corev1.Secret{} + err := c.Get(ctx, key, secret) + if err != nil { + return "", fmt.Errorf(errGetKubeSecret, ref.Name, err) + } + val, ok := secret.Data[ref.Key] + if !ok { + return "", fmt.Errorf(errSecretKeyFmt, ref.Key) + } + return string(val), nil +} diff --git a/pkg/utils/resolvers/secret_ref_test.go b/pkg/utils/resolvers/secret_ref_test.go new file mode 100644 index 000000000..a3b8fdd2c --- /dev/null +++ b/pkg/utils/resolvers/secret_ref_test.go @@ -0,0 +1,129 @@ +/* +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package resolvers + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/utils/ptr" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + esmeta "github.com/external-secrets/external-secrets/apis/meta/v1" +) + +func TestResolveSecretKeyRef(t *testing.T) { + ctx := context.TODO() + c := fake.NewClientBuilder().WithScheme(scheme.Scheme).Build() + testNamespace := "test-namespace" + testSecret := "test-secret" + testKey := "test-key" + testValue := "test-value" + secret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: testNamespace, + Name: testSecret, + }, + Data: map[string][]byte{ + testKey: []byte(testValue), + }, + } + err := c.Create(ctx, secret) + require.NoError(t, err) + + testCases := []struct { + name string + namespace string + storeKind string + selector *esmeta.SecretKeySelector + expected string + err error + }{ + { + name: "namespaced secret store can access secret in same namespace", + namespace: testNamespace, + storeKind: "SecretStore", + selector: &esmeta.SecretKeySelector{ + Name: testSecret, + Namespace: ptr.To(testNamespace), + Key: testKey, + }, + expected: testValue, + err: nil, + }, + { + name: "omitting namespace in secret store defaults to same namespace", + namespace: testNamespace, + storeKind: "SecretStore", + selector: &esmeta.SecretKeySelector{ + Name: testSecret, + Key: testKey, + }, + expected: testValue, + err: nil, + }, + { + name: "namespaced secret store can not access secret in different namespace", + namespace: "other-namespace", + storeKind: "SecretStore", + selector: &esmeta.SecretKeySelector{ + Name: testSecret, + Namespace: ptr.To(testNamespace), + Key: testKey, + }, + err: errors.New(`cannot get Kubernetes secret "test-secret": secrets "test-secret" not found`), + }, + { + name: "cluster secret store may access all namespaces", + storeKind: "ClusterSecretStore", + selector: &esmeta.SecretKeySelector{ + Name: testSecret, + Namespace: ptr.To(testNamespace), + Key: testKey, + }, + expected: testValue, + err: nil, + }, + { + name: "key not found in secret", + namespace: testNamespace, + storeKind: "SecretStore", + selector: &esmeta.SecretKeySelector{ + Name: testSecret, + Namespace: ptr.To(testNamespace), + Key: "xxxxxxxx", + }, + expected: "", + err: errors.New(`cannot find secret data for key: "xxxxxxxx"`), + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + resolvedValue, err := SecretKeyRef(ctx, c, tc.storeKind, tc.namespace, tc.selector) + if tc.err != nil { + assert.EqualError(t, err, tc.err.Error()) + } else { + require.NoError(t, err) + } + assert.Equal(t, tc.expected, resolvedValue) + }) + } +}