-
Notifications
You must be signed in to change notification settings - Fork 412
Add expiry validation for S3 gateway requests #9710
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
Conversation
01442fe to
3915765
Compare
77a8138 to
f5fb7ba
Compare
This change adds validation for the `X-Amz-Expires` parameter in presigned URLs. Changes: - Parse `X-Amz-Expires` and validate within allowed range - Check expiration time for presigned URLs and deny request if past expiration time. - Add an explicit check for all required parameters for presigned URLs Fixes #9599
375ec38 to
07ff93a
Compare
N-o-Z
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
Added comment inline.
In addition:
- I think it will be nice to have an esti test that checks the e2e flow with an expired presigned URL request (I think this can be done with a very short expiry) but if you think it's too much work for too less of a value let me know
- These changes fix the behavior for the V4 signer but we also have the V2 which although deprecated is still being used in some cases and is still supported in our code. We might want to consult with product regarding that. If we do need to fix this for V2 let's open a separate issue for that so this PR can converge.
| if err == nil { | ||
| c.chosen = method | ||
| return sigContext, nil | ||
| } else if !errors.Is(err, ErrHeaderMalformed) && !errors.Is(err, ErrBadAuthorizationFormat) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please add a comment here explaining why we are singling out these error types
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added a comment here, hopefully it explains it clearly!
| return expires, nil | ||
| } | ||
|
|
||
| func isV4PresignedRequest(query url.Values) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice taking this out to a separate method
| return ctx, nil | ||
| } | ||
|
|
||
| // otherwise, see if we have all the required query parameters |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's important to keep the comment.
I'd modify it to: "Otherwise try to parse request as presigned URL
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment replaced and updated
pkg/gateway/sig/v4.go
Outdated
|
|
||
| func (ctx *verificationCtx) verifyExpiration() error { | ||
| if !ctx.AuthValue.IsPresigned { | ||
| // TODO: we currently don't have handling for this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is that a TODO?
Is there an expiry mechanism for regular requests?
pkg/gateway/sig/v4.go
Outdated
| timeDiff := now.Sub(requestTime) | ||
|
|
||
| // Check for requests from the future and allow small clock skew | ||
| if timeDiff < 0 && timeDiff.Abs() > 5*time.Minute { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we checking for 5min?
What's the behavior in AWS for presigned requests that are less than 5 min in the future?
Please document
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had a misunderstanding here of how AWS handles clock skew for signed requests. After testing against AWS S3: AWS validates clock skew for presigned URLs when X-Amz-Date is in the future. If that time is >15 minutes ahead of the server time, the requests are rejected and anything within 15 minutes is tolerated. No clock skew check for past timestamps (only expiration). I'll fix this and add comments.
| "github.com/treeverse/lakefs/pkg/gateway/errors" | ||
| ) | ||
|
|
||
| func TestVerifyExpiration(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice tests - do we want to also add tests for non-presigned with expiry and check our behavior is consistent with S3?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm going to add those tests in a follow up.
| } | ||
| } | ||
|
|
||
| func TestParseExpires(t *testing.T) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should test for nil expires as well (no header)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parseExpires is only called after isV4PresignedRequest validates that X-Amz-Expires exists in the request. At that point, we have a string value which can't be nil. The empty string case is already covered in this test and the tests for isV4PresignedRequest check for the missing expiry param. Did you have a different test in mind?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parseExpires is only called after isV4PresignedRequest validates that X-Amz-Expires exists in the request. At that point, query.Get() returns a string (which can't be nil). The empty string case is already covered by the existing test and the tests for isV4PresignedRequest check for the missing expiry param. Did you have a different test in mind?
07ff93a to
d5ee853
Compare
This change ensures that when a request doesn't have the v4 algorithm URL parameter (`x-amz-algorithm`), the chained authenticator tries the next auth method instead of failing. Previously, requests using other signature versions would fail because v4 parsing returned and error that blocked the chain.
Updates future timestamp validation to match AWS S3 behaviour. AWS tolerates future clock skew of up to 15 minutes for presigned requests.
d5ee853 to
99a1f85
Compare
|
Thanks for the review @N-o-Z! I think I've addressed your comments, and I also added some esti tests to cover the different expiry situations. I agree that anything related to V2 can be addressed in a follow up 👍 |
N-o-Z
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for addressing the comments and adding the tests!
| return errors.ErrRequestNotReadyYet | ||
| } | ||
|
|
||
| // Calculate expiration from the signed time, not current time |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍🏽
Please link follow-up issue to this task |
Closes #9599
Change Description
Bug Fix
This change adds validation for the
X-Amz-Expiresparameter in presigned URLs.Changes:
X-Amz-Expiresand validate within allowed rangeTesting Details
Unit tests for the verification logic were added.
Also tested using the python test in this gist
Breaking Change?
This could be considered a breaking change if users were unaware of this issue and have been relying on presigned URLs not expiring. However, this is a security issue and should be fixed.