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

feat: add metadata setting to encode secrets as decoded values (#4535)

* feat: add metadata setting to encode secrets as decoded values

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>

* Update docs/provider/aws-parameter-store.md

Co-authored-by: Gustavo Fernandes de Carvalho <17139678+gusfcarvalho@users.noreply.github.com>
Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>

---------

Signed-off-by: Gergely Brautigam <182850+Skarlso@users.noreply.github.com>
Co-authored-by: Gustavo Fernandes de Carvalho <17139678+gusfcarvalho@users.noreply.github.com>
Gergely Brautigam 1 год назад
Родитель
Сommit
512ebd0800

+ 4 - 3
docs/provider/aws-parameter-store.md

@@ -129,6 +129,7 @@ Optionally, it is possible to configure additional options for the parameter. Th
 - type
 - keyID
 - tier & policies
+- encodeAsDecoded
 
 To control this behaviour you can set the following provider's `metadata`:
 
@@ -138,9 +139,9 @@ To control this behaviour you can set the following provider's `metadata`:
 
 - `secretType` takes three options. `String`, `StringList`, and `SecureString`, where `String` is the _default_
 - `kmsKeyID` takes a KMS Key `$ID` or `$ARN` (in case a key source is created in another account) as a string, where `alias/aws/ssm` is the _default_. This property is only used if `secretType` is set as `SecureString`.
-- 
-
-
+- tier & policies contains advanced policy configs such as `ExpirationNotification`.
+- encodeAsDecoded if set to true will get the secrets and push them as plain values when pushing the entire secret (instead of encoding them)
+instead of base64 encoding the []byte values from the secret.
 
 #### Check successful secret sync
 

+ 22 - 4
pkg/provider/aws/parameterstore/parameterstore.go

@@ -50,9 +50,10 @@ type Tier struct {
 
 // PushSecretMetadataSpec defines the spec for the metadata for PushSecret.
 type PushSecretMetadataSpec struct {
-	SecretType string `json:"secretType,omitempty"`
-	KMSKeyID   string `json:"kmsKeyID,omitempty"`
-	Tier       Tier   `json:"tier,omitempty"`
+	SecretType      string `json:"secretType,omitempty"`
+	KMSKeyID        string `json:"kmsKeyID,omitempty"`
+	Tier            Tier   `json:"tier,omitempty"`
+	EncodeAsDecoded bool   `json:"encodeAsDecoded,omitempty"`
 }
 
 // https://github.com/external-secrets/external-secrets/issues/644
@@ -190,7 +191,7 @@ func (pm *ParameterStore) PushSecret(ctx context.Context, secret *corev1.Secret,
 	key := data.GetSecretKey()
 
 	if key == "" {
-		value, err = utils.JSONMarshal(secret.Data)
+		value, err = pm.encodeSecretData(meta.Spec.EncodeAsDecoded, secret.Data)
 		if err != nil {
 			return fmt.Errorf("failed to serialize secret content as JSON: %w", err)
 		}
@@ -240,6 +241,23 @@ func (pm *ParameterStore) PushSecret(ctx context.Context, secret *corev1.Secret,
 	return pm.setManagedRemoteParameter(ctx, secretRequest, true)
 }
 
+func (pm *ParameterStore) encodeSecretData(encodeAsDecoded bool, data map[string][]byte) ([]byte, error) {
+	if encodeAsDecoded {
+		// This will result in map byte slices not being base64 encoded by json.Marshal.
+		return utils.JSONMarshal(convertMap(data))
+	}
+
+	return utils.JSONMarshal(data)
+}
+
+func convertMap(in map[string][]byte) map[string]string {
+	m := make(map[string]string)
+	for k, v := range in {
+		m[k] = string(v)
+	}
+	return m
+}
+
 func (pm *ParameterStore) setExisting(ctx context.Context, existing *ssm.GetParameterOutput, secretName string, value []byte, secretRequest ssm.PutParameterInput) error {
 	tags, err := pm.getTagsByName(ctx, existing)
 	if err != nil {

+ 41 - 0
pkg/provider/aws/parameterstore/parameterstore_test.go

@@ -646,6 +646,47 @@ func TestPushSecretWithPrefix(t *testing.T) {
 	assert.Equal(t, "/test/this/thing/fake-key", *input.Name)
 }
 
+func TestPushSecretWithoutKeyAndEncodedAsDecodedTrue(t *testing.T) {
+	fakeSecret := &corev1.Secret{
+		Data: map[string][]byte{
+			fakeSecretKey: []byte(fakeValue),
+		},
+	}
+	managedByESO := ssm.Tag{
+		Key:   &managedBy,
+		Value: &externalSecrets,
+	}
+	putParameterOutput := &ssm.PutParameterOutput{}
+	getParameterOutput := &ssm.GetParameterOutput{}
+	describeParameterOutput := &ssm.DescribeParametersOutput{}
+	validListTagsForResourceOutput := &ssm.ListTagsForResourceOutput{
+		TagList: []*ssm.Tag{&managedByESO},
+	}
+
+	client := fakeps.Client{
+		PutParameterWithContextFn:        fakeps.NewPutParameterWithContextFn(putParameterOutput, nil),
+		GetParameterWithContextFn:        fakeps.NewGetParameterWithContextFn(getParameterOutput, nil),
+		DescribeParametersWithContextFn:  fakeps.NewDescribeParametersWithContextFn(describeParameterOutput, nil),
+		ListTagsForResourceWithContextFn: fakeps.NewListTagsForResourceWithContextFn(validListTagsForResourceOutput, nil),
+	}
+
+	psd := fake.PushSecretData{RemoteKey: remoteKey, Metadata: &apiextensionsv1.JSON{Raw: []byte(`
+apiVersion: kubernetes.external-secrets.io/v1alpha1
+kind: PushSecretMetadata
+spec:
+  encodeAsDecoded: true
+`)}}
+	ps := ParameterStore{
+		client: &client,
+		prefix: "/test/this/thing/",
+	}
+	err := ps.PushSecret(context.TODO(), fakeSecret, psd)
+	require.NoError(t, err)
+
+	input := client.PutParameterWithContextFnCalledWith[0][0]
+	assert.Equal(t, "{\"fakeSecretKey\":\"fakeValue\"}", *input.Value)
+}
+
 func TestPushSecretCalledOnlyOnce(t *testing.T) {
 	fakeSecret := &corev1.Secret{
 		Data: map[string][]byte{