Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: AWS provider #120

Merged
merged 7 commits into from
May 14, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
| Local provider | ✅ Implemented |
| [HashiCorp Vault](https://www.vaultproject.io) | ✅ Implemented |
| [OpenBao](https://github.com/openbao/openbao) | ✅ Implemented |
| [AWS Secrets Manager](https://aws.amazon.com/secrets-manager)| Upcoming |
| [AWS Secrets Manager](https://aws.amazon.com/secrets-manager)| ✅ Implemented |

## Getting started

Expand Down
2 changes: 0 additions & 2 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
version: "3.9"

services:
vault:
container_name: secret-init-vault
Expand Down
105 changes: 40 additions & 65 deletions env_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

"github.com/bank-vaults/secret-init/pkg/common"
"github.com/bank-vaults/secret-init/pkg/provider"
"github.com/bank-vaults/secret-init/pkg/provider/aws"
"github.com/bank-vaults/secret-init/pkg/provider/bao"
"github.com/bank-vaults/secret-init/pkg/provider/file"
"github.com/bank-vaults/secret-init/pkg/provider/vault"
Expand All @@ -33,6 +34,7 @@ var supportedProviders = []string{
file.ProviderName,
vault.ProviderName,
bao.ProviderName,
aws.ProviderName,
}

// EnvStore is a helper for managing interactions between environment variables and providers,
Expand All @@ -57,45 +59,45 @@ func NewEnvStore(appConfig *common.Config) *EnvStore {
}
}

// GetProviderPaths returns a map of secret paths for each provider
func (s *EnvStore) GetProviderPaths() map[string][]string {
csatib02 marked this conversation as resolved.
Show resolved Hide resolved
providerPaths := make(map[string][]string)
// GetSecretReferences returns a map of secret key=value pairs for each provider
func (s *EnvStore) GetSecretReferences() map[string][]string {
secretReferences := make(map[string][]string)

for envKey, path := range s.data {
providerName, path := getProviderPath(path)
for envKey, envPath := range s.data {
providerName, envSecretReference := getProviderPath(envPath)
envSecretReference = envKey + "=" + envSecretReference
switch providerName {
case file.ProviderName:
providerPaths[file.ProviderName] = append(providerPaths[file.ProviderName], path)
secretReferences[file.ProviderName] = append(secretReferences[file.ProviderName], envSecretReference)

case vault.ProviderName:
// The injector function expects a map of key:value pairs
path = envKey + "=" + path
providerPaths[vault.ProviderName] = append(providerPaths[vault.ProviderName], path)
secretReferences[vault.ProviderName] = append(secretReferences[vault.ProviderName], envSecretReference)

case bao.ProviderName:
// The injector function expects a map of key:value pairs
path = envKey + "=" + path
providerPaths[bao.ProviderName] = append(providerPaths[bao.ProviderName], path)
secretReferences[bao.ProviderName] = append(secretReferences[bao.ProviderName], envSecretReference)

case aws.ProviderName:
secretReferences[aws.ProviderName] = append(secretReferences[aws.ProviderName], envSecretReference)
}
}

return providerPaths
return secretReferences
}

// LoadProviderSecrets creates a new provider for each detected provider using a specified config.
// It then asynchronously loads secrets using each provider and it's corresponding paths.
// The secrets from each provider are then placed into a map with the provider name as the key.
func (s *EnvStore) LoadProviderSecrets(providerPaths map[string][]string) (map[string][]provider.Secret, error) {
// The secrets from each provider are then placed into a single slice.
func (s *EnvStore) LoadProviderSecrets(ctx context.Context, providerPaths map[string][]string) ([]provider.Secret, error) {
// At most, we will have one error per provider
errCh := make(chan error, len(supportedProviders))
providerSecrets := make(map[string][]provider.Secret)
var providerSecrets []provider.Secret

// Workaround for openBao
// Remove once openBao uses BAO_ADDR in their client, instead of VAULT_ADDR
vaultPaths, ok := providerPaths[vault.ProviderName]
if ok {
var err error
providerSecrets[vault.ProviderName], err = s.workaroundForBao(vaultPaths)
providerSecrets, err = s.workaroundForBao(vaultPaths)
if err != nil {
return nil, fmt.Errorf("failed to workaround for bao: %w", err)
}
Expand All @@ -119,14 +121,14 @@ func (s *EnvStore) LoadProviderSecrets(providerPaths map[string][]string) (map[s
return
}

secrets, err := provider.LoadSecrets(context.Background(), paths)
secrets, err := provider.LoadSecrets(ctx, paths)
if err != nil {
errCh <- fmt.Errorf("failed to load secrets for provider %s: %w", providerName, err)
return
}

mu.Lock()
providerSecrets[providerName] = secrets
providerSecrets = append(providerSecrets, secrets...)
mu.Unlock()
}(providerName, paths, errCh)
}
Expand Down Expand Up @@ -167,25 +169,11 @@ func (s *EnvStore) workaroundForBao(vaultPaths []string) ([]provider.Secret, err
}

// ConvertProviderSecrets converts the loaded secrets to environment variables
func (s *EnvStore) ConvertProviderSecrets(providerSecrets map[string][]provider.Secret) ([]string, error) {
func (s *EnvStore) ConvertProviderSecrets(providerSecrets []provider.Secret) ([]string, error) {
var secretsEnv []string

for providerName, secrets := range providerSecrets {
switch providerName {
case vault.ProviderName, bao.ProviderName:
// The Vault and Bao providers already returns the secrets with the environment variable keys
for _, secret := range secrets {
secretsEnv = append(secretsEnv, fmt.Sprintf("%s=%s", secret.Path, secret.Value))
}

default:
secrets, err := createSecretEnvsFrom(s.data, secrets)
if err != nil {
return nil, fmt.Errorf("failed to create secret environment variables: %w", err)
}

secretsEnv = append(secretsEnv, secrets...)
}
for _, secret := range providerSecrets {
secretsEnv = append(secretsEnv, fmt.Sprintf("%s=%s", secret.Key, secret.Value))
}

return secretsEnv, nil
Expand All @@ -194,24 +182,28 @@ func (s *EnvStore) ConvertProviderSecrets(providerSecrets map[string][]provider.
// Returns the detected provider name and path with removed prefix
func getProviderPath(path string) (string, string) {
if strings.HasPrefix(path, "file:") {
var fileProviderName = file.ProviderName
return fileProviderName, strings.TrimPrefix(path, "file:")
return file.ProviderName, path
}

// If the path contains some string formatted as "vault:{STR}#{STR}"
// it is most probably a vault path
if vault.ProviderEnvRegex.MatchString(path) {
// Do not remove the prefix since it will be processed during injection
return vault.ProviderName, path
}

// If the path contains some string formatted as "bao:{STR}#{STR}"
// it is most probably a vault path
if bao.ProviderEnvRegex.MatchString(path) {
// Do not remove the prefix since it will be processed during injection
return bao.ProviderName, path
}

// Example AWS prefixes:
// arn:aws:secretsmanager:us-west-2:123456789012:secret:my-secret
// arn:aws:ssm:us-west-2:123456789012:parameter/my-parameter
if strings.HasPrefix(path, "arn:aws:secretsmanager:") || strings.HasPrefix(path, "arn:aws:ssm:") {
return aws.ProviderName, path
}

return "", path
}

Expand Down Expand Up @@ -249,33 +241,16 @@ func newProvider(providerName string, appConfig *common.Config) (provider.Provid
}
return provider, nil

default:
return nil, fmt.Errorf("provider %s is not supported", providerName)
}
}

func createSecretEnvsFrom(envs map[string]string, secrets []provider.Secret) ([]string, error) {
// Reverse the map so we can match
// the environment variable key to the secret
// by using the secret path
reversedEnvs := make(map[string]string)
for envKey, path := range envs {
providerName, path := getProviderPath(path)
if providerName != "" {
reversedEnvs[path] = envKey
case aws.ProviderName:
config, err := aws.LoadConfig()
if err != nil {
return nil, fmt.Errorf("failed to create aws config: %w", err)
}
}

var secretsEnv []string
for _, secret := range secrets {
path := secret.Path
key, ok := reversedEnvs[path]
if !ok {
return nil, fmt.Errorf("failed to find environment variable key for secret path: %s", path)
}
provider := aws.NewProvider(config)
return provider, nil

secretsEnv = append(secretsEnv, fmt.Sprintf("%s=%s", key, secret.Value))
default:
return nil, fmt.Errorf("provider %s is not supported", providerName)
}

return secretsEnv, nil
}
Loading
Loading