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

fix(cli): Fix incorrect parsing of ARNs w/ nested param names #1012

Merged
merged 4 commits into from
May 21, 2020
Merged
Show file tree
Hide file tree
Changes from 2 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
19 changes: 19 additions & 0 deletions ecs-cli/modules/cli/local/secrets/clients/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
package clients

import (
"strings"

"github.com/aws/amazon-ecs-cli/ecs-cli/modules/config"
"github.com/aws/aws-sdk-go/aws"
arnParser "github.com/aws/aws-sdk-go/aws/arn"
Expand All @@ -26,6 +28,11 @@ import (
"github.com/pkg/errors"
)

// ssmSeparator is used to check if ssm parameter names are fully qualified paths
const ssmSeparator = "/"

const splitAllStrings = -1

// region represents an AWS region.
type region string

Expand Down Expand Up @@ -61,6 +68,7 @@ func (d *SSMDecrypter) DecryptSecret(arnOrName string) (string, error) {
paramName := arnOrName
if parsedARN, err := arnParser.Parse(arnOrName); err == nil {
SoManyHs marked this conversation as resolved.
Show resolved Hide resolved
paramName = parsedARN.Resource[len("parameter/"):] // Resource is formatted as parameter/{paramName}.
paramName = formatParam(paramName)
d.SSMAPI = d.getClient(region(parsedARN.Region))
}

Expand All @@ -74,6 +82,17 @@ func (d *SSMDecrypter) DecryptSecret(arnOrName string) (string, error) {
return *val.Parameter.Value, nil
}

// Clean up param if it is hierarchical.
// SSM parameters containing hierarchies must start with "/"
// Leading / is optional for non-hierarchical param names
func formatParam(p string) string {
SoManyHs marked this conversation as resolved.
Show resolved Hide resolved
parts := strings.SplitN(p, ssmSeparator, splitAllStrings)
if len(parts) > 1 {
return "/" + p
}
return p
}

// getClient returns the SSM client for a given region.
// If there is no client available for that region, then creates and caches it.
func (d *SSMDecrypter) getClient(r region) ssmiface.SSMAPI {
Expand Down
31 changes: 31 additions & 0 deletions ecs-cli/modules/cli/local/secrets/clients/clients_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,37 @@ func TestSSMDecrypter_DecryptSecret(t *testing.T) {
}
},
},
"with ARN and forward slashes": {
input: "arn:aws:ssm:us-west-2:11111111111:parameter/TEST/DB/PASSWORD",
wantedSecret: "ponies",
setupDecrypter: func(ctrl *gomock.Controller) *SSMDecrypter {
iadClient := mock_ssmiface.NewMockSSMAPI(ctrl)
pdxClient := mock_ssmiface.NewMockSSMAPI(ctrl)

m := make(map[region]ssmiface.SSMAPI)
m["default"] = iadClient
m["us-east-1"] = iadClient
m["us-west-2"] = pdxClient

gomock.InOrder(
pdxClient.EXPECT().GetParameter(&ssm.GetParameterInput{
Name: aws.String("/TEST/DB/PASSWORD"),
WithDecryption: aws.Bool(true),
}).Return(&ssm.GetParameterOutput{
Parameter: &ssm.Parameter{
Value: aws.String("ponies"),
},
}, nil),

iadClient.EXPECT().GetParameter(gomock.Any()).Times(0), // Should not have called IAD
)

return &SSMDecrypter{
SSMAPI: iadClient,
clients: m,
}
},
},
"without ARN": {
input: "TEST_DB_PASSWORD",
wantedSecret: "hello",
Expand Down
4 changes: 4 additions & 0 deletions ecs-cli/modules/cli/local/secrets/secrets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ func TestName(t *testing.T) {
input: NewContainerSecret("mongodb", "DB_PASSWORD", "arn:aws:secretsmanager:us-east-1:11111111111:secret:alpha/efe/local"),
wanted: "mongodb_DB_PASSWORD",
},
"complex": {
input: NewContainerSecret("mongodb", "DB_PASSWORD", "arn:aws:secretsmanager:us-east-1:11111111111:secret:alpha/efe/local/mongo/aws"),
wanted: "mongodb_DB_PASSWORD",
},
}

for name, tc := range testCases {
Expand Down