Browse Source

Merge pull request #265 from ricardoptcosta/mtls-auth

feat: adding Vault TLS client authentication
paul-the-alien[bot] 5 years ago
parent
commit
e65658d543

+ 19 - 1
apis/externalsecrets/v1alpha1/secretstore_vault_types.go

@@ -62,7 +62,7 @@ type VaultProvider struct {
 }
 }
 
 
 // VaultAuth is the configuration used to authenticate with a Vault server.
 // VaultAuth is the configuration used to authenticate with a Vault server.
-// Only one of `tokenSecretRef`, `appRole`,  `kubernetes`, `ldap` or `jwt`
+// Only one of `tokenSecretRef`, `appRole`,  `kubernetes`, `ldap`, `jwt` or `cert`
 // can be specified.
 // can be specified.
 type VaultAuth struct {
 type VaultAuth struct {
 	// TokenSecretRef authenticates with Vault by presenting a token.
 	// TokenSecretRef authenticates with Vault by presenting a token.
@@ -88,6 +88,11 @@ type VaultAuth struct {
 	// JWT/OIDC authentication method
 	// JWT/OIDC authentication method
 	// +optional
 	// +optional
 	Jwt *VaultJwtAuth `json:"jwt,omitempty"`
 	Jwt *VaultJwtAuth `json:"jwt,omitempty"`
+
+	// Cert authenticates with TLS Certificates by passing client certificate, private key and ca certificate
+	// Cert authentication method
+	// +optional
+	Cert *VaultCertAuth `json:"cert,omitempty"`
 }
 }
 
 
 // VaultAppRole authenticates with Vault using the App Role auth mechanism,
 // VaultAppRole authenticates with Vault using the App Role auth mechanism,
@@ -161,3 +166,16 @@ type VaultJwtAuth struct {
 	// authenticate with Vault using the JWT/OIDC authentication method
 	// authenticate with Vault using the JWT/OIDC authentication method
 	SecretRef esmeta.SecretKeySelector `json:"secretRef,omitempty"`
 	SecretRef esmeta.SecretKeySelector `json:"secretRef,omitempty"`
 }
 }
+
+// VaultJwtAuth authenticates with Vault using the JWT/OIDC authentication
+// method, with the role name and token stored in a Kubernetes Secret resource.
+type VaultCertAuth struct {
+	// ClientCert is a certificate to authenticate using the Cert Vault
+	// authentication method
+	// +optional
+	ClientCert esmeta.SecretKeySelector `json:"clientCert,omitempty"`
+
+	// SecretRef to a key in a Secret resource containing client private key to
+	// authenticate with Vault using the Cert authentication method
+	SecretRef esmeta.SecretKeySelector `json:"secretRef,omitempty"`
+}

+ 22 - 0
apis/externalsecrets/v1alpha1/zz_generated.deepcopy.go

@@ -833,6 +833,11 @@ func (in *VaultAuth) DeepCopyInto(out *VaultAuth) {
 		*out = new(VaultJwtAuth)
 		*out = new(VaultJwtAuth)
 		(*in).DeepCopyInto(*out)
 		(*in).DeepCopyInto(*out)
 	}
 	}
+	if in.Cert != nil {
+		in, out := &in.Cert, &out.Cert
+		*out = new(VaultCertAuth)
+		(*in).DeepCopyInto(*out)
+	}
 }
 }
 
 
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultAuth.
 // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultAuth.
