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

refactor: move s3 url signing to car-store #66

Merged
merged 10 commits into from
Dec 8, 2022

Conversation

olizilla
Copy link
Contributor

@olizilla olizilla commented Dec 5, 2022

  • Add createUploadUrl to car-store.js as creating presigned s3 urls is a job for the bucket abstraction and having a thing called signer that wasn't a keypair for a DID for ucanto was confusing.
  • Switch to the @aws-sdk/s3-request-presigner for creation of signed s3 urls to simplify credential management. The existing s3 client is reused.
  • Adds content-length to the list of headers that form part of the url signature.

fixes #38

Notes

When presigning a URL for s3, some headers are "hoisted" to the URLSearchParams; they appear in the query string so you can hand the url off as a string, and the params needed for auth are baked right in there. S3 support unpacking those params from either the query or from http request headers. The query params are part of the signature, so they can't be tampered with.

You can also "hoist" and sign any other params you want... it is tempting to move x-amz-checksum-sha256 to the query so a user wouldn't need to provide it separately. However it appears that aws does not support pulling that value out of the query, only from a request header.

Meanwhile, it also appears that minio does not validate the PUT payload matches the x-amz-checksum-sha256 header at all. I include a test here that fails to show minio accepting a presigned PUT, but sending data that doesn't match the checksum.

It is unknown how s3 reacts to sending a content-length that doesn't match the payload size, but there are tests for it in storacha/sigv4#7

TODO:

  • add in content-length assertion for presigned url
  • check if we can remove the amz header from store/add response. The checksum must be enforced via the URL, not from an additional header that the client may(not) send. I think the signing of headers bakes in an assertion that those headers will be provided when using the presigned url.

License: MIT
Signed-off-by: Oli Evans oli@protocol.ai

- Add `createUploadUrl` to `car-store.js` as creating presigned s3 urls is a job for the bucket abstraction and having a thing called `signer` that wasn't a keypair for a DID for ucanto was confusing.
- Switch to the `@aws-sdk/s3-request-presigner` for creation of signed s3 urls to simplify credential management. The existing s3 client is reused.

TODO:
- add in content-length assertion for presigned url
- check if we can remove the amz header from store/add response. The checksum must be enforced via the URL, not from an additional header that the client may(not) send.

License: MIT
Signed-off-by: Oli Evans <oli@protocol.ai>
@seed-deploy seed-deploy bot temporarily deployed to pr66 December 5, 2022 22:59 Inactive
@seed-deploy
Copy link

seed-deploy bot commented Dec 5, 2022

View stack outputs

@seed-deploy seed-deploy bot temporarily deployed to pr66 December 5, 2022 23:07 Inactive
License: MIT
Signed-off-by: Oli Evans <oli@protocol.ai>
@seed-deploy seed-deploy bot temporarily deployed to pr66 December 5, 2022 23:47 Inactive
@olizilla olizilla changed the title refactor: move s2 url signing to car-store refactor: move s3 url signing to car-store Dec 5, 2022
@olizilla
Copy link
Contributor Author

olizilla commented Dec 5, 2022

these are the cool headers i get on the signed url

URLSearchParams {
  'X-Amz-Algorithm' => 'AWS4-HMAC-SHA256',
  'X-Amz-Content-Sha256' => 'UNSIGNED-PAYLOAD',
  'X-Amz-Credential' => 'minioadmin/20221205/us-west-2/s3/aws4_request',
  'X-Amz-Date' => '20221205T231949Z',
  'X-Amz-Expires' => '86400',
  'X-Amz-Signature' => '4bf836f67afea8349a5441c5d798c9b1fb07e233e5da24c309ac6e2df2a1d5d8',
  'X-Amz-SignedHeaders' => 'content-length;host',
  'x-amz-checksum-sha256' => 'go8RUmHWc07TANIf//M+HV8oQaKr93qElP0pc4qkHTA=',
  'x-id' => 'PutObject' }

@olizilla
Copy link
Contributor Author

olizilla commented Dec 5, 2022

docs

TODO: figure out what s3 does with content length. It appears to be using it as one of the signed headers, so an upload should fail if the client sends a different content length header than the one used to create the url, as the signature wouldn't match.

@olizilla olizilla added this to the w3up phase 1 milestone Dec 6, 2022
License: MIT
Signed-off-by: Oli Evans <oli@protocol.ai>
@seed-deploy seed-deploy bot temporarily deployed to pr66 December 7, 2022 15:41 Inactive
@vasco-santos
Copy link
Contributor

@olizilla tsc is complaining 😅

Copy link
Contributor

@vasco-santos vasco-santos left a comment

Choose a reason for hiding this comment

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

This looks good to me! Ci is failing because of lint, we need to confirm all the tests are running.

Using @aws-sdk/s3-request-presigner seems quite straightforward. Would love to know the reasons that we wrote sigv4 though. Is there something that we are missing?

api/test/service/store.test.js Outdated Show resolved Hide resolved
api/test/service/store.test.js Outdated Show resolved Hide resolved
api/test/service/store.test.js Outdated Show resolved Hide resolved
@olizilla
Copy link
Contributor Author

olizilla commented Dec 7, 2022

@hugomrdias wrote it for using in things like cloudflare workers where we don't want to pull in a bunch of aws SDK and don't already have an S3 client set up.

@seed-deploy seed-deploy bot temporarily deployed to pr66 December 8, 2022 12:05 Inactive
License: MIT
Signed-off-by: Oli Evans <oli@protocol.ai>
@seed-deploy seed-deploy bot temporarily deployed to pr66 December 8, 2022 12:24 Inactive
also add engines as a hint that folks need at least node 16.15 for --experimental-fetch to work

see: https://nodejs.org/docs/latest-v16.x/api/globals.html#fetch
see: https://docs.npmjs.com/cli/v6/configuring-npm/package-json#engines

License: MIT
Signed-off-by: Oli Evans <oli@protocol.ai>
@seed-deploy seed-deploy bot temporarily deployed to pr66 December 8, 2022 12:56 Inactive
@olizilla
Copy link
Contributor Author

olizilla commented Dec 8, 2022

@vasco-santos fixed merge conflicts, appeased tsc, and made tests actually work given they depend on global fetch... now, as I am about to drop a PR that upgrades us to use node 18 here, I've chosen to just get it working by enabling the node >=16.15 support for global fetch with the `--experimental-fetch' flag, just for the tests. I can remove that when we are all node 18

License: MIT
Signed-off-by: Oli Evans <oli@protocol.ai>
@seed-deploy seed-deploy bot temporarily deployed to pr66 December 8, 2022 13:29 Inactive
@olizilla
Copy link
Contributor Author

olizilla commented Dec 8, 2022

Holy cow. Consider #72 closed! I just got that test passing in CI using the latest minio from docker hub. When i removed my local 4 month old image and let it pull the latest the test started passing locally too!

}
})
// expect signature to not match
t.is(unexpectedLength.status, 403, await unexpectedLength.text())
Copy link
Contributor

Choose a reason for hiding this comment

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

Test description does not match this flow. Can we change it or decouple in 2 tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

strictly this is a "signature not match" failure as the content-length header that was used to sign the url has the value 5, and when using it we send 6, so it fails when validating the url sig.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

i've split the tests anyway

License: MIT
Signed-off-by: Oli Evans <oli@protocol.ai>
@seed-deploy seed-deploy bot temporarily deployed to pr66 December 8, 2022 15:30 Inactive
@seed-deploy seed-deploy bot temporarily deployed to pr66 December 8, 2022 15:33 Inactive
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.

Include content-length in signed headers for signed url
2 participants