aws-samples / sigv4a-signing-examples

MIT No Attribution
55 stars 23 forks source link

Golang examples? #3

Open jwineinger opened 1 year ago

jwineinger commented 1 year ago

Do you have any golang examples that can be added here? thanks!

lovaarte commented 1 year ago

Hi @jwineinger , ack. I'll see to provide examples shortly. Thanks for opening it.

brittandeyoung commented 1 year ago

@lovaarte I Would like to second the need for golang examples. I currently have a need to use IAM auth with multi region apigateway deployments, but am unable to find any examples on the internet.

jwineinger commented 1 year ago

I contributed a PR to Open Policy Agent that added v4a signing for S3 multi-region access points. It lifts most of the v4a signing code from the AWS SDK because OPA didn't want to bring the entire SDK in as a dependency.

https://github.com/open-policy-agent/opa/pull/5489/files is the PR, and https://github.com/open-policy-agent/opa/blob/main/internal/providers/aws/signing_v4a.go#L383-L410 might be a good entrypoint to start at for the actual signing mechanics.

lovaarte commented 1 year ago

Thanks everyone for your feedback, I'll prioritize to get a working code. @jwineinger thanks for the pointer, this is as in your case as there is no go AWS SDK that does it.

jwineinger commented 1 year ago

https://github.com/aws/aws-sdk-go-v2/issues/1935

The AWS SDK go v2 SDK has internal code to do it, which is where I pulled from.

brittandeyoung commented 1 year ago

@jwineinger Thank you, I have been looking at your examples and seeing if i can make sense of it. Using your example I have been able to successfully sign a request, but am getting access denied when attempting to access apigateway. I am able to access this same apigateway when using sigv4. @lovaarte I am wondering if apigateway supports v4a yet?

lovaarte commented 1 year ago

@brittandeyoung assuming you tested in a language that have sample code. I just tested and it works for me. Make sure you pass correct config. Here is what I used:

service = 'execute-api'
region = '*' # Also works with 'global'
method = 'GET'
url = 'https://{YOUR-DNS}/{ENDPOINT}'

Or are you trying to make it work in go?

brittandeyoung commented 1 year ago

@lovaarte I am using the example provided by @jwineinger to create the signed headers. https://github.com/open-policy-agent/opa/blob/c29d375de5a9adf51a0dcd4dda843e811a06c4fc/internal/providers/aws/signing_v4a.go#L383-L410

I am attempting do this in golang.

brittandeyoung commented 1 year ago

Here is an example of the headers that are being included with the request. I replaced any sensitive information with ****

map[
Authorization:[
AWS4-ECDSA-P256-SHA256 Credential=*****/execute-api/aws4_request, 
SignedHeaders=content-length;host;x-amz-content-sha256;x-amz-date;x-amz-region-set;x-amz-security-token, Signature=******
] 
Content-Type:[application/json] 
Host:[***.execute-api.us-east-1.amazonaws.com] 
X-Amz-Content-Sha256:[***] 
X-Amz-Date:[20230615T131316Z] 
X-Amz-Region-Set:[*] 
X-Amz-Security-Token:[***]
]
brittandeyoung commented 1 year ago

Here the snippit I am using to do the signing,

    now := time.Now()
    req = req.WithContext(ctx)
    var creds signer.Credentials
    var body []byte
    if req.Body == nil {
        body = []byte("")
    } else {
        var err error
        body, err = io.ReadAll(req.Body)
        if err != nil {
            return nil, 0, errors.New("error getting request body: " + err.Error())
        }
        // Since ReadAll consumed the body ReadCloser, we must create a new ReadCloser for the request so that the
        // subsequent read starts from the beginning
        req.Body = io.NopCloser(bytes.NewReader(body))
    }

    creds.AccessKey = c.options.AWSAccessKey
    creds.SecretKey = c.options.AWSSecretKey
    if c.options.AWSSessionToken != "" {
        creds.SessionToken = c.options.AWSSessionToken
    }
    creds.RegionName = c.options.Region

    req.Header.Add("Content-Type", "application/json")
    signedHeaders := signer.SignV4a(req.Header, req.Method, req.URL, body, signersService, creds, now)
    req.Header = signedHeaders

    res, err := c.HTTPClient.Do(req)
    if err != nil {
        return nil, 0, err
    }
    defer res.Body.Close()

    body, err = io.ReadAll(res.Body)
    if err != nil {
        return nil, 0, err
    }

    return body, res.StatusCode, err

The SignV4a is taken exactly as is from @jwineinger provided example.

lovaarte commented 1 year ago

Really appreciate sharing it! Would you be interested to contribute and open a PR in the format as other examples? In my view for this case it's important to add a note/disclaimer that it's either a temporary work around till AWS SDK supports it fully, or that it's an example for use cases where you can't, or don't want, bring the whole AWS SDK.

brittandeyoung commented 1 year ago

@lovaarte I have not had a successful authentication with this code against apigateway. I am currently getting access denied.

lovaarte commented 1 year ago

It likely means it's not related to API Gateway (API-GW) but there is something missing in general, as API GW itself supports it (based on my testing yesterday).

brittandeyoung commented 1 year ago

@lovaarte In your testing, did you use a rest api or http api. I did some more testing and it looks like the sigv4a IS working when attempting to hit a rest api apigateway, but failing when attempting to hit a http api apigateway.

For our current use case we are using a http api.

Can you confirm that sigv4a IS or IS NOT supported on http api apigateway?

lucix-aws commented 3 days ago

https://github.com/aws/aws-sdk-go-v2/discussions/2812

prashil-g commented 3 days ago

Thanks @lucix-aws for releasing the support https://pkg.go.dev/github.com/aws/smithy-go/aws-http-auth @lovaarte Can you please add example now using the go lang package

lucix-aws commented 3 days ago

In the absence of an explicit example in this repository, https://github.com/aws/smithy-go/blob/main/aws-http-auth/sigv4a/e2e_test.go demonstrates using both sigv4 and sigv4a to do some S3 operations.