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: Add support for AWS Signing Version 4A #5489

Merged
merged 4 commits into from
Dec 19, 2022

Conversation

jwineinger
Copy link
Contributor

AWS is rolling out an extension to SigV4 called Signature Version 4A (SigV4A) which enables signatures that are valid in more than one AWS Region. This is required for signing multi-region API requests, for example with Amazon S3 Multi-Region Access Points (MRAP). This lets OPA use an S3 MRAP as a bundle source that is latency optimized by geography and also highly-available / resilient against regional outages.

This commit adds SigV4A support with an implementation that is a modified version of internal code from the aws-sdk-go-v2 project: https://github.com/aws/aws-sdk-go-v2/tree/93c3f18/internal/v4a. That approach felt far safer than trying to re-implement the necessary crypto functions from scratch. Note: the aws-sdk-go-v2 project is Apache 2 licensed, so copying and modifying their code seems fine according to section 4. I've attempted to satisfy the conditions of that section by:

  • 4.b: adding a comment line at the top of internal/providers/aws/signing_v4a.go
  • 4.d: including the NOTICES.txt file from aws-sdk-go-v2 in the internal/providers/aws/ folder

This also makes an internal/providers/aws package that contains both the existing V4 signing code as well as the V4A signing code being added. This seemed reasonable for code organization and because this is internal code.

Fixes #5429

@netlify
Copy link

netlify bot commented Dec 15, 2022

Deploy Preview for openpolicyagent ready!

Name Link
🔨 Latest commit febf55d
🔍 Latest deploy log https://app.netlify.com/sites/openpolicyagent/deploys/63a0cfe7eb34fc00082be076
😎 Deploy Preview https://deploy-preview-5489--openpolicyagent.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

@jwineinger
Copy link
Contributor Author

@philipaconrad I'm curious on your thoughts on creating the aws package under providers. That causes the linter to not like all of the things with the "AWS" prefix to their name since it now matches the package they're in. I can rename those things if that's acceptable. I'm not sure the reasoning behind having a single "providers" package.

@jwineinger jwineinger force-pushed the aws-sig-v4a branch 3 times, most recently from 3191710 to faea7c2 Compare December 15, 2022 21:09
@jwineinger jwineinger changed the title Add support for AWS Signing Version 4A Feat: Add support for AWS Signing Version 4A Dec 16, 2022
@philipaconrad
Copy link
Contributor

Hello @jwineinger! In reply to some of your questions:

I'm curious on your thoughts on creating the aws package under providers.

That was done because there was only 1x cloud provider with 1x feature to worry about: AWS, with Sig v4 request signing. It seemed a bit excessive at the time to carve out another directory just for that. Now that there's 2 signing algorithms, giving it a sub-package unto itself makes more sense.

The reason for the "AWS" naming prefix was precisely because everything was nested under providers at the time, and I wanted to reduce the possibility of mistakes if we added, say, GCP or Backblaze provider-specific functions in that package. Now that your PR has moved the AWS functionality under its own package, dropping the prefix makes perfect sense to do. 🙂

@philipaconrad
Copy link
Contributor

@jwineinger This is a chunky PR (+/- around 1500 LOC!) that I want take the time to properly review, but my initial impression from skimming over the code is a very positive one. 🙂

I'm planning to carve out some time early next week to specifically go and double-check over the trickier parts, especially the crypto bits, tests, and anything mangling header values. I suspect the crypto parts will be simple enough, since it looks like you simply ported the ECC utilities over straight from the AWS SDK.

Early Comments:

  • The majority of the re-plumbing you've done to move the AWS functionality into its own package looks great! The new config option for the AWS REST plugin is good as well. 👍
  • I have noticed a few failing tests around the v4 signing for the REST plugin; I'll dig into those next week unless you get to triaging them first.

Thank you for your contribution, and I'll see about getting some more detailed CR comments up for you next week! 😄

@jwineinger
Copy link
Contributor Author

jwineinger commented Dec 19, 2022

This is a chunky PR (+/- around 1500 LOC!)

yeah ☹️. Much of the code is ported and stripped down from AWS's repo, but it is still hefty. I happy to help make the review job easier if you have ideas on how to do that.

I have noticed a few failing tests around the v4 signing for the REST plugin;

I saw those as well. It seems to be only on go 1.17, which is a little strange to me. Go 1.18 and 1.19 (locally) all pass. I did start digging into them a little on Friday and hopefully I'll get more time to finish that early this week. I am able to repro the failures using 1.17 locally. It seems that the generated signature is different on 1.17 than 1.18/1.19.

@jwineinger
Copy link
Contributor Author

