From ae8f8d4e5afb2a7f2061f041d272c1472985d192 Mon Sep 17 00:00:00 2001 From: Theron Voran Date: Mon, 2 Sep 2024 23:26:30 -0700 Subject: [PATCH] HVS: rotating secret support (#893) --- controllers/hcpvaultsecretsapp_controller.go | 1 + internal/helpers/secrets.go | 20 ++++-- internal/helpers/secrets_test.go | 76 ++++++++++++++++---- 3 files changed, 79 insertions(+), 18 deletions(-) diff --git a/controllers/hcpvaultsecretsapp_controller.go b/controllers/hcpvaultsecretsapp_controller.go index 1a97cda1..b72b5a6a 100644 --- a/controllers/hcpvaultsecretsapp_controller.go +++ b/controllers/hcpvaultsecretsapp_controller.go @@ -124,6 +124,7 @@ func (r *HCPVaultSecretsAppReconciler) Reconcile(ctx context.Context, req ctrl.R AppName: o.Spec.AppName, Types: []string{ helpers.HVSSecretTypeKV, + helpers.HVSSecretTypeRotating, }, } diff --git a/internal/helpers/secrets.go b/internal/helpers/secrets.go index 664d75da..c60c4dc3 100644 --- a/internal/helpers/secrets.go +++ b/internal/helpers/secrets.go @@ -25,8 +25,9 @@ import ( ) const ( - SecretDataKeyRaw = "_raw" - HVSSecretTypeKV = "kv" + SecretDataKeyRaw = "_raw" + HVSSecretTypeKV = "kv" + HVSSecretTypeRotating = "rotating" ) var SecretDataErrorContainsRaw = fmt.Errorf("key '%s' not permitted in Secret data", SecretDataKeyRaw) @@ -497,11 +498,21 @@ func (s *SecretDataBuilder) WithHVSAppSecrets(resp *hvsclient.OpenAppSecretsOK, data := make(map[string][]byte) hasTemplates := len(opt.KeyedTemplates) > 0 for _, v := range p.Secrets { - if v.StaticVersion == nil { + if v.StaticVersion == nil && v.RotatingVersion == nil { continue } - if v.Type != HVSSecretTypeKV { + switch v.Type { + case HVSSecretTypeKV: + secrets[v.Name] = v.StaticVersion.Value + case HVSSecretTypeRotating: + // Since rotating secrets have multiple values, prefix each key with + // the secret name to avoid collisions. + for rvk, rvv := range v.RotatingVersion.Values { + rName := fmt.Sprintf("%s_%s", v.Name, rvk) + secrets[rName] = rvv + } + default: continue } @@ -515,7 +526,6 @@ func (s *SecretDataBuilder) WithHVSAppSecrets(resp *hvsclient.OpenAppSecretsOK, // maps secret name to its secret metadata metadata[v.Name] = m } - secrets[v.Name] = v.StaticVersion.Value } if hasTemplates { diff --git a/internal/helpers/secrets_test.go b/internal/helpers/secrets_test.go index 91defb02..e91fdbd2 100644 --- a/internal/helpers/secrets_test.go +++ b/internal/helpers/secrets_test.go @@ -1304,6 +1304,29 @@ func TestSecretDataBuilder_WithHVSAppSecrets(t *testing.T) { }, Type: HVSSecretTypeKV, }, + { + CreatedAt: strfmt.NewDateTime(), + CreatedByID: "vso-2 uuid", + LatestVersion: 1, + Name: "rotatingfoo", + Provider: "providerfoo", + SyncStatus: nil, + RotatingVersion: &models.Secrets20231128OpenSecretRotatingVersion{ + CreatedAt: strfmt.DateTime{}, + CreatedByID: "vault-secrets-rotator", + ExpiresAt: strfmt.DateTime{}, + Keys: []string{ + "api_key_one", + "api_key_two", + }, + Values: map[string]string{ + "api_key_one": "123456", + "api_key_two": "654321", + }, + Version: 1, + }, + Type: HVSSecretTypeRotating, + }, }, }, } @@ -1366,9 +1389,11 @@ func TestSecretDataBuilder_WithHVSAppSecrets(t *testing.T) { name: "valid", resp: respValid, want: map[string][]byte{ - "bar": []byte("foo"), - "foo": []byte("qux"), - SecretDataKeyRaw: rawValid, + "bar": []byte("foo"), + "foo": []byte("qux"), + "rotatingfoo_api_key_one": []byte("123456"), + "rotatingfoo_api_key_two": []byte("654321"), + SecretDataKeyRaw: rawValid, }, wantErr: assert.NoError, }, @@ -1387,9 +1412,11 @@ func TestSecretDataBuilder_WithHVSAppSecrets(t *testing.T) { }, }, want: map[string][]byte{ - "bar": []byte("FOO"), - "foo": []byte("qux"), - SecretDataKeyRaw: rawValid, + "bar": []byte("FOO"), + "foo": []byte("qux"), + "rotatingfoo_api_key_one": []byte("123456"), + "rotatingfoo_api_key_two": []byte("654321"), + SecretDataKeyRaw: rawValid, }, wantErr: assert.NoError, }, @@ -1428,12 +1455,31 @@ func TestSecretDataBuilder_WithHVSAppSecrets(t *testing.T) { "version": 2 }, "type": "kv" + }, + "rotatingfoo": { + "created_at": "1970-01-01T00:00:00.000Z", + "latest_version": 1, + "name": "rotatingfoo", + "provider": "providerfoo", + "rotating_version": { + "created_at": "0001-01-01T00:00:00.000Z", + "expires_at": "0001-01-01T00:00:00.000Z", + "keys": [ + "api_key_one", + "api_key_two" + ], + "revoked_at": "0001-01-01T00:00:00.000Z", + "version": 1 + }, + "type": "rotating" } }`, ), - "bar": []byte("foo"), - "foo": []byte("qux"), - SecretDataKeyRaw: rawValid, + "bar": []byte("foo"), + "foo": []byte("qux"), + "rotatingfoo_api_key_one": []byte("123456"), + "rotatingfoo_api_key_two": []byte("654321"), + SecretDataKeyRaw: rawValid, }, wantErr: assert.NoError, }, @@ -1465,8 +1511,10 @@ func TestSecretDataBuilder_WithHVSAppSecrets(t *testing.T) { Includes: []string{"foo"}, }, want: map[string][]byte{ - "foo": []byte("qux"), - SecretDataKeyRaw: rawValid, + "foo": []byte("qux"), + "rotatingfoo_api_key_one": []byte("123456"), + "rotatingfoo_api_key_two": []byte("654321"), + SecretDataKeyRaw: rawValid, }, wantErr: assert.NoError, }, @@ -1517,8 +1565,10 @@ func TestSecretDataBuilder_WithHVSAppSecrets(t *testing.T) { ExcludeRaw: true, }, want: map[string][]byte{ - "bar": []byte("foo"), - "foo": []byte("qux"), + "bar": []byte("foo"), + "foo": []byte("qux"), + "rotatingfoo_api_key_one": []byte("123456"), + "rotatingfoo_api_key_two": []byte("654321"), }, wantErr: assert.NoError, },