Browse Source

Merge pull request #1130 from matt-demers/sm-version-id

Add support for referencing secrets manager secrets by their VersionId
paul-the-alien[bot] 4 years ago
parent
commit
3409023113

+ 50 - 0
docs/provider-aws-secrets-manager.md

@@ -57,4 +57,54 @@ This is an example on how you would look up nested keys in the above json object
 {% include 'aws-sm-external-secret.yaml' %}
 ```
 
+### Secret Versions
+
+SecretsManager creates a new version of a secret every time it is updated. The secret version can be reference in two ways, the `VersionStage` and the `VersionId`. The `VersionId` is a unique uuid which is generated every time the secret changes. This id is immutable and will always refer to the same secret data. The `VersionStage` is an alias to a `VersionId`, and can refer to different secret data as the secret is updated. By default, SecretsManager will add the version stages `AWSCURRENT` and `AWSPREVIOUS` to every secret, but other stages can be created via the [update-secret-version-stage](https://docs.aws.amazon.com/cli/latest/reference/secretsmanager/update-secret-version-stage.html) api.
+
+The `version` field on the `remoteRef` of the ExternalSecret will normally consider the version to be a `VersionStage`, but if the field is prefixed with `uuid/`, then the version will be considered a `VersionId`.
+
+So in this example, the operator will request the secret with `VersionStage` as `AWSPREVIOUS`:
+
+``` yaml
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+  name: example
+spec:
+  refreshInterval: 1h
+  secretStoreRef:
+    name: secretstore-sample
+    kind: SecretStore
+  target:
+    name: secret-to-be-created
+    creationPolicy: Owner
+  data:
+  - secretKey: secret-key-to-be-managed
+    remoteRef:
+      key: "example/secret"
+      version: "AWSPREVIOUS"
+```
+
+While in this example, the operator will request the secret with `VersionId` as `abcd-1234`
+
+``` yaml
+apiVersion: external-secrets.io/v1beta1
+kind: ExternalSecret
+metadata:
+  name: example
+spec:
+  refreshInterval: 1h
+  secretStoreRef:
+    name: secretstore-sample
+    kind: SecretStore
+  target:
+    name: secret-to-be-created
+    creationPolicy: Owner
+  data:
+  - secretKey: secret-key-to-be-managed
+    remoteRef:
+      key: "example/secret"
+      version: "uuid/abcd-1234"
+```
+
 --8<-- "snippets/provider-aws-access.md"

+ 15 - 4
pkg/provider/aws/secretsmanager/secretsmanager.go

@@ -77,10 +77,21 @@ func (sm *SecretsManager) fetch(_ context.Context, ref esv1beta1.ExternalSecretD
 		log.Info("found secret in cache", "key", ref.Key, "version", ver)
 		return secretOut, nil
 	}
-	secretOut, err := sm.client.GetSecretValue(&awssm.GetSecretValueInput{
-		SecretId:     &ref.Key,
-		VersionStage: &ver,
-	})
+
+	var getSecretValueInput *awssm.GetSecretValueInput
+	if strings.HasPrefix(ver, "uuid/") {
+		versionID := strings.TrimPrefix(ver, "uuid/")
+		getSecretValueInput = &awssm.GetSecretValueInput{
+			SecretId:  &ref.Key,
+			VersionId: &versionID,
+		}
+	} else {
+		getSecretValueInput = &awssm.GetSecretValueInput{
+			SecretId:     &ref.Key,
+			VersionStage: &ver,
+		}
+	}
+	secretOut, err := sm.client.GetSecretValue(getSecretValueInput)
 	var nf *awssm.ResourceNotFoundException
 	if errors.As(err, &nf) {
 		return nil, esv1beta1.NoSecretErr

+ 13 - 3
pkg/provider/aws/secretsmanager/secretsmanager_test.go

@@ -155,14 +155,23 @@ func TestSecretsManagerGetSecret(t *testing.T) {
 		smtc.expectedSecret = "nestedval"
 	}
 
-	// good case: custom version set
-	setCustomVersion := func(smtc *secretsManagerTestCase) {
+	// good case: custom version stage set
+	setCustomVersionStage := func(smtc *secretsManagerTestCase) {
 		smtc.apiInput.VersionStage = aws.String("1234")
 		smtc.remoteRef.Version = "1234"
 		smtc.apiOutput.SecretString = aws.String("FOOBA!")
 		smtc.expectedSecret = "FOOBA!"
 	}
 
+	// good case: custom version id set
+	setCustomVersionID := func(smtc *secretsManagerTestCase) {
+		smtc.apiInput.VersionStage = nil
+		smtc.apiInput.VersionId = aws.String("1234-5678")
+		smtc.remoteRef.Version = "uuid/1234-5678"
+		smtc.apiOutput.SecretString = aws.String("myvalue")
+		smtc.expectedSecret = "myvalue"
+	}
+
 	successCases := []*secretsManagerTestCase{
 		makeValidSecretsManagerTestCase(),
 		makeValidSecretsManagerTestCaseCustom(setSecretString),
@@ -173,7 +182,8 @@ func TestSecretsManagerGetSecret(t *testing.T) {
 		makeValidSecretsManagerTestCaseCustom(setSecretBinaryAndSecretStringToNil),
 		makeValidSecretsManagerTestCaseCustom(setNestedSecretValueJSONParsing),
 		makeValidSecretsManagerTestCaseCustom(setSecretValueWithDot),
-		makeValidSecretsManagerTestCaseCustom(setCustomVersion),
+		makeValidSecretsManagerTestCaseCustom(setCustomVersionStage),
+		makeValidSecretsManagerTestCaseCustom(setCustomVersionID),
 		makeValidSecretsManagerTestCaseCustom(setAPIErr),
 	}