aws-sdk-go-v2 icon indicating copy to clipboard operation
aws-sdk-go-v2 copied to clipboard

Unable to query SSM Public Parameters

Open hairyhenderson opened this issue 3 years ago • 1 comments

Describe the bug

I've tried to use the SSM service to access public parameters with the ssm.GetParameter method, and it seems impossible.

Expected Behavior

I expect to be able to simply call ssm.GetParameter with a regular SSM client in order to access public SSM parameters.

Here's a sample of how I make the call with the CLI:

$ aws ssm get-parameter --output json --name /aws/service/global-infrastructure/regions/ap-northeast-1
{
    "Parameter": {
        "Name": "/aws/service/global-infrastructure/regions/ap-northeast-1",
        "Type": "String",
        "Value": "ap-northeast-1",
        "Version": 1,
        "LastModifiedDate": "2019-04-08T17:37:38.637000-04:00",
        "ARN": "arn:aws:ssm:us-east-1::parameter/aws/service/global-infrastructure/regions/ap-northeast-1"
    }
}

Current Behavior

I've tried 3 approaches (outlined below in Reproduction Steps), and they result each with 3 different errors:

operation error SSM: GetParameter, https response error StatusCode: 400, RequestID: 5c0b332d-c0a4-4933-a2cd-3c23882e517e, api error UnrecognizedClientException: The security token included in the request is invalid.
operation error SSM: GetParameter, failed to sign request: failed to retrieve credentials: failed to refresh cached credentials, the AnonymousCredentials is not a valid credential provider, and cannot be used to sign AWS requests with
operation error SSM: GetParameter, https response error StatusCode: 400, RequestID: 20a29533-3b03-40a9-b2de-2998692e3535, api error MissingAuthenticationTokenException: Missing Authentication Token

Reproduction Steps

I've tried 3 approaches, and none of them work. Here's a set of Example tests that should be runnable as-is:

package bug

import (
	"context"
	"fmt"

	"github.com/aws/aws-sdk-go-v2/aws"
	"github.com/aws/aws-sdk-go-v2/config"
	"github.com/aws/aws-sdk-go-v2/service/ssm"
)

// Default config, default client
func ExampleGetPublicParameter() {
	ctx := context.Background()

	cfg, err := config.LoadDefaultConfig(ctx)
	if err != nil {
		panic(err)
	}

	client := ssm.NewFromConfig(cfg)

	param, err := client.GetParameter(ctx, &ssm.GetParameterInput{
		Name: aws.String("/aws/service/global-infrastructure/regions/ap-northeast-1"),
	})
	if err != nil {
		fmt.Printf("failed to get parameter: %v", err)
	} else {
		fmt.Printf("value: %s", *param.Parameter.Value)
	}

	// Output:
	// value: ap-northeast-1
}

// Custom config with aws.AnonymousCredentials, default client
func ExampleGetPublicParameter_ConfigWithAnonymousCredentials() {
	ctx := context.Background()

	cfg, err := config.LoadDefaultConfig(ctx, config.WithCredentialsProvider(aws.AnonymousCredentials{}))
	if err != nil {
		panic(err)
	}

	client := ssm.NewFromConfig(cfg)

	param, err := client.GetParameter(ctx, &ssm.GetParameterInput{
		Name: aws.String("/aws/service/global-infrastructure/regions/ap-northeast-1"),
	})
	if err != nil {
		fmt.Printf("failed to get parameter: %v", err)
	} else {
		fmt.Printf("value: %s", *param.Parameter.Value)
	}

	// Output:
	// value: ap-northeast-1
}

