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

Add support for Vault kvv1 (#3790)

* Squash changes to prep for manual testing

Signed-off-by: Nick Knowlson <nick.knowlson@alayacare.com>

* remove commented out test data

Signed-off-by: Nick Knowlson <nick.knowlson@alayacare.com>

* update e2e test file

Signed-off-by: Nick Knowlson <nick.knowlson@alayacare.com>

---------

Signed-off-by: Nick Knowlson <nick.knowlson@alayacare.com>
Co-authored-by: Gustavo Fernandes de Carvalho <17139678+gusfcarvalho@users.noreply.github.com>
This commit is contained in:
Nick Knowlson 2024-09-17 13:57:08 -07:00 committed by GitHub
parent f76be9fa78
commit 5c22447c13
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 160 additions and 30 deletions

4
.gitattributes vendored Normal file
View file

@ -0,0 +1,4 @@
* text eol=lf
*.png binary
*.jpg binary
*.pfx binary

View file

@ -82,6 +82,8 @@ var _ = Describe("[vault]", Label("vault"), func() {
framework.Compose(withApprole, f, common.DataPropertyDockerconfigJSON, useApproleAuth),
framework.Compose(withApprole, f, common.JSONDataWithoutTargetName, useApproleAuth),
// use v1 provider
framework.Compose(withV1, f, common.FindByName, useV1Provider),
framework.Compose(withV1, f, common.FindByNameAndRewrite, useV1Provider),
framework.Compose(withV1, f, common.JSONDataFromSync, useV1Provider),
framework.Compose(withV1, f, common.JSONDataFromRewrite, useV1Provider),
framework.Compose(withV1, f, common.JSONDataWithProperty, useV1Provider),

View file

@ -27,14 +27,14 @@ import (
)
const (
errUnsupportedKvVersion = "cannot perform find operations with kv version v1"
errUnsupportedKvVersion = "cannot perform find by tag operations with kv version v1"
)
// GetAllSecrets gets multiple secrets from the provider and loads into a kubernetes secret.
// First load all secrets from secretStore path configuration
// Then, gets secrets from a matching name or matching custom_metadata.
func (c *client) GetAllSecrets(ctx context.Context, ref esv1beta1.ExternalSecretFind) (map[string][]byte, error) {
if c.store.Version == esv1beta1.VaultKVStoreV1 {
if c.store.Version == esv1beta1.VaultKVStoreV1 && ref.Tags != nil {
return nil, errors.New(errUnsupportedKvVersion)
}
searchPath := ""

View file

@ -35,7 +35,7 @@ func TestGetAllSecrets(t *testing.T) {
path2Bytes := []byte("{\"access_key\":\"path2\",\"access_secret\":\"path2\"}")
tagBytes := []byte("{\"access_key\":\"unfetched\",\"access_secret\":\"unfetched\"}")
path := "path"
secret := map[string]any{
kv2secret := map[string]any{
"secret1": map[string]any{
"metadata": map[string]any{
"custom_metadata": map[string]any{
@ -116,6 +116,28 @@ func TestGetAllSecrets(t *testing.T) {
},
},
}
kv1secret := map[string]any{
"secret1": map[string]any{
"access_key": "access_key",
"access_secret": "access_secret",
},
"secret2": map[string]any{
"access_key": "access_key2",
"access_secret": "access_secret2",
},
"tag": map[string]any{
"access_key": "unfetched",
"access_secret": "unfetched",
},
"path/1": map[string]any{
"access_key": "path1",
"access_secret": "path1",
},
"path/2": map[string]any{
"access_key": "path2",
"access_secret": "path2",
},
}
type args struct {
store *esv1beta1.VaultProvider
kube kclient.Client
@ -134,13 +156,13 @@ func TestGetAllSecrets(t *testing.T) {
args args
want want
}{
"FindByName": {
reason: "should map multiple secrets matching name",
"FindByNameKv2": {
reason: "should map multiple secrets matching name for kv2",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vLogical: &fake.Logical{
ListWithContextFn: newListWithContextFn(secret),
ReadWithDataWithContextFn: newReadtWithContextFn(secret),
ListWithContextFn: newListWithContextFn(kv2secret),
ReadWithDataWithContextFn: newReadtWithContextFn(kv2secret),
},
data: esv1beta1.ExternalSecretFind{
Name: &esv1beta1.FindName{
@ -156,13 +178,35 @@ func TestGetAllSecrets(t *testing.T) {
},
},
},
"FindByTag": {
"FindByNameKv1": {
reason: "should map multiple secrets matching name for kv1",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
vLogical: &fake.Logical{
ListWithContextFn: newListWithContextKvv1Fn(kv1secret),
ReadWithDataWithContextFn: newReadtWithContextKvv1Fn(kv1secret),
},
data: esv1beta1.ExternalSecretFind{
Name: &esv1beta1.FindName{
RegExp: "secret.*",
},
},
},
want: want{
err: nil,
val: map[string][]byte{
"secret1": secret1Bytes,
"secret2": secret2Bytes,
},
},
},
"FindByTagKv2": {
reason: "should map multiple secrets matching tags",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vLogical: &fake.Logical{
ListWithContextFn: newListWithContextFn(secret),
ReadWithDataWithContextFn: newReadtWithContextFn(secret),
ListWithContextFn: newListWithContextFn(kv2secret),
ReadWithDataWithContextFn: newReadtWithContextFn(kv2secret),
},
data: esv1beta1.ExternalSecretFind{
Tags: map[string]string{
@ -178,13 +222,31 @@ func TestGetAllSecrets(t *testing.T) {
},
},
},
"FilterByPath": {
reason: "should filter secrets based on path",
"FindByTagKv1": {
reason: "find by tag should not work if using kv1 store",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
vLogical: &fake.Logical{
ListWithContextFn: newListWithContextKvv1Fn(kv1secret),
ReadWithDataWithContextFn: newReadtWithContextKvv1Fn(kv1secret),
},
data: esv1beta1.ExternalSecretFind{
Tags: map[string]string{
"foo": "baz",
},
},
},
want: want{
err: errors.New(errUnsupportedKvVersion),
},
},
"FilterByPathKv2WithTags": {
reason: "should filter secrets based on path for kv2 with tags",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vLogical: &fake.Logical{
ListWithContextFn: newListWithContextFn(secret),
ReadWithDataWithContextFn: newReadtWithContextFn(secret),
ListWithContextFn: newListWithContextFn(kv2secret),
ReadWithDataWithContextFn: newReadtWithContextFn(kv2secret),
},
data: esv1beta1.ExternalSecretFind{
Path: &path,
@ -201,22 +263,44 @@ func TestGetAllSecrets(t *testing.T) {
},
},
},
"FailIfKv1": {
reason: "should not work if using kv1 store",
"FilterByPathKv2WithoutTags": {
reason: "should filter secrets based on path for kv2 without tags",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vLogical: &fake.Logical{
ListWithContextFn: newListWithContextFn(secret),
ReadWithDataWithContextFn: newReadtWithContextFn(secret),
ListWithContextFn: newListWithContextFn(kv2secret),
ReadWithDataWithContextFn: newReadtWithContextFn(kv2secret),
},
data: esv1beta1.ExternalSecretFind{
Tags: map[string]string{
"foo": "baz",
},
Path: &path,
},
},
want: want{
err: errors.New(errUnsupportedKvVersion),
err: nil,
val: map[string][]byte{
"path/1": path1Bytes,
"path/2": path2Bytes,
},
},
},
"FilterByPathKv1": {
reason: "should filter secrets based on path for kv1",
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
vLogical: &fake.Logical{
ListWithContextFn: newListWithContextKvv1Fn(kv1secret),
ReadWithDataWithContextFn: newReadtWithContextKvv1Fn(kv1secret),
},
data: esv1beta1.ExternalSecretFind{
Path: &path,
},
},
want: want{
err: nil,
val: map[string][]byte{
"path/1": path1Bytes,
"path/2": path2Bytes,
},
},
},
"MetadataNotFound": {
@ -224,7 +308,7 @@ func TestGetAllSecrets(t *testing.T) {
args: args{
store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
vLogical: &fake.Logical{
ListWithContextFn: newListWithContextFn(secret),
ListWithContextFn: newListWithContextFn(kv2secret),
ReadWithDataWithContextFn: func(ctx context.Context, path string, d map[string][]string) (*vault.Secret, error) {
return nil, nil
},
@ -251,10 +335,10 @@ func TestGetAllSecrets(t *testing.T) {
}
val, err := vStore.GetAllSecrets(context.Background(), tc.args.data)
if diff := cmp.Diff(tc.want.err, err, EquateErrors()); diff != "" {
t.Errorf("\n%s\nvault.GetSecretMap(...): -want error, +got error:\n%s", tc.reason, diff)
t.Errorf("\n%s\nvault.GetAllSecrets(...): -want error, +got error:\n%s", tc.reason, diff)
}
if diff := cmp.Diff(tc.want.val, val); diff != "" {
t.Errorf("\n%s\nvault.GetSecretMap(...): -want val, +got val:\n%s", tc.reason, diff)
t.Errorf("\n%s\nvault.GetAllSecrets(...): -want val, +got val:\n%s", tc.reason, diff)
}
})
}
@ -262,10 +346,11 @@ func TestGetAllSecrets(t *testing.T) {
func newListWithContextFn(secrets map[string]any) func(ctx context.Context, path string) (*vault.Secret, error) {
return func(ctx context.Context, path string) (*vault.Secret, error) {
path = strings.TrimPrefix(path, "secret/metadata/")
path = strings.TrimPrefix(path, "secret/metadata/") // kvv2
if path == "" {
path = "default"
}
data, ok := secrets[path]
if !ok {
return nil, errors.New("Secret not found")
@ -281,13 +366,35 @@ func newListWithContextFn(secrets map[string]any) func(ctx context.Context, path
}
}
func newListWithContextKvv1Fn(secrets map[string]any) func(ctx context.Context, path string) (*vault.Secret, error) {
return func(ctx context.Context, path string) (*vault.Secret, error) {
path = strings.TrimPrefix(path, "secret/")
keys := make([]any, 0, len(secrets))
for k := range secrets {
if strings.HasPrefix(k, path) {
uniqueSuffix := strings.TrimPrefix(k, path)
keys = append(keys, uniqueSuffix)
}
}
if len(keys) == 0 {
return nil, errors.New("Secret not found")
}
secret := &vault.Secret{
Data: map[string]any{
"keys": keys,
},
}
return secret, nil
}
}
func newReadtWithContextFn(secrets map[string]any) func(ctx context.Context, path string, data map[string][]string) (*vault.Secret, error) {
return func(ctx context.Context, path string, d map[string][]string) (*vault.Secret, error) {
path = strings.TrimPrefix(path, "secret/data/")
path = strings.TrimPrefix(path, "secret/metadata/")
if path == "" {
path = "default"
}
data, ok := secrets[path]
if !ok {
return nil, errors.New("Secret not found")
@ -304,3 +411,20 @@ func newReadtWithContextFn(secrets map[string]any) func(ctx context.Context, pat
return secret, nil
}
}
func newReadtWithContextKvv1Fn(secrets map[string]any) func(ctx context.Context, path string, data map[string][]string) (*vault.Secret, error) {
return func(ctx context.Context, path string, d map[string][]string) (*vault.Secret, error) {
path = strings.TrimPrefix(path, "secret/")
data, ok := secrets[path]
if !ok {
return nil, errors.New("Secret not found")
}
dataAsMap := data.(map[string]any)
secret := &vault.Secret{
Data: dataAsMap,
}
return secret, nil
}
}