Skip to content

Commit

Permalink
feat(configs): vaultCredentialEnvPrefix to support several prefixes (#…
Browse files Browse the repository at this point in the history
…4745)

* feat(configs): vaultCredentialEnvPrefix to support several prefixes

* minor refactoring

* docs

---------

Co-authored-by: Muhammadali Nazarov <[email protected]>
  • Loading branch information
adamko147 and Jk1484 authored Jan 9, 2024
1 parent 32657c4 commit a5ea24d
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 13 deletions.
8 changes: 6 additions & 2 deletions documentation/docs/infrastructure/vault.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,16 @@ The `vaultCredentialPath` parameter is the endpoint of your credential path in V

The `vaultCredentialKeys`parameter is a list of credential IDs. The secret value of the credential will be exposed as an environment variable prefixed by "PIPER_VAULTCREDENTIAL_" and transformed to a valid variable name. For a credential ID named `myAppId` the forwarded environment variable to the step will be `PIPER_VAULTCREDENTIAL_MYAPPID` containing the secret. The Base64 encoded secret value will be exposed as environment variable to the step as `PIPER_VAULTCREDENTIAL_MYAPPID_BASE64`. Hyphens will be replaced by underscores and other non-alphanumeric characters will be removed.

!!! hint "Using a custom prefix for test credentials"
By default the prefix for test credentials is `PIPER_VAULTCREDENTIAL_`.
!!! hint "Using a custom prefix for credentials"
By default the prefix for credentials is `PIPER_VAULTCREDENTIAL_`.

It is possible to use a custom prefix by setting for example `vaultCredentialEnvPrefix: MY_CUSTOM_PREFIX` in your configuration.
With this above credential ID named `myAppId` will be populated into an environment variable with the name `MY_CUSTOM_PREFIX_MYAPPID`.

In case you want to use specific prefix for secrets retrieved from different vault folders, pass multiple prefixes as
`vaultCredentialEnvPrefix: ['MY_CUSTOM_PREFIX_1', 'MY_CUSTOM_PREFIX_2']`.
With this above credential ID named `myAppId1` will be populated into an environment variable with the name `MY_CUSTOM_PREFIX_1_MYAPPID1` and `myAppId2` will be populated into an environment variable with name `MY_CUSTOM_PREFIX_2_MYAPPID2`

Extended logging for Vault secret fetching (e.g. found credentials and environment variable names) can be activated via `verbose: true` configuration.