// Default config, custom client with aws.AnonymousCredentials
func ExampleGetPublicParameter_ClientWithAnonymousCredentials() {
	ctx := context.Background()

	cfg, err := config.LoadDefaultConfig(ctx)
	if err != nil {
		panic(err)
	}

	client := ssm.NewFromConfig(cfg, func(o *ssm.Options) {
		o.Credentials = aws.AnonymousCredentials{}
	})

	param, err := client.GetParameter(ctx, &ssm.GetParameterInput{
		Name: aws.String("/aws/service/global-infrastructure/regions/ap-northeast-1"),
	})
	if err != nil {
		fmt.Printf("failed to get parameter: %v", err)
	} else {
		fmt.Printf("value: %s", *param.Parameter.Value)
	}

	// Output:
	// value: ap-northeast-1
}

The output is:

$ go test .
--- FAIL: ExampleGetPublicParameter (0.10s)
got:
failed to get parameter: operation error SSM: GetParameter, https response error StatusCode: 400, RequestID: c858d236-ae18-47ef-a314-8ff53e4b404e, api error UnrecognizedClientException: The security token included in the request is invalid.
want:
value: ap-northeast-1
--- FAIL: ExampleGetPublicParameter_ConfigWithAnonymousCredentials (0.00s)
got:
failed to get parameter: operation error SSM: GetParameter, failed to sign request: failed to retrieve credentials: failed to refresh cached credentials, the AnonymousCredentials is not a valid credential provider, and cannot be used to sign AWS requests with
want:
value: ap-northeast-1
--- FAIL: ExampleGetPublicParameter_ClientWithAnonymousCredentials (0.09s)
got:
failed to get parameter: operation error SSM: GetParameter, https response error StatusCode: 400, RequestID: 642d283b-d98e-4880-8aa1-d06812cbd4ff, api error MissingAuthenticationTokenException: Missing Authentication Token
want:
value: ap-northeast-1
FAIL
FAIL    bug        0.332s
FAIL

Possible Solution

No response

Additional Information/Context

