mirror of
https://github.com/external-secrets/external-secrets.git
synced 2024-12-14 11:57:59 +00:00
* 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>
This commit is contained in:
parent
c6bafe8c61
commit
48cccaeded
11 changed files with 401 additions and 94 deletions
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,15 +285,52 @@ 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,
|
||||
},
|
||||
{
|
||||
name: "should set token from service account",
|
||||
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{
|
||||
URL: "https://my.test.tld",
|
||||
CABundle: []byte("1234"),
|
||||
},
|
||||
Auth: esv1beta1.KubernetesAuth{
|
||||
ServiceAccount: &v1.ServiceAccountSelector{
|
||||
Name: "my-sa",
|
||||
Namespace: pointer.To("shouldnotberelevant"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
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{
|
||||
|
@ -227,9 +352,36 @@ func TestSetAuth(t *testing.T) {
|
|||
},
|
||||
},
|
||||
},
|
||||
want: want{
|
||||
CA: []byte("1234"),
|
||||
BearerToken: []byte("my-sa-token"),
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -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
|
||||
cfg, err := client.getAuth(ctx)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to prepare auth: %w", 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,
|
||||
},
|
||||
}
|
||||
|
||||
userClientset, err := kubernetes.NewForConfig(config)
|
||||
userClientset, err := kubernetes.NewForConfig(cfg)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error configuring clientset: %w", err)
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 &&
|
||||
|
|
Loading…
Reference in a new issue