Skip to content

Commit

Permalink
Support raw/plain secret pull from AWS Secret Manager
Browse files Browse the repository at this point in the history
  • Loading branch information
obabec committed Nov 27, 2024
1 parent 3c943cc commit 1a67ec3
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 7 deletions.
22 changes: 21 additions & 1 deletion template/funcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,5 +117,25 @@ func GetAWSSecret(name, key string) (string, error) {
Key: key,
}

return client.GetSecret(spec)
return client.GetSecret(spec, false)
}

func GetRawAWSSecret(name string) (string, error) {
if len(name) == 0 {
return "", errors.New("At least one secret name must be provided")
}

// client uses AWS SDK CredentialChain method. So,credentials can
// be loaded from credential file, environment variables, or IAM
// roles.
client := awssmapi.New(
&awssmapi.AWSConfig{},
)

spec := &awssmapi.SecretSpec{
Name: name,
Key: "",
}

return client.GetSecret(spec, true)
}
10 changes: 5 additions & 5 deletions template/interpolate/aws/secretsmanager/secretsmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ func (c *Client) newSession(config *AWSConfig) *session.Session {

// GetSecret return an AWS Secret Manager secret
// in plain text from a given secret name
func (c *Client) GetSecret(spec *SecretSpec) (string, error) {
func (c *Client) GetSecret(spec *SecretSpec, raw bool) (string, error) {
params := &secretsmanager.GetSecretValueInput{
SecretId: aws.String(spec.Name),
VersionStage: aws.String("AWSCURRENT"),
Expand All @@ -71,20 +71,20 @@ func (c *Client) GetSecret(spec *SecretSpec) (string, error) {
Name: *resp.Name,
SecretString: *resp.SecretString,
}
value, err := getSecretValue(&secret, spec)
value, err := getSecretValue(&secret, spec, raw)
if err != nil {
return "", err
}

return value, nil
}

func getSecretValue(s *SecretString, spec *SecretSpec) (string, error) {
func getSecretValue(s *SecretString, spec *SecretSpec, raw bool) (string, error) {
var secretValue map[string]interface{}
blob := []byte(s.SecretString)

//For those plaintext secrets just return the value
if !json.Valid(blob) {
//For those plaintext secrets just return the value or if raw is requested
if !json.Valid(blob) || raw {
return s.SecretString, nil
}

Expand Down
33 changes: 32 additions & 1 deletion template/interpolate/aws/secretsmanager/secretsmanager_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,15 @@ func TestGetSecret(t *testing.T) {
testCases := []struct {
description string
arg *SecretSpec
raw bool
mock secretsmanager.GetSecretValueOutput
want string
ok bool
}{
{
description: "input has valid secret name, secret has single key",
arg: &SecretSpec{Name: "test/secret"},
raw: false,
mock: secretsmanager.GetSecretValueOutput{
Name: aws.String("test/secret"),
SecretString: aws.String(`{"key": "test"}`),
Expand All @@ -45,6 +47,7 @@ func TestGetSecret(t *testing.T) {
Name: "test/secret",
Key: "key",
},
raw: false,
mock: secretsmanager.GetSecretValueOutput{
Name: aws.String("test/secret"),
SecretString: aws.String(`{"key": "test"}`),
Expand All @@ -58,6 +61,7 @@ func TestGetSecret(t *testing.T) {
Name: "test/secret",
Key: "second_key",
},
raw: false,
mock: secretsmanager.GetSecretValueOutput{
Name: aws.String("test/secret"),
SecretString: aws.String(`{"first_key": "first_val", "second_key": "second_val"}`),
Expand All @@ -70,6 +74,7 @@ func TestGetSecret(t *testing.T) {
arg: &SecretSpec{
Name: "test/secret",
},
raw: false,
mock: secretsmanager.GetSecretValueOutput{
Name: aws.String("test/secret"),
SecretString: aws.String(`{"first_key": "first_val", "second_key": "second_val"}`),
Expand All @@ -82,6 +87,7 @@ func TestGetSecret(t *testing.T) {
Name: "test/secret",
Key: "nonexistent",
},
raw: false,
mock: secretsmanager.GetSecretValueOutput{
Name: aws.String("test/secret"),
SecretString: aws.String(`{"key": "test"}`),
Expand All @@ -94,6 +100,7 @@ func TestGetSecret(t *testing.T) {
Name: "test/secret",
Key: "nonexistent",
},
raw: false,
mock: secretsmanager.GetSecretValueOutput{
Name: aws.String("test/secret"),
SecretString: aws.String(`{"first_key": "first_val", "second_key": "second_val"}`),
Expand All @@ -106,6 +113,7 @@ func TestGetSecret(t *testing.T) {
Name: "test/secret",
Key: "nonexistent",
},
raw: false,
mock: secretsmanager.GetSecretValueOutput{},
ok: false,
},
Expand All @@ -114,6 +122,7 @@ func TestGetSecret(t *testing.T) {
arg: &SecretSpec{
Name: "test",
},
raw: false,
mock: secretsmanager.GetSecretValueOutput{
Name: aws.String("test"),
SecretString: aws.String("ThisIsThePassword"),
Expand All @@ -124,20 +133,42 @@ func TestGetSecret(t *testing.T) {
{
description: "input as secret stored with 'String: int' value",
arg: &SecretSpec{Name: "test"},
raw: false,
mock: secretsmanager.GetSecretValueOutput{
Name: aws.String("test"),
SecretString: aws.String(`{"port": 5432}`),
},
want: "5432",
ok: true,
},
{
description: "input as secret stored as json object, returned as json",
arg: &SecretSpec{Name: "test"},
raw: true,
mock: secretsmanager.GetSecretValueOutput{
Name: aws.String("test"),
SecretString: aws.String(`{"foo":{"bar":"baz"}}`),
},
want: `{"foo":{"bar":"baz"}}`,
ok: true,
},
{
description: "input as secret stored as json with object, fails without raw",
arg: &SecretSpec{Name: "test"},
raw: false,
mock: secretsmanager.GetSecretValueOutput{
Name: aws.String("test"),
SecretString: aws.String(`{"foo":{"bar":"baz"}}`),
},
ok: false,
},
}

for _, test := range testCases {
c := &Client{
api: mockedSecret{Resp: test.mock},
}
got, err := c.GetSecret(test.arg)
got, err := c.GetSecret(test.arg, test.raw)
if test.ok {
if got != test.want {
t.Fatalf("want %v, got %v, error %v, using arg %v", test.want, got, err, test.arg)
Expand Down

0 comments on commit 1a67ec3

Please sign in to comment.