There is a similar issue filed relating to S3 (#1797), but it seems there is a workaround for that one (https://github.com/aws/aws-sdk-go-v2/issues/1797#issuecomment-1210534150). That same workaround does not work for SSM (hence the separate issue).

AWS Go SDK V2 Module Versions Used

go.mod:

module bug

go 1.19

require (
	github.com/aws/aws-sdk-go-v2 v1.16.14
	github.com/aws/aws-sdk-go-v2/config v1.17.5
	github.com/aws/aws-sdk-go-v2/service/ssm v1.27.13
)

require (
	github.com/aws/aws-sdk-go-v2/credentials v1.12.18 // indirect
	github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.15 // indirect
	github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.21 // indirect
	github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.15 // indirect
	github.com/aws/aws-sdk-go-v2/internal/ini v1.3.22 // indirect
	github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.15 // indirect
	github.com/aws/aws-sdk-go-v2/service/sso v1.11.21 // indirect
	github.com/aws/aws-sdk-go-v2/service/ssooidc v1.13.3 // indirect
	github.com/aws/aws-sdk-go-v2/service/sts v1.16.17 // indirect
	github.com/aws/smithy-go v1.13.2 // indirect
	github.com/jmespath/go-jmespath v0.4.0 // indirect
)

Compiler and Version used

go version go1.19.1 darwin/amd64

Operating System and version

macOS 12.5.1

hairyhenderson avatar Sep 10 '22 22:09 hairyhenderson

Hi @hairyhenderson ,

Thanks for opening this issue.

I just ran your code against a parameter I have configured in SSM and it works fine.

The first implementation you have there should work. Im curious as to how you set up your credentials? Are you using the shared credentials file? Environment variables? You should also specify a region if its not configured already.

Anon Credentials won't work in this case because you need valid credentials to sign requests to SSM. (Moreover Anon credentials are broken and a fix is pending release)

I suggest you take a look at Configuring the AWS SDK for Go V2 on how to set your credentials. Once you have valid credentials the SDK will sign those requests correctly for you.

Let me know if you need any additional help. Thanks, Ran~

RanVaknin avatar Oct 05 '22 00:10 RanVaknin

I just ran your code against a parameter I have configured in SSM and it works fine.

Was it a public parameter? This issue is specifically about Public Parameters... I'm able to use private user-defined parameters just fine.

The first implementation you have there should work. Im curious as to how you set up your credentials? Are you using the shared credentials file? Environment variables? You should also specify a region if its not configured already.

I don't recall exactly how they were set up at the time (it's been almost a month, and I've moved on to something else), but I think my thought was that it should fall back to using anonymous credentials.

Anon Credentials won't work in this case because you need valid credentials to sign requests to SSM. (Moreover Anon credentials are broken and a fix is pending release)

Ah - so you're saying that public parameters can not be read with anonymous credentials?

Perhaps this is more of a documentation issue then - there's no mention of needing credentials in the public parameters docs, whereas the term "public" implies that it should be available without authentication.

I guess I assumed that because S3 allows the use of anonymous credentials to access public data, the same would be true for SSM public parameters!

hairyhenderson avatar Oct 05 '22 15:10 hairyhenderson

Hi @hairyhenderson ,

Was it a public parameter? This issue is specifically about Public Parameters... I'm able to use private user-defined parameters just fine.

Yes:

func main() {
	cfg, err := config.LoadDefaultConfig(context.TODO(), config.WithRegion("us-east-1"))
	if err != nil {
		log.Fatalf("unable to load SDK config, %v", err)
	}

	client := ssm.NewFromConfig(cfg)

	param, err := client.GetParameter(context.Background(), &ssm.GetParameterInput{
		Name: aws.String("/aws/service/global-infrastructure/regions/ap-northeast-1"),
	})
	if err != nil {
		fmt.Printf("failed to get parameter: %v", err)
	} else {
		fmt.Printf("value: %s", *param.Parameter.Value)
	}
}


// output: value: ap-northeast-1

I don't recall exactly how they were set up at the time (it's been almost a month, and I've moved on to something else),

I'd like to apologize for the slow response. The team is very low on headcount and we are doing are best to get to everyone.

but I think my thought was that it should fall back to using anonymous credentials.

Unfortunately that is not the case. The credential fallback strategy does not include anonymous credentials.

Whether public parameters should be accessible with anonymous credentials is a good question. I'm not sure what was the design decision behind it but I believe this comes down to how the SSM service team handles request from the SDK clients.

Following your example with the CLI : you can disable signing with the --no-sign-request flag, which would be the equivalent of using Anon credentials in the SDK. You can see that the requests is rejected by the service since they expect it to be signed with valid credentials.

$ aws ssm get-parameter --output json --name /aws/service/global-infrastructure/regions/ap-northeast-1  --no-sign-request

An error occurred (MissingAuthenticationTokenException) when calling the GetParameter operation: Missing Authentication Token

I still suspect that your credentials might be configured incorrectly thus resulting in that error. I'd check out the link I provided in my previous comment - that might help solve the problem.

If you need anything else please let me know 😄

Thanks, Ran~

RanVaknin avatar Oct 05 '22 21:10 RanVaknin

This issue has not received a response in 1 week. If you want to keep this issue open, please just leave a comment below and auto-close will be canceled.

github-actions[bot] avatar Oct 08 '22 00:10 github-actions[bot]

@RanVaknin ok, thanks for the response (and apologies for my slow reply - I've been on vacation!)

Whether public parameters should be accessible with anonymous credentials is a good question. I'm not sure what was the design decision behind it but I believe this comes down to how the SSM service team handles request from the SDK clients.

Fair enough.

Ultimately this is a non-obvious/surprising design (given the difference from S3) which is fine, but non-obvious designs like this really need to be more clearly documented.

hairyhenderson avatar Oct 12 '22 20:10 hairyhenderson

⚠️COMMENT VISIBILITY WARNING⚠️

Comments on closed issues are hard for our team to see. If you need more assistance, please either tag a team member or open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.

github-actions[bot] avatar Oct 14 '22 12:10 github-actions[bot]