aws_request_signing_filter hash payload by default#15846
aws_request_signing_filter hash payload by default#15846lizan merged 21 commits intoenvoyproxy:mainfrom
Conversation
|
Hi @jstewmon, welcome and thank you for your contribution. We will try to review your Pull Request as quickly as possible. In the meantime, please take a look at the contribution guidelines if you have not done so already. |
rojkov
left a comment
There was a problem hiding this comment.
Thank you for working on this! Could you please add unit tests illustrating the new behavior introduced by the added Filter::decodeData() method?
To fix the formatting CI check you need to run ./tools/code_format/check_format.py fix.
There was a problem hiding this comment.
Better set the value in the init list right after host_rewrite_:
- host_rewrite_(host_rewrite) {}
+ host_rewrite_(host_rewrite), unsigned_payload_{unsigned_payload} {}There was a problem hiding this comment.
I can't see where this member is initialized. Perhaps you wanted to use config_->isPayloadUnsigned() here?
There was a problem hiding this comment.
This was a really helpful comment for me! I wasn't paying close attention to what I was doing and completely failed to thread the config setting through to the filter implementation. I ended up grabbing the option from the config_ instance already available to the filter. Would appreciate another look to see if it looks ok now. 🙏
rgs1
left a comment
There was a problem hiding this comment.
Thanks for working on this. Could you add an entry to docs/root/version_history/current.rst and also some tests for the cases when hashing the body fails?
It'll be nice to update docs/root/configuration/http/http_filters/aws_request_signing_filter.rst with a note about this being supported.
There was a problem hiding this comment.
@rojkov I started working on the tests tonight only to realize that the new sign overload can't be disambiguated by the mocks.
I wanted to get feedback on this strategy before updating everywhere. I didn't see Matcher or the other recommended techniques used elsewhere in the envoy code.
Is this a good approach? If not, do you have a recommendation?
There was a problem hiding this comment.
This seems to be fine. testing::Matcher is actually used in some Envoy tests.
There was a problem hiding this comment.
Ah, thanks - I now see Matcher and others used throughout the tests. I must have had a stray search filter enabled when I was looking before.
35a7e2f to
0f70ce0
Compare
|
I think this PR is in pretty good shape now. Though, I've just realized that none of the tests for decodeData are checking the stats - I can try to add that tomorrow. I've tested the filter with GET and POST requests to SNS (without unsigned_payload). I've also tested GET and PUT requests to S3 with and without unsigned_payload. I also verified that PUTs to an S3 having a bucket policy which denies unsigned payloads succeeds without unsigned payloads and fails with a 403 with unsigned_payloads. |
There was a problem hiding this comment.
Do we stop iterating to avoid tampering the signature? Probably a comment could be helpful here.
There was a problem hiding this comment.
Hm, I inferred this as necessary because it's what the buffer filter and the aws lambda filter do. I supposed it was necessary to preclude the headers from being sent upstream before the stream ends.
I don't mind adding a comment, but the best I can honestly write with my current understanding is "stop iteration is what other filters do when the request is to be buffered."
Did you have something more concrete in mind?
There was a problem hiding this comment.
My experience with filter flows is limited, but I think you are right: StopIteration is needed to buffer the request.
I don't have strong opinion if a comment is needed. Let's leave it as it is.
There was a problem hiding this comment.
The new param needs to be documented in the doc string.
There was a problem hiding this comment.
Nit: better put explicit type here instead of auto. It helps readers to disambiguate which variant of the overloaded sign() function is called with less mental effort.
There was a problem hiding this comment.
The method doesn't mutate the object's state -> can be declared as const.
There was a problem hiding this comment.
Hm, I tried this, but it gives a compiler error:
error: 'const' type qualifier on return type has no effect [-Werror,-Wignored-qualifiers]
const bool FilterConfigImpl::useUnsignedPayload() { return unsigned_payload_; }
There was a problem hiding this comment.
Doh, I figured it out - was using const in the wrong place.
There was a problem hiding this comment.
ASSERT(request_headers_ != nullptr)There was a problem hiding this comment.
Nit: usually we initialize to null values with empty braces:
Http::RequestHeaderMap* request_headers_{};
rojkov
left a comment
There was a problem hiding this comment.
Thanks! Looks good to me.
Please fix the CI checks. To run some checks (like DCO) automatically before pushing you can install the git hooks with
./support/bootstrap
There was a problem hiding this comment.
My experience with filter flows is limited, but I think you are right: StopIteration is needed to buffer the request.
I don't have strong opinion if a comment is needed. Let's leave it as it is.
26b6a01 to
7f03b7b
Compare
rgs1
left a comment
There was a problem hiding this comment.
Thanks, left a few more comments.
There was a problem hiding this comment.
Please link to the unsigned_payload field
There was a problem hiding this comment.
Link to field and I don't think false needs to the inverted quotes.
There was a problem hiding this comment.
nit:
ENVOY_LOG(debug, "aws request signing from decodeHeaders unsigned_payload: {}", unsigned_payload);
There was a problem hiding this comment.
I think we are moving away from exceptions in the serving path, so maybe add a TODO about making sign() unexceptional
There was a problem hiding this comment.
Hmm should we add a new stat signing_body_added or similar? To distinguish from the existing signing code path...
There was a problem hiding this comment.
Having separate counters could add fidelity when unsigned_payload is false. I don't actually know under what circumstances end_stream is true in decodeHeaders, so I suppose having discrete counters could be useful in diagnosing any unexpected behavior (like if there's some way end_stream is true when there was a request body).
There was a problem hiding this comment.
Should we call this use_unsigned_payload or skip_body_buffering?
There was a problem hiding this comment.
I'll change it to use_unsigned_payload. Unsigned payload is a very specific signing algorithm variant that isn't widely supported. I would say that using unsigned payload implies buffering is not required, but there could be some future case that warrants disabling buffering without using the unsigned payload variant.
There was a problem hiding this comment.
I added two new counters that are only incremented when signing occurs in decodeData.
|
I looked at the failed CI checks, and see two issues. On windows: on linux_x64 tsan: The windows build failure seems possibly related to this PR, but I don't see a way to get the detailed build output to figure out what the problem is. I'm not sure how the integration test failure might be related to this PR. I don't see a way to get the test log output to see what the problem was. I can't reproduce either of these locally (macOS). |
|
Hi, don't want to pollute this PR but not sure where else to ask. |
|
@dragonbone81 this PR should resolve your issue. The current aws request signing filter implementation does not sign request payloads, so it only works with requests which do not have a body or with a few services that support a special UNSIGNED PAYLOAD (e.g. s3). @mattklein123 I've worked through all of the reviewer feedback thus far. There are basically two CI failures which I don't think I can resolve with the limited visibility I have. Could you please take a look and give any feedback you might have that can help me push this over the line? 🙏 |
|
@jstewmon mind merging main and pushing? I can take a look at the CI failures. |
canonical must include the hashed payload for most services. The prior behavior of using UNSIGNED-PAYLOAD is an exception to the rule, which select services like s3 support, since hashing the payload may be impractical if the payload is very large. A new filter option is introduced, so that the filter may be explicitly configured to use the UNSIGNED-PAYLOAD string literal as specified in the S3 signing docs: https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html fixes envoyproxy#13904 Signed-off-by: Jonathan Stewmon <jstewmon@gmail.com>
Signed-off-by: Jonathan Stewmon <jstewmon@gmail.com>
Signed-off-by: Jonathan Stewmon <jstewmon@gmail.com>
Signed-off-by: Jonathan Stewmon <jstewmon@gmail.com>
Signed-off-by: Jonathan Stewmon <jstewmon@gmail.com>
|
Overall this is looking good. Just added some minor comments. Thanks! |
Signed-off-by: Jonathan Stewmon <jstewmon@gmail.com>
Signed-off-by: Jonathan Stewmon <jstewmon@gmail.com>
Signed-off-by: Jonathan Stewmon <jstewmon@gmail.com>
|
@tonya11en thanks for the last round of feedback - I think I've addressed everything. Could you please take another look? 🙏 |
tonya11en
left a comment
There was a problem hiding this comment.
I'm digging the thorough tests! Thanks for doing this and for all of the rigor in the implementation.
The explanations around the various decisions make sense. LGTM.
| headers.setHost(host_rewrite); | ||
| } | ||
|
|
||
| if (!use_unsigned_payload && !end_stream) { |
There was a problem hiding this comment.
Makes sense. Thanks for the really thorough answer-- made it really clear.
|
Can you merge main and resolve conflicts? |
Signed-off-by: Jonathan Stewmon <jstewmon@gmail.com>
Signed-off-by: Jonathan Stewmon <jstewmon@gmail.com>
|
@lizan merged main and fixed up the version history 👍 |
|
@lizan is this ready to be merged? Anything else I need to do? |
|
I think he was waiting on the tests to pass. Looks like one of them was cancelled, so it never happened. /retest |
|
Retrying Azure Pipelines: |
Signed-off-by: Jonathan Stewmon <jstewmon@gmail.com>
|
@tonya11en @lizan the retest passed, but in the meantime, the version history required another merge. If you can please approve the CI checks to run again, hopefully we'll get a clean run and can merge this. 🤞 🙏 |
👋 Hi, I thought I'd have a crack at this one. It's the first time I've written C++ in nearly 20y, so feedback is more than welcome - I mostly waded though similar sources to figure out how to get something working.
Commit Message:
aws_request_signing_filter hash payload by default
canonical must include the hashed payload for most services. The prior
behavior of using UNSIGNED-PAYLOAD is an exception to the rule, which
select services like s3 support, since hashing the payload may be
impractical if the payload is very large.
A new filter option is introduced, so that the filter may be explicitly
configured to use the UNSIGNED-PAYLOAD string literal as specified
in the S3 signing docs:
https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
fixes #13904
Additional Description:
The original implementation was seemingly very specific to S3 and was subsequently amended to extend the same niche singing behaviors for ES and Glacier. This changes the filter's default behavior to match the general SigV4 guidelines while providing a configuration option to enable the specialized UNSIGNED-PAYLOAD behavior.
https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html
Risk Level: Medium?
Deployments using the filter will now buffer requests by default, which could result in 413 responses for requests with bodies exceeding the buffer limit. These users can mitigate buffering by enabling the
unsigned_payloadoption.Testing:
I tested locally with a filter config. I anticipate updating the automated tests based on feedback from maintainers.
Docs Changes:
I think the .proto docstrings are sufficient, though I suppose a changelog entry may be needed somewhere? Pointers appreciated.
Release Notes:
Platform Specific Features:
[Optional Runtime guard:]
[Optional Fixes #Issue]
[Optional Deprecated:]
[Optional API Considerations:]