jwineinger commented Dec 19, 2022

It appears the test failures between 1.17 and 1.18 are caused by a go crypto library change, specifically in the generation of the key used to initialize the AES cypher.

In go 1.17, it calculates that it needs 128 bits / 16 bytes of entropy (https://github.com/golang/go/blob/go1.17.13/src/crypto/ecdsa/ecdsa.go#L204-L208) because the derived private key uses a 256 bit curve (https://github.com/open-policy-agent/opa/blob/0e06fb332fdda0d00e0ad0e55b63f904eb42f9fb/internal/providers/aws/signing_v4a.go#L75-L120), which is used in the key generation (https://github.com/golang/go/blob/go1.17.13/src/crypto/ecdsa/ecdsa.go#L217).

In go 1.18, the implementation was changed to always get 256 bits of entropy (https://github.com/golang/go/blob/go1.18.9/src/crypto/ecdsa/ecdsa.go#L209).

This changes the key, and thus the AES cipher, and thus the resulting signature. It seems a bit odd that the same derived key could produce different signatures, though I'm no crypto expert. I may do some testing against AWS and see if they accept both.

@jwineinger
Copy link
Contributor Author

Interestingly, both go 1.17.13 and 1.18.9 produce binaries that sign requests in a way that AWS accepts. So I guess the thing to do is have the tests expect different signatures between go versions.

Comment on lines +667 to +676
expectedAuthorization: []string{
// this signature is for go 1.18+, which changed crypto/ecdsa so signatures differ from go 1.17
"AWS4-ECDSA-P256-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20190424/execute-api/aws4_request, " +
"SignedHeaders=content-length;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-region-set;x-amz-security-token, " +
"Signature=30450221009f3b0cda178456dfd1bec61b78bdbd115c0cf497eaa52c58bbb2850ad9c49c3002207009cb88a1219a4a6626056c31823a6b5bc2728bc88bc98a06e12e1148482c94",
// this signature is for go 1.17. Remove this and only test for a single value when OPA drops go 1.17
"AWS4-ECDSA-P256-SHA256 Credential=MYAWSACCESSKEYGOESHERE/20190424/execute-api/aws4_request, " +
"SignedHeaders=content-length;content-type;host;x-amz-content-sha256;x-amz-date;x-amz-region-set;x-amz-security-token, " +
"Signature=304602210088b5a5ccf9e37aac765f7e6bf0507577eb1b919b80bc3c385b8856c7ab7a9912022100f4c558e36be338c9644240b722e06333ea9a5305b2e638d56ad0105995c9b1f7",
},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is excellent, and solves the problem with 2x signature results being possible. 👍

@philipaconrad
Copy link
Contributor

So I guess the thing to do is have the tests expect different signatures between go versions.

Well, that's horrifying. 😅 Thank you for digging into that issue, and for coming up with a workaround.

Copy link
Contributor

@philipaconrad philipaconrad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jwineinger This PR looks good to me! 😄

Thank you for putting in the time to investigate the test signature differences.

AWS is rolling out an extension to SigV4 called Signature Version 4A (SigV4A)
which enables signatures that are valid in more than one AWS Region. This is
required for signing multi-region API requests, for example with Amazon S3
Multi-Region Access Points (MRAP). This lets OPA use an S3 MRAP as a bundle
source that is latency optimized by geography and also highly-available /
resilient against regional outages.

This commit adds SigV4A support with an implementation that is a modified
version of internal code from the aws-sdk-go-v2 project:
https://github.com/aws/aws-sdk-go-v2/tree/93c3f18/internal/v4a. That approach
felt far safer than trying to re-implement the necessary crypto functions
from scratch. Note: the aws-sdk-go-v2 project is Apache 2 licensed, so
copying and modifying their code seems fine according to section 4. I've
attempted to satisfy the conditions of that section by:
* 4.b: adding a comment line at the top of
  internal/providers/aws/signing_v4a.go
* 4.d: including the NOTICES.txt file from aws-sdk-go-v2 in the
  internal/providers/aws/ folder

This also makes an internal/providers/aws package that contains both the
existing V4 signing code as well as the V4A signing code being added.
This seemed reasonable for code organization and because this is
internal code.

Fixes open-policy-agent#5429

Signed-off-by: Jay Wineinger <jawineinger@spscommerce.com>
Signed-off-by: Jay Wineinger <jawineinger@spscommerce.com>
…cdsa changes

Signed-off-by: Jay Wineinger <jawineinger@spscommerce.com>
Signed-off-by: Jay Wineinger <jawineinger@spscommerce.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Implement AWS Signature V4a for multi-region access point usage
2 participants