@@ -845,6 +850,23 @@ func (in *VaultAuth) DeepCopy() *VaultAuth {
 	return out
 	return out
 }
 }
 
 
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *VaultCertAuth) DeepCopyInto(out *VaultCertAuth) {
+	*out = *in
+	in.ClientCert.DeepCopyInto(&out.ClientCert)
+	in.SecretRef.DeepCopyInto(&out.SecretRef)
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VaultCertAuth.
+func (in *VaultCertAuth) DeepCopy() *VaultCertAuth {
+	if in == nil {
+		return nil
+	}
+	out := new(VaultCertAuth)
+	in.DeepCopyInto(out)
+	return out
+}
+
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
 func (in *VaultJwtAuth) DeepCopyInto(out *VaultJwtAuth) {
 func (in *VaultJwtAuth) DeepCopyInto(out *VaultJwtAuth) {
 	*out = *in
 	*out = *in

+ 53 - 0
deploy/crds/external-secrets.io_clustersecretstores.yaml

@@ -359,6 +359,59 @@ spec:
                             - roleId
                             - roleId
                             - secretRef
                             - secretRef
                             type: object
                             type: object
+                          cert:
+                            description: Cert authenticates with TLS Certificates
+                              by passing client certificate, private key and ca certificate
+                              Cert authentication method
+                            properties:
+                              clientCert:
+                                description: ClientCert is a certificate to authenticate
+                                  using the Cert Vault authentication method
+                                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
+                                required:
+                                - name
+                                type: object
+                              secretRef:
+                                description: SecretRef to a key in a Secret resource
+                                  containing client private key to authenticate with
+                                  Vault using the Cert authentication method
+                                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
+                                required:
+                                - name
+                                type: object
+                            type: object
                           jwt:
                           jwt:
                             description: Jwt authenticates with Vault by passing role
                             description: Jwt authenticates with Vault by passing role
                               and JWT token using the JWT/OIDC authentication method
                               and JWT token using the JWT/OIDC authentication method

+ 53 - 0
deploy/crds/external-secrets.io_secretstores.yaml

@@ -359,6 +359,59 @@ spec:
                             - roleId
                             - roleId
                             - secretRef
                             - secretRef
                             type: object
                             type: object
+                          cert:
+                            description: Cert authenticates with TLS Certificates
+                              by passing client certificate, private key and ca certificate
+                              Cert authentication method
+                            properties:
+                              clientCert:
+                                description: ClientCert is a certificate to authenticate
+                                  using the Cert Vault authentication method
+                                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
+                                required:
+                                - name
+                                type: object
+                              secretRef:
+                                description: SecretRef to a key in a Secret resource
+                                  containing client private key to authenticate with
+                                  Vault using the Cert authentication method
+                                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
+                                required:
+                                - name
+                                type: object
+                            type: object
                           jwt:
                           jwt:
                             description: Jwt authenticates with Vault by passing role
                             description: Jwt authenticates with Vault by passing role
                               and JWT token using the JWT/OIDC authentication method
                               and JWT token using the JWT/OIDC authentication method

+ 59 - 2
pkg/provider/vault/vault.go

@@ -16,6 +16,7 @@ package vault
 
 
 import (
 import (
 	"context"
 	"context"
+	"crypto/tls"
 	"crypto/x509"
 	"crypto/x509"
 	"errors"
 	"errors"
 	"fmt"
 	"fmt"
@@ -63,6 +64,8 @@ const (
 
 
 	errGetKubeSecret = "cannot get Kubernetes secret %q: %w"
 	errGetKubeSecret = "cannot get Kubernetes secret %q: %w"
 	errSecretKeyFmt  = "cannot find secret data for key: %q"
 	errSecretKeyFmt  = "cannot find secret data for key: %q"
+
+	errClientTLSAuth = "error from Client TLS Auth: %q"
 )
 )
 
 
 type Client interface {
 type Client interface {
@@ -113,6 +116,7 @@ func (c *connector) NewClient(ctx context.Context, store esv1alpha1.GenericStore
 	}
 	}
 
 
 	cfg, err := vStore.newConfig()
 	cfg, err := vStore.newConfig()
+
 	if err != nil {
 	if err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
@@ -126,7 +130,7 @@ func (c *connector) NewClient(ctx context.Context, store esv1alpha1.GenericStore
 		client.SetNamespace(*vaultSpec.Namespace)
 		client.SetNamespace(*vaultSpec.Namespace)
 	}
 	}
 
 
-	if err := vStore.setAuth(ctx, client); err != nil {
+	if err := vStore.setAuth(ctx, client, cfg); err != nil {
 		return nil, err
 		return nil, err
 	}
 	}
 
 
@@ -231,7 +235,7 @@ func (v *client) newConfig() (*vault.Config, error) {
 	return cfg, nil
 	return cfg, nil
 }
 }
 
 
-func (v *client) setAuth(ctx context.Context, client Client) error {
+func (v *client) setAuth(ctx context.Context, client Client, cfg *vault.Config) error {
 	tokenRef := v.store.Auth.TokenSecretRef
 	tokenRef := v.store.Auth.TokenSecretRef
 	if tokenRef != nil {
 	if tokenRef != nil {
 		token, err := v.secretKeyRef(ctx, tokenRef)
 		token, err := v.secretKeyRef(ctx, tokenRef)
@@ -282,6 +286,16 @@ func (v *client) setAuth(ctx context.Context, client Client) error {
 		return nil
 		return nil
 	}
 	}
 
 
+	certAuth := v.store.Auth.Cert
+	if certAuth != nil {
+		token, err := v.requestTokenWithCertAuth(ctx, client, certAuth, cfg)
+		if err != nil {
+			return err
+		}
+		client.SetToken(token)
+		return nil
+	}
+
 	return errors.New(errAuthFormat)
 	return errors.New(errAuthFormat)
 }
 }
 
 
@@ -538,3 +552,46 @@ func (v *client) requestTokenWithJwtAuth(ctx context.Context, client Client, jwt
 
 
 	return token, nil
 	return token, nil
 }
 }
+
+func (v *client) requestTokenWithCertAuth(ctx context.Context, client Client, certAuth *esv1alpha1.VaultCertAuth, cfg *vault.Config) (string, error) {
+	clientKey, err := v.secretKeyRef(ctx, &certAuth.SecretRef)
+	if err != nil {
+		return "", err
+	}
+
+	clientCert, err := v.secretKeyRef(ctx, &certAuth.ClientCert)
+	if err != nil {
+		return "", err
+	}
+
+	cert, err := tls.X509KeyPair([]byte(clientCert), []byte(clientKey))
+	if err != nil {
+		return "", fmt.Errorf(errClientTLSAuth, err)
+	}
+
+	if transport, ok := cfg.HttpClient.Transport.(*http.Transport); ok {
+		transport.TLSClientConfig.Certificates = []tls.Certificate{cert}
+	}
+
+	url := strings.Join([]string{"/v1", "auth", "cert", "login"}, "/")
+	request := client.NewRequest("POST", url)
+
+	resp, err := client.RawRequestWithContext(ctx, request)
+	if err != nil {
+		return "", fmt.Errorf(errVaultRequest, err)
+	}
+
+	defer resp.Body.Close()
+
+	vaultResult := vault.Secret{}
+	if err = resp.DecodeJSON(&vaultResult); err != nil {
+		return "", fmt.Errorf(errVaultResponse, err)
+	}
+
+	token, err := vaultResult.TokenID()
+	if err != nil {
+		return "", fmt.Errorf(errVaultToken, err)
+	}
+
+	return token, nil
+}

+ 124 - 0
pkg/provider/vault/vault_test.go

@@ -63,6 +63,36 @@ func makeValidSecretStore() *esv1alpha1.SecretStore {
 	}
 	}
 }
 }
 
 
+func makeValidSecretStoreWithCerts() *esv1alpha1.SecretStore {
+	return &esv1alpha1.SecretStore{
+		ObjectMeta: metav1.ObjectMeta{
+			Name:      "vault-store",
+			Namespace: "default",
+		},
+		Spec: esv1alpha1.SecretStoreSpec{
+			Provider: &esv1alpha1.SecretStoreProvider{
+				Vault: &esv1alpha1.VaultProvider{
+					Server:  "vault.example.com",
+					Path:    "secret",
+					Version: esv1alpha1.VaultKVStoreV2,
+					Auth: esv1alpha1.VaultAuth{
+						Cert: &esv1alpha1.VaultCertAuth{
+							ClientCert: esmeta.SecretKeySelector{
+								Name: "tls-auth-certs",
+								Key:  "tls.crt",
+							},
+							SecretRef: esmeta.SecretKeySelector{
+								Name: "tls-auth-certs",
+								Key:  "tls.key",
+							},
+						},
+					},
+				},
+			},
+		},
+	}
+}
+
 type secretStoreTweakFn func(s *esv1alpha1.SecretStore)
 type secretStoreTweakFn func(s *esv1alpha1.SecretStore)
 
 
 func makeSecretStore(tweaks ...secretStoreTweakFn) *esv1alpha1.SecretStore {
 func makeSecretStore(tweaks ...secretStoreTweakFn) *esv1alpha1.SecretStore {
@@ -95,6 +125,13 @@ func newVaultTokenIDResponse(token string) *vault.Response {
 func TestNewVault(t *testing.T) {
 func TestNewVault(t *testing.T) {
 	errBoom := errors.New("boom")
 	errBoom := errors.New("boom")
 	secretData := []byte("some-creds")
 	secretData := []byte("some-creds")
+	secretClientKey := []byte(`-----BEGIN RSA PRIVATE KEY-----
+MIIEpAIBAAKCAQEArfZ4HV1obFVlVNiA24tX/UOakqRnEtWXpIvaOsMaPGvvODgGe4XnyJGO32idPv85sIr7vDH9p+OhactVlJV1fu5SZoZ7pg4jTCLqVDCb3IRD++yik2Sw58YayNe3HiaCTsJQWeMXLzfaqOeyk6bEpBCJo09+3QxUWxijgJ7YZCb+Gi8pf3ZWeSZG+rGNNvXHmTs1Yu1H849SYXu+uJOd/R3ZSTw8CxFe4eTLgbCnPf6tgA8Sg2hc+CAZxunPP2JLZWbiJXxjNRoypso6MAJ1FRkx5sTJiLg6UoLvd95/S/lCVOR2PDlM1hg7ox8VEd4QHky7tLx7gji/5hHQKJQSTwIDAQABAoIBAQCYPICQ8hVX+MNcpLrfZenycR7sBYNOMC0silbH5cUn6yzFfgHuRxi3pOnrCJnTb3cE0BvMbdMVAVdYReD2znSsR9NEdZvvjZ/GGSgH1SIQsI7t//+mDQ/jRLJb4KsXb4vJcLLwdpLrd22bMmhMXjzndrF8gSz8NLX9omozPM8RlLxjzPzYOdlX/Zw8V68qQH2Ic04KbtnCwyAUIgAJxYtn/uYB8lzILBkyzQqwhQKkDDZQ0wbZT0hP6z+HgsdifwQvHG1GZAgCuzzyXrL/4TgDaDhYdMVoBA4+HPmzqm5MkBvjH4oqroxjRofUroVix0OGXZJMI1OJ0z/ubzmwCq5BAoGBANqbwzAydUJs0P+GFL94K/Y6tXULKA2c9N0crbxoxheobRpuJvhpW1ZE/9UGpaYX1Rw3nW4x+Jwvt83YkgHAlR4LgEwDvdJPZobybfqifQDiraUO0t62Crn8mSxOsFCugtRIFniwnX67w3uKxiSdCZYbJGs9JEDTpxRG/PSWq3QlAoGBAMu3zOv1PJAhOky7VcxFxWQPEMY+t2PA/sneD01/qgGuhlTwL4QlpywmBqXcI070dcvcBkP0flnWI7y5cnuE1+55twmsrvfaS8s1+AYje0b35DsaF2vtKuJrXC0AGKP+/eiycd9cbvVW2GWOxE7Ui76Mj95MARK8ZNjt0wJagQhjAoGASm9dD80uhhadN1RFPkjB1054OMk6sx/tdFhug8e9I5MSyzwUguME2aQW5EcmIh7dToVVUo8rUqsgz7NdS8FyRM+vuLJRcQneJDbp4bxwCdwlOh2JCZI8psVutlp4yJATNgrxs9iXV+7BChDflNnvyK+nP+iKrpQiwNHHEdU3vg0CgYEAvEpwD4+loJn1psJn9NxwK6F5IaMKIhtZ4/9pKXpcCh3jb1JouL2MnFOxRVAJGor87aW57Mlol2RDt8W4OM56PqMlOL3xIokUEQka66GT6e5pdu8QwuJ9BrWwhq9WFw4yZQe6FHb836qbbJLegvYVC9QjjZW2UDjtBUwcAkrghH0CgYBUMmMOCwIfMEtMaWxZRGdxRabazLhn7TXhBpVTuv7WouPaXYd7ZGjCTMKAuVa/E4afBlxgemnqBuX90gHpK/dDmn9l+lp8GZey0grJ7G0x5HEMiKziaX5PrgAcKbQ70m9ZNZ1deYhsC05X8rHNexZB6ns7Yms9L7qnlAy51ZH2zw==
+-----END RSA PRIVATE KEY-----`)
+	clientCrt := []byte(`-----BEGIN CERTIFICATE-----
+MIICsTCCAZkCFEJJ4daz5sxkFlzq9n1djLEuG7bmMA0GCSqGSIb3DQEBCwUAMBMxETAPBgNVBAMMCHZhdWx0LWNhMB4XDTIxMDcyMDA4MTQxM1oXDTIyMDcyMDA4MTQxM1owFzEVMBMGA1UEAwwMdmF1bHQtY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArfZ4HV1obFVlVNiA24tX/UOakqRnEtWXpIvaOsMaPGvvODgGe4XnyJGO32idPv85sIr7vDH9p+OhactVlJV1fu5SZoZ7pg4jTCLqVDCb3IRD++yik2Sw58YayNe3HiaCTsJQWeMXLzfaqOeyk6bEpBCJo09+3QxUWxijgJ7YZCb+Gi8pf3ZWeSZG+rGNNvXHmTs1Yu1H849SYXu+uJOd/R3ZSTw8CxFe4eTLgbCnPf6tgA8Sg2hc+CAZxunPP2JLZWbiJXxjNRoypso6MAJ1FRkx5sTJiLg6UoLvd95/S/lCVOR2PDlM1hg7ox8VEd4QHky7tLx7gji/5hHQKJQSTwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQAsDYKtzScIA7bqIOmqF8rr+oLSjRhPt5OfT+KGNdXk8G3VAy1ED2tyCHaRNC7dPLq4EvcxbIXQnXPy1iZMofriGbFPAcQ2fyWUesAD6bYSpI+bYxwz6Ebb93hU5nc/FyXg8yh0kgiGbY3MrACPjxqP2+z5kcOC3u3hx3SZylgW7TeOXDTdqSbNfH1b+1rR/bVNgQQshjhU9d+c4Yv/t0u07uykBhHLWZDSnYiAeOZ8+mWuOSDkcZHE1zznx74fWgtN0zRDtr0L0w9evT9R2CnNSZGxXcEQxAlQ7SL/Jyw82TFCGEw0L4jj7jjvx0N5J8KX/DulUDE9vuVyQEJ88Epe
+-----END CERTIFICATE-----
+`)
 
 
 	type args struct {
 	type args struct {
 		newClientFunc func(c *vault.Config) (Client, error)
 		newClientFunc func(c *vault.Config) (Client, error)
@@ -217,6 +254,93 @@ func TestNewVault(t *testing.T) {
 				err: nil,
 				err: nil,
 			},
 			},
 		},
 		},
+		"SuccessfulVaultStoreWithCertAuth": {
+			reason: "Should return a Vault provider successfully",
+			args: args{
+				store: makeValidSecretStoreWithCerts(),
+				kube: &test.MockClient{
+					MockGet: test.NewMockGetFn(nil, func(obj kclient.Object) error {
+						if o, ok := obj.(*corev1.Secret); ok {
+							o.Data = map[string][]byte{
+								"tls.key": secretClientKey,
+								"tls.crt": clientCrt,
+							}
+							return nil
+						}
+						return nil
+					}),
+				},
+				newClientFunc: func(c *vault.Config) (Client, error) {
+					return &fake.VaultClient{
+						MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
+						MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
+							newVaultTokenIDResponse("test-token"), nil, func(got *vault.Request) error { return nil }),
+						MockSetToken: fake.NewSetTokenFn(),
+					}, nil
+				},
+			},
+			want: want{
+				err: nil,
+			},
+		},
+		"GetCertificateFormatError": {
+			reason: "Should return error if client certificate is in wrong format.",
+			args: args{
+				store: makeValidSecretStoreWithCerts(),
+				kube: &test.MockClient{
+					MockGet: test.NewMockGetFn(nil, func(obj kclient.Object) error {
+						if o, ok := obj.(*corev1.Secret); ok {
+							o.Data = map[string][]byte{
+								"tls.key": secretClientKey,
+								"tls.crt": []byte("cert with mistak"),
+							}
+							return nil
+						}
+						return nil
+					}),
+				},
+				newClientFunc: func(c *vault.Config) (Client, error) {
+					return &fake.VaultClient{
+						MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
+						MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
+							newVaultTokenIDResponse("test-token"), nil, func(got *vault.Request) error { return nil }),
+						MockSetToken: fake.NewSetTokenFn(),
+					}, nil
+				},
+			},
+			want: want{
+				err: fmt.Errorf(errClientTLSAuth, "tls: failed to find any PEM data in certificate input"),
+			},
+		},
+		"GetKeyFormatError": {
+			reason: "Should return error if client key is in wrong format.",
+			args: args{
+				store: makeValidSecretStoreWithCerts(),
+				kube: &test.MockClient{
+					MockGet: test.NewMockGetFn(nil, func(obj kclient.Object) error {
+						if o, ok := obj.(*corev1.Secret); ok {
+							o.Data = map[string][]byte{
+								"tls.key": []byte("key with mistake"),
+								"tls.crt": clientCrt,
+							}
+							return nil
+						}
+						return nil
+					}),
+				},
+				newClientFunc: func(c *vault.Config) (Client, error) {
+					return &fake.VaultClient{
+						MockNewRequest: fake.NewMockNewRequestFn(&vault.Request{}),
+						MockRawRequestWithContext: fake.NewMockRawRequestWithContextFn(
+							newVaultTokenIDResponse("test-token"), nil, func(got *vault.Request) error { return nil }),
+						MockSetToken: fake.NewSetTokenFn(),
+					}, nil
+				},
+			},
+			want: want{
+				err: fmt.Errorf(errClientTLSAuth, "tls: failed to find any PEM data in key input"),
+			},
+		},
 	}
 	}
 
 
 	for name, tc := range cases {
 	for name, tc := range cases {