## Using Vault for test credentials (Deprecated : use general purpose and test credentials as above)
Expand Down
32 changes: 21 additions & 11 deletions pkg/config/vault.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,44 +175,54 @@ func resolveVaultReference(ref *ResourceReference, config *StepConfig, client va

func resolveVaultTestCredentialsWrapper(config *StepConfig, client vaultClient) {
log.Entry().Infof("Resolving test credentials wrapper")
resolveVaultTestCredentialsWrapperBase(config, client, vaultTestCredentialPath, vaultTestCredentialKeys, resolveVaultTestCredentials)
resolveVaultCredentialsWrapperBase(config, client, vaultTestCredentialPath, vaultTestCredentialKeys, vaultTestCredentialEnvPrefix, resolveVaultTestCredentials)
}

func resolveVaultCredentialsWrapper(config *StepConfig, client vaultClient) {
log.Entry().Infof("Resolving credentials wrapper")
resolveVaultTestCredentialsWrapperBase(config, client, vaultCredentialPath, vaultCredentialKeys, resolveVaultCredentials)
resolveVaultCredentialsWrapperBase(config, client, vaultCredentialPath, vaultCredentialKeys, vaultCredentialEnvPrefix, resolveVaultCredentials)
}

func resolveVaultTestCredentialsWrapperBase(
func resolveVaultCredentialsWrapperBase(
config *StepConfig, client vaultClient,
vaultCredPath, vaultCredKeys string,
vaultCredPath, vaultCredKeys, vaultCredEnvPrefix string,
resolveVaultCredentials func(config *StepConfig, client vaultClient),
) {
switch config.Config[vaultCredPath].(type) {
case string:
resolveVaultCredentials(config, client)
case []interface{}:
vaultCredentialPathCopy := config.Config[vaultCredPath]
vaultCredentialKeysCopy := config.Config[vaultCredKeys]
vaultCredentialPathCopy := config.Config[vaultCredPath].([]interface{})
vaultCredentialKeysCopy, keysOk := config.Config[vaultCredKeys].([]interface{})
vaultCredentialEnvPrefixCopy, prefixOk := config.Config[vaultCredEnvPrefix].([]interface{})

if _, ok := vaultCredentialKeysCopy.([]interface{}); !ok {
if !keysOk {
log.Entry().Debugf(" failed, unknown type of keys")
return
}

if len(vaultCredentialKeysCopy.([]interface{})) != len(vaultCredentialPathCopy.([]interface{})) {
if len(vaultCredentialKeysCopy) != len(vaultCredentialPathCopy) {
log.Entry().Debugf(" failed, not same count of values and keys")
return
}

for i := 0; i < len(vaultCredentialPathCopy.([]interface{})); i++ {
config.Config[vaultCredPath] = vaultCredentialPathCopy.([]interface{})[i]
config.Config[vaultCredKeys] = vaultCredentialKeysCopy.([]interface{})[i]
if prefixOk && len(vaultCredentialEnvPrefixCopy) != len(vaultCredentialPathCopy) {
log.Entry().Debugf(" failed, not same count of values and environment prefixes")
return
}

for i := 0; i < len(vaultCredentialPathCopy); i++ {
if prefixOk {
config.Config[vaultCredEnvPrefix] = vaultCredentialEnvPrefixCopy[i]
}
config.Config[vaultCredPath] = vaultCredentialPathCopy[i]
config.Config[vaultCredKeys] = vaultCredentialKeysCopy[i]
resolveVaultCredentials(config, client)
}

config.Config[vaultCredPath] = vaultCredentialPathCopy
config.Config[vaultCredKeys] = vaultCredentialKeysCopy
config.Config[vaultCredEnvPrefix] = vaultCredentialEnvPrefixCopy
default:
log.Entry().Debugf(" failed, unknown type of path")
return
Expand Down
82 changes: 82 additions & 0 deletions pkg/config/vault_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,88 @@ func TestResolveVaultTestCredentialsWrapper(t *testing.T) {
}
})

t.Run("Multiple test credential prefixes", func(t *testing.T) {
t.Parallel()
// init
vaultMock := &mocks.VaultMock{}
envPrefixes := []interface{}{"TEST1_", "TEST2_"}
stepConfig := StepConfig{Config: map[string]interface{}{
"vaultPath": "team1",
"vaultTestCredentialPath": []interface{}{"appCredentials1", "appCredentials2"},
"vaultTestCredentialKeys": []interface{}{[]interface{}{"appUser", "appUserPw"}, []interface{}{"appUser", "appUserPw"}},
"vaultTestCredentialEnvPrefix": envPrefixes,
}}

defer os.Unsetenv("TEST1_APPUSER")
defer os.Unsetenv("TEST1_APPUSERPW")
defer os.Unsetenv("TEST2_APPUSER")
defer os.Unsetenv("TEST2_APPUSERPW")

// mock
vaultData1 := map[string]string{"appUser": "test-user1", "appUserPw": "password1"}
vaultMock.On("GetKvSecret", "team1/appCredentials1").Return(vaultData1, nil)
vaultData2 := map[string]string{"appUser": "test-user2", "appUserPw": "password2"}
vaultMock.On("GetKvSecret", "team1/appCredentials2").Return(vaultData2, nil)

// test
resolveVaultTestCredentialsWrapper(&stepConfig, vaultMock)

// assert
for k, expectedValue := range vaultData1 {
env := envPrefixes[0].(string) + strings.ToUpper(k)
assert.NotEmpty(t, os.Getenv(env))
assert.Equal(t, expectedValue, os.Getenv(env))
}

// assert
for k, expectedValue := range vaultData2 {
env := envPrefixes[1].(string) + strings.ToUpper(k)
assert.NotEmpty(t, os.Getenv(env))
assert.Equal(t, expectedValue, os.Getenv(env))
}
})

t.Run("Multiple custom general purpuse credential environment prefixes", func(t *testing.T) {
t.Parallel()
// init
vaultMock := &mocks.VaultMock{}
envPrefixes := []interface{}{"CUSTOM1_", "CUSTOM2_"}
stepConfig := StepConfig{Config: map[string]interface{}{
"vaultPath": "team1",
"vaultCredentialPath": []interface{}{"appCredentials1", "appCredentials2"},
"vaultCredentialKeys": []interface{}{[]interface{}{"appUser", "appUserPw"}, []interface{}{"appUser", "appUserPw"}},
"vaultCredentialEnvPrefix": envPrefixes,
}}

defer os.Unsetenv("CUSTOM1_APPUSER")
defer os.Unsetenv("CUSTOM1_APPUSERPW")
defer os.Unsetenv("CUSTOM2_APPUSER")
defer os.Unsetenv("CUSTOM2_APPUSERPW")

// mock
vaultData1 := map[string]string{"appUser": "test-user1", "appUserPw": "password1"}
vaultMock.On("GetKvSecret", "team1/appCredentials1").Return(vaultData1, nil)
vaultData2 := map[string]string{"appUser": "test-user2", "appUserPw": "password2"}
vaultMock.On("GetKvSecret", "team1/appCredentials2").Return(vaultData2, nil)

// test
resolveVaultCredentialsWrapper(&stepConfig, vaultMock)

// assert
for k, expectedValue := range vaultData1 {
env := envPrefixes[0].(string) + strings.ToUpper(k)
assert.NotEmpty(t, os.Getenv(env))
assert.Equal(t, expectedValue, os.Getenv(env))
}

// assert
for k, expectedValue := range vaultData2 {
env := envPrefixes[1].(string) + strings.ToUpper(k)
assert.NotEmpty(t, os.Getenv(env))
assert.Equal(t, expectedValue, os.Getenv(env))
}
})

// Test empty and non-empty custom general purpose credential prefix
envPrefixes := []string{"CUSTOM_MYCRED1_", ""}
for idx, envPrefix := range envPrefixes {
Expand Down

0 comments on commit a5ea24d

Please sign in to comment.