Просмотр исходного кода

:sparkles: Included metadataPolicy: Fetch in Hashicorp Vault (#2040)

* Implement fetch metadata from Hashicorp Vault (v2)

Signed-off-by: Sebastián Gómez <sebastiangomezcorrea@gmail.com>
Sebastián Gómez 3 лет назад
Родитель
Сommit
5b0ff977de
3 измененных файлов с 138 добавлено и 23 удалено
  1. 14 0
      pkg/provider/vault/fake/vault.go
  2. 45 23
      pkg/provider/vault/vault.go
  3. 79 0
      pkg/provider/vault/vault_test.go

+ 14 - 0
pkg/provider/vault/fake/vault.go

@@ -64,6 +64,20 @@ func NewReadWithContextFn(secret map[string]interface{}, err error) ReadWithData
 	}
 }
 
+func NewReadMetadataWithContextFn(secret map[string]interface{}, err error) ReadWithDataWithContextFn {
+	return func(ctx context.Context, path string, data map[string][]string) (*vault.Secret, error) {
+		if secret == nil {
+			return nil, err
+		}
+		metadata := make(map[string]interface{})
+		metadata["custom_metadata"] = secret
+		vault := &vault.Secret{
+			Data: metadata,
+		}
+		return vault, err
+	}
+}
+
 func NewWriteWithContextFn(secret map[string]interface{}, err error) WriteWithContextFn {
 	return func(ctx context.Context, path string, data map[string]interface{}) (*vault.Secret, error) {
 		vault := &vault.Secret{

+ 45 - 23
pkg/provider/vault/vault.go

@@ -64,26 +64,27 @@ var (
 const (
 	serviceAccTokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token"
 
-	errVaultStore           = "received invalid Vault SecretStore resource: %w"
-	errVaultCacheCreate     = "cannot create Vault client cache: %s"
-	errVaultCacheRemove     = "error removing item from Vault client cache: %w"
-	errVaultCacheEviction   = "unexpected eviction from Vault client cache"
-	errVaultClient          = "cannot setup new vault client: %w"
-	errVaultCert            = "cannot set Vault CA certificate: %w"
-	errReadSecret           = "cannot read secret data from Vault: %w"
-	errAuthFormat           = "cannot initialize Vault client: no valid auth method specified"
-	errInvalidCredentials   = "invalid vault credentials: %w"
-	errDataField            = "failed to find data field"
-	errJSONUnmarshall       = "failed to unmarshall JSON"
-	errPathInvalid          = "provided Path isn't a valid kv v2 path"
-	errSecretFormat         = "secret data not in expected format"
-	errUnexpectedKey        = "unexpected key in data: %s"
-	errVaultToken           = "cannot parse Vault authentication token: %w"
-	errVaultRequest         = "error from Vault request: %w"
-	errServiceAccount       = "cannot read Kubernetes service account token from file system: %w"
-	errJwtNoTokenSource     = "neither `secretRef` nor `kubernetesServiceAccountToken` was supplied as token source for jwt authentication"
-	errUnsupportedKvVersion = "cannot perform find operations with kv version v1"
-	errNotFound             = "secret not found"
+	errVaultStore                   = "received invalid Vault SecretStore resource: %w"
+	errVaultCacheCreate             = "cannot create Vault client cache: %s"
+	errVaultCacheRemove             = "error removing item from Vault client cache: %w"
+	errVaultCacheEviction           = "unexpected eviction from Vault client cache"
+	errVaultClient                  = "cannot setup new vault client: %w"
+	errVaultCert                    = "cannot set Vault CA certificate: %w"
+	errReadSecret                   = "cannot read secret data from Vault: %w"
+	errAuthFormat                   = "cannot initialize Vault client: no valid auth method specified"
+	errInvalidCredentials           = "invalid vault credentials: %w"
+	errDataField                    = "failed to find data field"
+	errJSONUnmarshall               = "failed to unmarshall JSON"
+	errPathInvalid                  = "provided Path isn't a valid kv v2 path"
+	errSecretFormat                 = "secret data not in expected format"
+	errUnexpectedKey                = "unexpected key in data: %s"
+	errVaultToken                   = "cannot parse Vault authentication token: %w"
+	errVaultRequest                 = "error from Vault request: %w"
+	errServiceAccount               = "cannot read Kubernetes service account token from file system: %w"
+	errJwtNoTokenSource             = "neither `secretRef` nor `kubernetesServiceAccountToken` was supplied as token source for jwt authentication"
+	errUnsupportedKvVersion         = "cannot perform find operations with kv version v1"
+	errUnsupportedMetadataKvVersion = "cannot perform metadata fetch operations with kv version v1"
+	errNotFound                     = "secret not found"
 
 	errGetKubeSA             = "cannot get Kubernetes service account %q: %w"
 	errGetKubeSASecrets      = "cannot find secrets bound to service account: %q"
@@ -626,10 +627,31 @@ func (v *client) readSecretMetadata(ctx context.Context, path string) (map[strin
 //  2. get a key from the secret.
 //     Nested values are supported by specifying a gjson expression
 func (v *client) GetSecret(ctx context.Context, ref esv1beta1.ExternalSecretDataRemoteRef) ([]byte, error) {
-	data, err := v.readSecret(ctx, ref.Key, ref.Version)
-	if err != nil {
-		return nil, err
+	var data map[string]interface{}
+	var err error
+	if ref.MetadataPolicy == esv1beta1.ExternalSecretMetadataPolicyFetch {
+		if v.store.Version == esv1beta1.VaultKVStoreV1 {
+			return nil, errors.New(errUnsupportedMetadataKvVersion)
+		}
+
+		metadata, err := v.readSecretMetadata(ctx, ref.Key)
+		if err != nil {
+			return nil, err
+		}
+		if len(metadata) == 0 {
+			return nil, nil
+		}
+		data = make(map[string]interface{}, len(metadata))
+		for k, v := range metadata {
+			data[k] = v
+		}
+	} else {
+		data, err = v.readSecret(ctx, ref.Key, ref.Version)
+		if err != nil {
+			return nil, err
+		}
 	}
+
 	// Return nil if secret value is null
 	if data == nil {
 		return nil, nil

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

@@ -630,6 +630,85 @@ func TestGetSecret(t *testing.T) {
 				err: esv1beta1.NoSecretError{},
 			},
 		},
+		"ReadSecretMetadataWithoutProperty": {
+			reason: "Should return the json encoded metadata",
+			args: args{
+				store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
+				data: esv1beta1.ExternalSecretDataRemoteRef{
+					MetadataPolicy: "Fetch",
+				},
+				vLogical: &fake.Logical{
+					ReadWithDataWithContextFn: fake.NewReadMetadataWithContextFn(secret, nil),
+				},
+			},
+			want: want{
+				err: nil,
+				val: []byte(`{"access_key":"access_key","access_secret":"access_secret"}`),
+			},
+		},
+		"ReadSecretMetadataWithProperty": {
+			reason: "Should return the access_key value from the metadata",
+			args: args{
+				store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
+				data: esv1beta1.ExternalSecretDataRemoteRef{
+					MetadataPolicy: "Fetch",
+					Property:       "access_key",
+				},
+				vLogical: &fake.Logical{
+					ReadWithDataWithContextFn: fake.NewReadMetadataWithContextFn(secret, nil),
+				},
+			},
+			want: want{
+				err: nil,
+				val: []byte("access_key"),
+			},
+		},
+		"FailReadSecretMetadataInvalidProperty": {
+			reason: "Should return error of non existent key inmetadata",
+			args: args{
+				store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
+				data: esv1beta1.ExternalSecretDataRemoteRef{
+					MetadataPolicy: "Fetch",
+					Property:       "does_not_exist",
+				},
+				vLogical: &fake.Logical{
+					ReadWithDataWithContextFn: fake.NewReadMetadataWithContextFn(secret, nil),
+				},
+			},
+			want: want{
+				err: fmt.Errorf(errSecretKeyFmt, "does_not_exist"),
+			},
+		},
+		"FailReadSecretMetadataNoMetadata": {
+			reason: "Should return the access_key value from the metadata",
+			args: args{
+				store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV2).Spec.Provider.Vault,
+				data: esv1beta1.ExternalSecretDataRemoteRef{
+					MetadataPolicy: "Fetch",
+				},
+				vLogical: &fake.Logical{
+					ReadWithDataWithContextFn: fake.NewReadMetadataWithContextFn(nil, nil),
+				},
+			},
+			want: want{
+				err: fmt.Errorf(errNotFound),
+			},
+		},
+		"FailReadSecretMetadataWrongVersion": {
+			reason: "Should return the access_key value from the metadata",
+			args: args{
+				store: makeValidSecretStoreWithVersion(esv1beta1.VaultKVStoreV1).Spec.Provider.Vault,
+				data: esv1beta1.ExternalSecretDataRemoteRef{
+					MetadataPolicy: "Fetch",
+				},
+				vLogical: &fake.Logical{
+					ReadWithDataWithContextFn: fake.NewReadMetadataWithContextFn(nil, nil),
+				},
+			},
+			want: want{
+				err: fmt.Errorf(errUnsupportedMetadataKvVersion),
+			},
+		},
 	}
 
 	for name, tc := range cases {