Browse Source

add AuthRef to kubernetes provider fixes #3627 (#3628)

* add AuthRef to kubernetes provider fixes #3627

Signed-off-by: kaedwen <kaedwen@heinrich.blue>

* run make reviewable

Signed-off-by: kaedwen <kaedwen@heinrich.blue>

* fix validation for given authRef

Signed-off-by: kaedwen <kaedwen@heinrich.blue>

* refactor kubernetes provider auth

Signed-off-by: kaedwen <kaedwen@heinrich.blue>

* satisfy linter

Signed-off-by: kaedwen <kaedwen@heinrich.blue>

* add URL for kubernetes provider tests

Signed-off-by: kaedwen <kaedwen@heinrich.blue>

---------

Signed-off-by: kaedwen <kaedwen@heinrich.blue>
kaedwen 2 years ago
parent
commit
48cccaeded

+ 6 - 0
apis/externalsecrets/v1beta1/secretstore_kubernetes_types.go

@@ -37,11 +37,17 @@ type KubernetesServer struct {
 // Configures a store to sync secrets with a Kubernetes instance.
 type KubernetesProvider struct {
 	// configures the Kubernetes server Address.
+	// +optional
 	Server KubernetesServer `json:"server,omitempty"`
 
 	// Auth configures how secret-manager authenticates with a Kubernetes instance.
+	// +optional
 	Auth KubernetesAuth `json:"auth"`
 
+	// A reference to a secret that contains the auth information.
+	// +optional
+	AuthRef *esmeta.SecretKeySelector `json:"authRef,omitempty"`
+
 	// Remote namespace to fetch the secrets from
 	// +kubebuilder:default= default
 	// +optional

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

@@ -1858,6 +1858,11 @@ func (in *KubernetesProvider) DeepCopyInto(out *KubernetesProvider) {
 	*out = *in
 	in.Server.DeepCopyInto(&out.Server)
 	in.Auth.DeepCopyInto(&out.Auth)
+	if in.AuthRef != nil {
+		in, out := &in.AuthRef, &out.AuthRef
+		*out = new(metav1.SecretKeySelector)
+		(*in).DeepCopyInto(*out)
+	}
 }
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesProvider.

+ 19 - 2
config/crds/bases/external-secrets.io_clustersecretstores.yaml

@@ -3199,6 +3199,25 @@ spec:
                                 type: object
                             type: object
                         type: object
+                      authRef:
+                        description: A reference to a secret that contains the auth
+                          information.
+                        properties:
+                          key:
+                            description: |-
+                              The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
+                              defaulted, in others it may be required.
+                            type: string
+                          name:
+                            description: The name of the Secret resource being referred
+                              to.
+                            type: string
+                          namespace:
+                            description: |-
+                              Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
+                              to the namespace of the referent.
+                            type: string
+                        type: object
                       remoteNamespace:
                         default: default
                         description: Remote namespace to fetch the secrets from
@@ -3242,8 +3261,6 @@ spec:
                             description: configures the Kubernetes server Address.
                             type: string
                         type: object
-                    required:
-                    - auth
                     type: object
                   onboardbase:
                     description: Onboardbase configures this store to sync secrets

+ 19 - 2
config/crds/bases/external-secrets.io_secretstores.yaml

@@ -3199,6 +3199,25 @@ spec:
                                 type: object
                             type: object
                         type: object
+                      authRef:
+                        description: A reference to a secret that contains the auth
+                          information.
+                        properties:
+                          key:
+                            description: |-
+                              The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
+                              defaulted, in others it may be required.
+                            type: string
+                          name:
+                            description: The name of the Secret resource being referred
+                              to.
+                            type: string
+                          namespace:
+                            description: |-
+                              Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
+                              to the namespace of the referent.
+                            type: string
+                        type: object
                       remoteNamespace:
                         default: default
                         description: Remote namespace to fetch the secrets from
@@ -3242,8 +3261,6 @@ spec:
                             description: configures the Kubernetes server Address.
                             type: string
                         type: object
-                    required:
-                    - auth
                     type: object
                   onboardbase:
                     description: Onboardbase configures this store to sync secrets

+ 34 - 4
deploy/crds/bundle.yaml

@@ -3634,6 +3634,23 @@ spec:
                                   type: object
                               type: object
                           type: object
+                        authRef:
+                          description: A reference to a secret that contains the auth information.
+                          properties:
+                            key:
+                              description: |-
+                                The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
+                                defaulted, in others it may be required.
+                              type: string
+                            name:
+                              description: The name of the Secret resource being referred to.
+                              type: string
+                            namespace:
+                              description: |-
+                                Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
+                                to the namespace of the referent.
+                              type: string
+                          type: object
                         remoteNamespace:
                           default: default
                           description: Remote namespace to fetch the secrets from
@@ -3674,8 +3691,6 @@ spec:
                               description: configures the Kubernetes server Address.
                               type: string
                           type: object
-                      required:
-                        - auth
                       type: object
                     onboardbase:
                       description: Onboardbase configures this store to sync secrets using the Onboardbase provider
@@ -9182,6 +9197,23 @@ spec:
                                   type: object
                               type: object
                           type: object
+                        authRef:
+                          description: A reference to a secret that contains the auth information.
+                          properties:
+                            key:
+                              description: |-
+                                The key of the entry in the Secret resource's `data` field to be used. Some instances of this field may be
+                                defaulted, in others it may be required.
+                              type: string
+                            name:
+                              description: The name of the Secret resource being referred to.
+                              type: string
+                            namespace:
+                              description: |-
+                                Namespace of the resource being referred to. Ignored if referent is not cluster-scoped. cluster-scoped defaults
+                                to the namespace of the referent.
+                              type: string
+                          type: object
                         remoteNamespace:
                           default: default
                           description: Remote namespace to fetch the secrets from
@@ -9222,8 +9254,6 @@ spec:
                               description: configures the Kubernetes server Address.
                               type: string
                           type: object
-                      required:
-                        - auth
                       type: object
                     onboardbase:
                       description: Onboardbase configures this store to sync secrets using the Onboardbase provider

+ 16 - 0
docs/api/spec.md

@@ -4864,6 +4864,7 @@ KubernetesServer
 </em>
 </td>
 <td>
+<em>(Optional)</em>
 <p>configures the Kubernetes server Address.</p>
 </td>
 </tr>
@@ -4877,11 +4878,26 @@ KubernetesAuth
 </em>
 </td>
 <td>
+<em>(Optional)</em>
 <p>Auth configures how secret-manager authenticates with a Kubernetes instance.</p>
 </td>
 </tr>
 <tr>
 <td>
+<code>authRef</code></br>
+<em>
+<a href="https://pkg.go.dev/github.com/external-secrets/external-secrets/apis/meta/v1#SecretKeySelector">
+External Secrets meta/v1.SecretKeySelector
+</a>
+</em>
+</td>
+<td>
+<em>(Optional)</em>
+<p>A reference to a secret that contains the auth information.</p>
+</td>
+</tr>
+<tr>
+<td>
 <code>remoteNamespace</code></br>
 <em>
 string

+ 56 - 27
pkg/provider/kubernetes/auth.go

@@ -22,6 +22,8 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/apimachinery/pkg/types"
+	"k8s.io/client-go/rest"
+	"k8s.io/client-go/tools/clientcmd"
 
 	esv1beta1 "github.com/external-secrets/external-secrets/apis/externalsecrets/v1beta1"
 	esmeta "github.com/external-secrets/external-secrets/apis/meta/v1"
@@ -36,35 +38,63 @@ const (
 	errUnableCreateToken                   = "cannot create service account token: %q"
 )
 
-func (c *Client) setAuth(ctx context.Context) error {
-	err := c.setCA(ctx)
+func (c *Client) getAuth(ctx context.Context) (*rest.Config, error) {
+	if c.store.AuthRef != nil {
+		cfg, err := c.fetchSecretKey(ctx, *c.store.AuthRef)
+		if err != nil {
+			return nil, err
+		}
+
+		return clientcmd.RESTConfigFromKubeConfig(cfg)
+	}
+
+	ca, err := c.getCA(ctx)
 	if err != nil {
-		return err
+		return nil, err
 	}
+
+	var token []byte
 	if c.store.Auth.Token != nil {
-		c.BearerToken, err = c.fetchSecretKey(ctx, c.store.Auth.Token.BearerToken)
+		token, err = c.fetchSecretKey(ctx, c.store.Auth.Token.BearerToken)
 		if err != nil {
-			return fmt.Errorf("could not fetch Auth.Token.BearerToken: %w", err)
+			return nil, fmt.Errorf("could not fetch Auth.Token.BearerToken: %w", err)
 		}
-		return nil
-	}
-	if c.store.Auth.ServiceAccount != nil {
-		c.BearerToken, err = c.serviceAccountToken(ctx, c.store.Auth.ServiceAccount)
+	} else if c.store.Auth.ServiceAccount != nil {
+		token, err = c.serviceAccountToken(ctx, c.store.Auth.ServiceAccount)
 		if err != nil {
-			return fmt.Errorf("could not fetch Auth.ServiceAccount: %w", err)
+			return nil, fmt.Errorf("could not fetch Auth.ServiceAccount: %w", err)
 		}
-		return nil
+	} else {
+		return nil, fmt.Errorf("no auth provider given")
 	}
+
+	var key, cert []byte
 	if c.store.Auth.Cert != nil {
-		return c.setClientCert(ctx)
+		key, cert, err = c.getClientKeyAndCert(ctx)
+		if err != nil {
+			return nil, fmt.Errorf("could not fetch client key and cert: %w", err)
+		}
 	}
-	return fmt.Errorf("no credentials provided")
+
+	if c.store.Server.URL == "" {
+		return nil, fmt.Errorf("no server URL provided")
+	}
+
+	return &rest.Config{
+		Host:        c.store.Server.URL,
+		BearerToken: string(token),
+		TLSClientConfig: rest.TLSClientConfig{
+			Insecure: false,
+			CertData: cert,
+			KeyData:  key,
+			CAData:   ca,
+		},
+	}, nil
 }
 
-func (c *Client) setCA(ctx context.Context) error {
+func (c *Client) getCA(ctx context.Context) ([]byte, error) {
 	if c.store.Server.CABundle != nil {
-		c.CA = c.store.Server.CABundle
-		return nil
+		return c.store.Server.CABundle, nil
 	}
 	if c.store.Server.CAProvider != nil {
 		var ca []byte
@@ -78,7 +108,7 @@ func (c *Client) setCA(ctx context.Context) error {
 			}
 			ca, err = c.fetchConfigMapKey(ctx, keySelector)
 			if err != nil {
-				return fmt.Errorf("unable to fetch Server.CAProvider ConfigMap: %w", err)
+				return nil, fmt.Errorf("unable to fetch Server.CAProvider ConfigMap: %w", err)
 			}
 		case esv1beta1.CAProviderTypeSecret:
 			keySelector := esmeta.SecretKeySelector{
@@ -88,26 +118,25 @@ func (c *Client) setCA(ctx context.Context) error {
 			}
 			ca, err = c.fetchSecretKey(ctx, keySelector)
 			if err != nil {
-				return fmt.Errorf("unable to fetch Server.CAProvider Secret: %w", err)
+				return nil, fmt.Errorf("unable to fetch Server.CAProvider Secret: %w", err)
 			}
 		}
-		c.CA = ca
-		return nil
+		return ca, nil
 	}
-	return fmt.Errorf("no Certificate Authority provided")
+	return nil, fmt.Errorf("no Certificate Authority provided")
 }
 
-func (c *Client) setClientCert(ctx context.Context) error {
+func (c *Client) getClientKeyAndCert(ctx context.Context) ([]byte, []byte, error) {
 	var err error
-	c.Certificate, err = c.fetchSecretKey(ctx, c.store.Auth.Cert.ClientCert)
+	cert, err := c.fetchSecretKey(ctx, c.store.Auth.Cert.ClientCert)
 	if err != nil {
-		return fmt.Errorf("unable to fetch client certificate: %w", err)
+		return nil, nil, fmt.Errorf("unable to fetch client certificate: %w", err)
 	}
-	c.Key, err = c.fetchSecretKey(ctx, c.store.Auth.Cert.ClientKey)
+	key, err := c.fetchSecretKey(ctx, c.store.Auth.Cert.ClientKey)
 	if err != nil {
-		return fmt.Errorf("unable to fetch client key: %w", err)
+		return nil, nil, fmt.Errorf("unable to fetch client key: %w", err)
 	}
-	return nil
+	return key, cert, nil
 }
 
 func (c *Client) serviceAccountToken(ctx context.Context, serviceAccountRef *esmeta.ServiceAccountSelector) ([]byte, error) {

+ 185 - 38
pkg/provider/kubernetes/auth_test.go

@@ -22,6 +22,7 @@ import (
 	corev1 "k8s.io/api/core/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
+	"k8s.io/client-go/rest"
 	pointer "k8s.io/utils/ptr"
 	kclient "sigs.k8s.io/controller-runtime/pkg/client"
 	fclient "sigs.k8s.io/controller-runtime/pkg/client/fake"
@@ -31,6 +32,43 @@ import (
 	utilfake "github.com/external-secrets/external-secrets/pkg/provider/util/fake"
 )
 
+const (
+	caCert = `-----BEGIN CERTIFICATE-----
+MIICGTCCAZ+gAwIBAgIQCeCTZaz32ci5PhwLBCou8zAKBggqhkjOPQQDAzBOMQsw
+CQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xJjAkBgNVBAMTHURp
+Z2lDZXJ0IFRMUyBFQ0MgUDM4NCBSb290IEc1MB4XDTIxMDExNTAwMDAwMFoXDTQ2
+MDExNDIzNTk1OVowTjELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ
+bmMuMSYwJAYDVQQDEx1EaWdpQ2VydCBUTFMgRUNDIFAzODQgUm9vdCBHNTB2MBAG
+ByqGSM49AgEGBSuBBAAiA2IABMFEoc8Rl1Ca3iOCNQfN0MsYndLxf3c1TzvdlHJS
+7cI7+Oz6e2tYIOyZrsn8aLN1udsJ7MgT9U7GCh1mMEy7H0cKPGEQQil8pQgO4CLp
+0zVozptjn4S1mU1YoI71VOeVyaNCMEAwHQYDVR0OBBYEFMFRRVBZqz7nLFr6ICIS
+B4CIfBFqMA4GA1UdDwEB/wQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49
+BAMDA2gAMGUCMQCJao1H5+z8blUD2WdsJk6Dxv3J+ysTvLd6jLRl0mlpYxNjOyZQ
+LgGheQaRnUi/wr4CMEfDFXuxoJGZSZOoPHzoRgaLLPIxAJSdYsiJvRmEFOml+wG4
+DXZDjC5Ty3zfDBeWUA==
+-----END CERTIFICATE-----
+`
+	authTestKubeConfig = `apiVersion: v1
+clusters:
+- cluster:
+    server: https://api.my-domain.tld
+    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNHVENDQVorZ0F3SUJBZ0lRQ2VDVFphejMyY2k1UGh3TEJDb3U4ekFLQmdncWhrak9QUVFEQXpCT01Rc3cKQ1FZRFZRUUdFd0pWVXpFWE1CVUdBMVVFQ2hNT1JHbG5hVU5sY25Rc0lFbHVZeTR4SmpBa0JnTlZCQU1USFVScApaMmxEWlhKMElGUk1VeUJGUTBNZ1VETTROQ0JTYjI5MElFYzFNQjRYRFRJeE1ERXhOVEF3TURBd01Gb1hEVFEyCk1ERXhOREl6TlRrMU9Wb3dUakVMTUFrR0ExVUVCaE1DVlZNeEZ6QVZCZ05WQkFvVERrUnBaMmxEWlhKMExDQkoKYm1NdU1TWXdKQVlEVlFRREV4MUVhV2RwUTJWeWRDQlVURk1nUlVORElGQXpPRFFnVW05dmRDQkhOVEIyTUJBRwpCeXFHU000OUFnRUdCU3VCQkFBaUEySUFCTUZFb2M4UmwxQ2EzaU9DTlFmTjBNc1luZEx4ZjNjMVR6dmRsSEpTCjdjSTcrT3o2ZTJ0WUlPeVpyc244YUxOMXVkc0o3TWdUOVU3R0NoMW1NRXk3SDBjS1BHRVFRaWw4cFFnTzRDTHAKMHpWb3pwdGpuNFMxbVUxWW9JNzFWT2VWeWFOQ01FQXdIUVlEVlIwT0JCWUVGTUZSUlZCWnF6N25MRnI2SUNJUwpCNENJZkJGcU1BNEdBMVVkRHdFQi93UUVBd0lCaGpBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUFvR0NDcUdTTTQ5CkJBTURBMmdBTUdVQ01RQ0phbzFINSt6OGJsVUQyV2RzSms2RHh2M0oreXNUdkxkNmpMUmwwbWxwWXhOak95WlEKTGdHaGVRYVJuVWkvd3I0Q01FZkRGWHV4b0pHWlNaT29QSHpvUmdhTExQSXhBSlNkWXNpSnZSbUVGT21sK3dHNApEWFpEakM1VHkzemZEQmVXVUE9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
+  name: mycluster
+contexts:
+- context:
+    cluster: mycluster
+    user: myuser
+  name: mycontext
+current-context: mycontext
+kind: Config
+preferences: {}
+users:
+- name: myuser
+  user:
+    token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE3MTkzOTY4OTksImV4cCI6MTc1MDkzMjg4NywiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.xXrfIl0akhfjWU_BDl7Ad54SXje0YlJdnugzwh96VmM
+`
+)
+
 func TestSetAuth(t *testing.T) {
 	type fields struct {
 		kube          kclient.Client
@@ -39,16 +77,11 @@ func TestSetAuth(t *testing.T) {
 		namespace     string
 		storeKind     string
 	}
-	type want struct {
-		Certificate []byte
-		Key         []byte
-		CA          []byte
-		BearerToken []byte
-	}
+	type want = rest.Config
 	tests := []struct {
 		name    string
 		fields  fields
-		want    want
+		want    *want
 		wantErr bool
 	}{
 		{
@@ -58,7 +91,7 @@ func TestSetAuth(t *testing.T) {
 					Server: esv1beta1.KubernetesServer{},
 				},
 			},
-			want:    want{},
+			want:    nil,
 			wantErr: true,
 		},
 		{
@@ -70,9 +103,7 @@ func TestSetAuth(t *testing.T) {
 					},
 				},
 			},
-			want: want{
-				CA: []byte("1234"),
-			},
+			want:    nil,
 			wantErr: true,
 		},
 		{
@@ -85,29 +116,52 @@ func TestSetAuth(t *testing.T) {
 						Namespace: "default",
 					},
 					Data: map[string][]byte{
-						"cert": []byte("1234"),
+						"cert":  []byte("1234"),
+						"token": []byte("mytoken"),
 					},
 				}).Build(),
 				store: &esv1beta1.KubernetesProvider{
 					Server: esv1beta1.KubernetesServer{
+						URL: "https://my.test.tld",
 						CAProvider: &esv1beta1.CAProvider{
 							Type: esv1beta1.CAProviderTypeSecret,
 							Name: "foobar",
 							Key:  "cert",
 						},
 					},
+					Auth: esv1beta1.KubernetesAuth{
+						Token: &esv1beta1.TokenAuth{
+							BearerToken: v1.SecretKeySelector{
+								Name:      "foobar",
+								Namespace: pointer.To("shouldnotberelevant"),
+								Key:       "token",
+							},
+						},
+					},
 				},
 			},
-			want: want{
-				CA: []byte("1234"),
+			want: &want{
+				Host:        "https://my.test.tld",
+				BearerToken: "mytoken",
+				TLSClientConfig: rest.TLSClientConfig{
+					CAData: []byte("1234"),
+				},
 			},
-			wantErr: true,
+			wantErr: false,
 		},
 		{
 			name: "should fetch ca from ConfigMap",
 			fields: fields{
 				namespace: "default",
-				kube: fclient.NewClientBuilder().WithObjects(&corev1.ConfigMap{
+				kube: fclient.NewClientBuilder().WithObjects(&corev1.Secret{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "foobar",
+						Namespace: "default",
+					},
+					Data: map[string][]byte{
+						"token": []byte("mytoken"),
+					},
+				}, &corev1.ConfigMap{
 					ObjectMeta: metav1.ObjectMeta{
 						Name:      "foobar",
 						Namespace: "default",
@@ -118,18 +172,32 @@ func TestSetAuth(t *testing.T) {
 				}).Build(),
 				store: &esv1beta1.KubernetesProvider{
 					Server: esv1beta1.KubernetesServer{
+						URL: "https://my.test.tld",
 						CAProvider: &esv1beta1.CAProvider{
 							Type: esv1beta1.CAProviderTypeConfigMap,
 							Name: "foobar",
 							Key:  "cert",
 						},
 					},
+					Auth: esv1beta1.KubernetesAuth{
+						Token: &esv1beta1.TokenAuth{
+							BearerToken: v1.SecretKeySelector{
+								Name:      "foobar",
+								Namespace: pointer.To("shouldnotberelevant"),
+								Key:       "token",
+							},
+						},
+					},
 				},
 			},
-			want: want{
-				CA: []byte("1234"),
+			want: &want{
+				Host:        "https://my.test.tld",
+				BearerToken: "mytoken",
+				TLSClientConfig: rest.TLSClientConfig{
+					CAData: []byte("1234"),
+				},
 			},
-			wantErr: true,
+			wantErr: false,
 		},
 		{
 			name: "should set token from secret",
@@ -146,6 +214,7 @@ func TestSetAuth(t *testing.T) {
 				}).Build(),
 				store: &esv1beta1.KubernetesProvider{
 					Server: esv1beta1.KubernetesServer{
+						URL:      "https://my.test.tld",
 						CABundle: []byte("1234"),
 					},
 					Auth: esv1beta1.KubernetesAuth{
@@ -159,9 +228,12 @@ func TestSetAuth(t *testing.T) {
 					},
 				},
 			},
-			want: want{
-				CA:          []byte("1234"),
-				BearerToken: []byte("mytoken"),
+			want: &want{
+				Host:        "https://my.test.tld",
+				BearerToken: "mytoken",
+				TLSClientConfig: rest.TLSClientConfig{
+					CAData: []byte("1234"),
+				},
 			},
 			wantErr: false,
 		},
@@ -178,12 +250,28 @@ func TestSetAuth(t *testing.T) {
 						"cert": []byte("my-cert"),
 						"key":  []byte("my-key"),
 					},
+				}, &corev1.Secret{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "foobar",
+						Namespace: "default",
+					},
+					Data: map[string][]byte{
+						"token": []byte("mytoken"),
+					},
 				}).Build(),
 				store: &esv1beta1.KubernetesProvider{
 					Server: esv1beta1.KubernetesServer{
+						URL:      "https://my.test.tld",
 						CABundle: []byte("1234"),
 					},
 					Auth: esv1beta1.KubernetesAuth{
+						Token: &esv1beta1.TokenAuth{
+							BearerToken: v1.SecretKeySelector{
+								Name:      "foobar",
+								Namespace: pointer.To("shouldnotberelevant"),
+								Key:       "token",
+							},
+						},
 						Cert: &esv1beta1.CertAuth{
 							ClientCert: v1.SecretKeySelector{
 								Name: "mycert",
@@ -197,10 +285,14 @@ func TestSetAuth(t *testing.T) {
 					},
 				},
 			},
-			want: want{
-				CA:          []byte("1234"),
-				Certificate: []byte("my-cert"),
-				Key:         []byte("my-key"),
+			want: &want{
+				Host:        "https://my.test.tld",
+				BearerToken: "mytoken",
+				TLSClientConfig: rest.TLSClientConfig{
+					CAData:   []byte("1234"),
+					CertData: []byte("my-cert"),
+					KeyData:  []byte("my-key"),
+				},
 			},
 			wantErr: false,
 		},
@@ -217,6 +309,7 @@ func TestSetAuth(t *testing.T) {
 				kubeclientset: utilfake.NewCreateTokenMock().WithToken("my-sa-token"),
 				store: &esv1beta1.KubernetesProvider{
 					Server: esv1beta1.KubernetesServer{
+						URL:      "https://my.test.tld",
 						CABundle: []byte("1234"),
 					},
 					Auth: esv1beta1.KubernetesAuth{
@@ -227,9 +320,68 @@ func TestSetAuth(t *testing.T) {
 					},
 				},
 			},
-			want: want{
-				CA:          []byte("1234"),
-				BearerToken: []byte("my-sa-token"),
+			want: &want{
+				Host:        "https://my.test.tld",
+				BearerToken: "my-sa-token",
+				TLSClientConfig: rest.TLSClientConfig{
+					CAData: []byte("1234"),
+				},
+			},
+			wantErr: false,
+		},
+		{
+			name: "should fail with missing URL",
+			fields: fields{
+				namespace: "default",
+				kube: fclient.NewClientBuilder().WithObjects(&corev1.ServiceAccount{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "my-sa",
+						Namespace: "default",
+					},
+				}).Build(),
+				kubeclientset: utilfake.NewCreateTokenMock().WithToken("my-sa-token"),
+				store: &esv1beta1.KubernetesProvider{
+					Server: esv1beta1.KubernetesServer{
+						CABundle: []byte("1234"),
+					},
+					Auth: esv1beta1.KubernetesAuth{
+						ServiceAccount: &v1.ServiceAccountSelector{
+							Name:      "my-sa",
+							Namespace: pointer.To("shouldnotberelevant"),
+						},
+					},
+				},
+			},
+			want:    nil,
+			wantErr: true,
+		},
+		{
+			name: "should read config from secret",
+			fields: fields{
+				namespace: "default",
+				kube: fclient.NewClientBuilder().WithObjects(&corev1.Secret{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "foobar",
+						Namespace: "default",
+					},
+					Data: map[string][]byte{
+						"config": []byte(authTestKubeConfig),
+					},
+				}).Build(),
+				store: &esv1beta1.KubernetesProvider{
+					AuthRef: &v1.SecretKeySelector{
+						Name:      "foobar",
+						Namespace: pointer.To("default"),
+						Key:       "config",
+					},
+				},
+			},
+			want: &want{
+				Host:        "https://api.my-domain.tld",
+				BearerToken: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE3MTkzOTY4OTksImV4cCI6MTc1MDkzMjg4NywiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.xXrfIl0akhfjWU_BDl7Ad54SXje0YlJdnugzwh96VmM",
+				TLSClientConfig: rest.TLSClientConfig{
+					CAData: []byte(caCert),
+				},
 			},
 			wantErr: false,
 		},
@@ -243,17 +395,12 @@ func TestSetAuth(t *testing.T) {
 				namespace:     tt.fields.namespace,
 				storeKind:     tt.fields.storeKind,
 			}
-			if err := k.setAuth(context.Background()); (err != nil) != tt.wantErr {
+			cfg, err := k.getAuth(context.Background())
+			if (err != nil) != tt.wantErr {
 				t.Errorf("BaseClient.setAuth() error = %v, wantErr %v", err, tt.wantErr)
 			}
-			w := want{
-				Certificate: k.Certificate,
-				Key:         k.Key,
-				CA:          k.CA,
-				BearerToken: k.BearerToken,
-			}
-			if !cmp.Equal(w, tt.want) {
-				t.Errorf("unexpected value: expected %#v, got %#v", tt.want, w)
+			if !cmp.Equal(cfg, tt.want) {
+				t.Errorf("unexpected value: expected %#v, got %#v", tt.want, cfg)
 			}
 		})
 	}

+ 5 - 20
pkg/provider/kubernetes/provider.go

@@ -23,7 +23,6 @@ import (
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/kubernetes"
 	typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
-	"k8s.io/client-go/rest"
 	kclient "sigs.k8s.io/controller-runtime/pkg/client"
 	ctrlcfg "sigs.k8s.io/controller-runtime/pkg/client/config"
 
@@ -73,11 +72,7 @@ type Client struct {
 
 	// namespace is the namespace of the
 	// ExternalSecret referencing this provider.
-	namespace   string
-	Certificate []byte
-	Key         []byte
-	CA          []byte
-	BearerToken []byte
+	namespace string
 }
 
 func init() {
@@ -123,22 +118,12 @@ func (p *Provider) newClient(ctx context.Context, store esv1beta1.GenericStore,
 		return client, nil
 	}
 
-	if err := client.setAuth(ctx); err != nil {
-		return nil, err
-	}
-
-	config := &rest.Config{
-		Host:        client.store.Server.URL,
-		BearerToken: string(client.BearerToken),
-		TLSClientConfig: rest.TLSClientConfig{
-			Insecure: false,
-			CertData: client.Certificate,
-			KeyData:  client.Key,
-			CAData:   client.CA,
-		},
+	cfg, err := client.getAuth(ctx)
+	if err != nil {
+		return nil, fmt.Errorf("failed to prepare auth: %w", err)
 	}
 
-	userClientset, err := kubernetes.NewForConfig(config)
+	userClientset, err := kubernetes.NewForConfig(cfg)
 	if err != nil {
 		return nil, fmt.Errorf("error configuring clientset: %w", err)
 	}

+ 55 - 0
pkg/provider/kubernetes/provider_test.go

@@ -51,6 +51,24 @@ mv+AggtK0aRFb9o47z/BypLdk5mhbf3Mmr88C8XBzEnfdYyf4JpTlZrYLBmDCu5d
 9RLLsjXxhag8xqMtd1uLUM8XOTGzVWacw8iGY+CTtBKqyA+AE6/bDwZvEwVtsKtC
 QJ85ioEpy00NioqcF0WyMZH80uMsPycfpnl5uF7RkW8u
 -----END CERTIFICATE-----`
+	testKubeConfig = `apiVersion: v1
+clusters:
+- cluster:
+    server: https://api.my-domain.tld
+  name: mycluster
+contexts:
+- context:
+    cluster: mycluster
+    user: myuser
+  name: mycontext
+current-context: mycontext
+kind: Config
+preferences: {}
+users:
+- name: myuser
+  user:
+    token: eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE3MTkzOTY4OTksImV4cCI6MTc1MDkzMjg4NywiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.xXrfIl0akhfjWU_BDl7Ad54SXje0YlJdnugzwh96VmM
+`
 )
 
 func TestNewClient(t *testing.T) {
@@ -88,6 +106,40 @@ func TestNewClient(t *testing.T) {
 			},
 			wantErr: true,
 		},
+		{
+			name:   "test auth ref",
+			fields: fields{},
+			args: args{
+				store: &esv1beta1.ClusterSecretStore{
+					TypeMeta: metav1.TypeMeta{
+						Kind: esv1beta1.ClusterSecretStoreKind,
+					},
+					Spec: esv1beta1.SecretStoreSpec{
+						Provider: &esv1beta1.SecretStoreProvider{
+							Kubernetes: &esv1beta1.KubernetesProvider{
+								AuthRef: &v1.SecretKeySelector{
+									Name:      "foo",
+									Namespace: pointer.To("default"),
+									Key:       "config",
+								},
+							},
+						},
+					},
+				},
+				namespace: "",
+				kube: fclient.NewClientBuilder().WithObjects(&corev1.Secret{
+					ObjectMeta: metav1.ObjectMeta{
+						Name:      "foo",
+						Namespace: "default",
+					},
+					Data: map[string][]byte{
+						"config": []byte(testKubeConfig),
+					},
+				}).Build(),
+				clientset: clientgofake.NewSimpleClientset(),
+			},
+			want: true,
+		},
 		{
 			name:   "test referent auth return",
 			fields: fields{},
@@ -100,6 +152,7 @@ func TestNewClient(t *testing.T) {
 						Provider: &esv1beta1.SecretStoreProvider{
 							Kubernetes: &esv1beta1.KubernetesProvider{
 								Server: esv1beta1.KubernetesServer{
+									URL:      "https://my.test.tld",
 									CABundle: []byte(testCertificate),
 								},
 								Auth: esv1beta1.KubernetesAuth{
@@ -132,6 +185,7 @@ func TestNewClient(t *testing.T) {
 						Provider: &esv1beta1.SecretStoreProvider{
 							Kubernetes: &esv1beta1.KubernetesProvider{
 								Server: esv1beta1.KubernetesServer{
+									URL:      "https://my.test.tld",
 									CABundle: []byte(testCertificate),
 								},
 								RemoteNamespace: "remote",
@@ -166,6 +220,7 @@ func TestNewClient(t *testing.T) {
 						Provider: &esv1beta1.SecretStoreProvider{
 							Kubernetes: &esv1beta1.KubernetesProvider{
 								Server: esv1beta1.KubernetesServer{
+									URL:      "https://my.test.tld",
 									CABundle: []byte(testCertificate),
 								},
 								RemoteNamespace: "remote",

+ 1 - 1
pkg/provider/kubernetes/validate.go

@@ -31,7 +31,7 @@ import (
 func (p *Provider) ValidateStore(store esv1beta1.GenericStore) (admission.Warnings, error) {
 	storeSpec := store.GetSpec()
 	k8sSpec := storeSpec.Provider.Kubernetes
-	if k8sSpec.Server.CABundle == nil && k8sSpec.Server.CAProvider == nil {
+	if k8sSpec.AuthRef == nil && k8sSpec.Server.CABundle == nil && k8sSpec.Server.CAProvider == nil {
 		return nil, fmt.Errorf("a CABundle or CAProvider is required")
 	}
 	if store.GetObjectKind().GroupVersionKind().Kind == esv1beta1.ClusterSecretStoreKind &&