HCM: guard Envoy from removal of critical response headers.#15658
HCM: guard Envoy from removal of critical response headers.#15658mathetake wants to merge 37 commits intoenvoyproxy:mainfrom
Conversation
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
|
cc @asraa |
asraa
left a comment
There was a problem hiding this comment.
My 2 cents! Thank you so much for doing this!
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
| ASSERT(response_encoder_->encode100ContinueHeaders(continue_headers).ok()); | ||
| chargeStats(continue_headers); |
There was a problem hiding this comment.
chargeStats assumes that the required headers are present, so we need to change the order here and elsewhere.
|
still crashes when it is head request 😕 |
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
|
OK, now all the crashes disappeared. Will work on the unit / integration tests. |
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
| const bool modified_end_stream = (end_stream && continue_data_entry == encoder_filters_.end()); | ||
| state_.non_100_response_headers_encoded_ = true; | ||
| filter_manager_callbacks_.encodeHeaders(headers, modified_end_stream); | ||
| state_.non_100_response_headers_encoded_ = true; |
There was a problem hiding this comment.
this change is necessary since there's a check of non_100_response_headers_encoded_ != false in the local reply and it makes sense to keep that assertion.
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
|
I thikn you could probably add an ASSERT(checkRequiredResponseHeaders()) after Alyssa's code to reset. |
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
|
Our test added in this PR has passed for QUIC now, but
protocol tests have started failing for not only QUIC but also htt1,2. Will take a look. |
|
OK I hope I fixed the problem. Our approach here would change the behavior slightly different from the one in #15648. That is the upstream client will never receive the RESET even if there's invalid status headers coming, and instead just send the 502 with 'protocol error'-ish message. |
|
I hope the latest commit efdc243 explains well :) |
|
@asraa @alyssawilk PTAL! :) |
asraa
left a comment
There was a problem hiding this comment.
The code changes by themselves look OK to me. I'm just not 100% sure if they should all be handled with reset like QUIC was.
@alyssawilk
alyssawilk
left a comment
There was a problem hiding this comment.
Thanks for tackling this! It'll be great to have Envoy even more robust.
| // filter_manager_callbacks_.streamEnded() returns True here when the codec failed to encode | ||
| // headers due to the lack of required response headers, and filter_manager_callbacks already sent | ||
| // the local reply and ended the stream by itself. So in that case this function must be no-op. | ||
| if (end_stream && !filter_manager_callbacks_.streamEnded()) { |
There was a problem hiding this comment.
where are we now calling maybeEndEncode after the stream already ended?
I'm not sure but my concern is we may have a problem we should be solving before we reach this point.
There was a problem hiding this comment.
that is when the local reply is sent (due to the missing requires response headers) in the ConnectionManagerImpl::ActiveStream::encodeHeaders as filter_manager_callbacks.encodeHeaders, and the filter manager then reaches maybeEndEncode.
envoy/source/common/http/filter_manager.cc
Lines 1070 to 1072 in 174e51a
But yeah I think this made the code a little bit more complex so maybe we should just return the status from filter_manager_callbacks and send the LocalReply just before maybeEndEncode in FilterManager..
There was a problem hiding this comment.
Yeah, I think I'd rather have the HCM encodeHeaders return a status, so the filter manager knows something has gone awry.
I think it's useful to retain checks in maybeEndEncode happening once to catch other weird corner case bugs.
cc @snowp for a second opinion.
| encoder_.encodeHeaders(*headers_copy, end_stream); | ||
| const auto status = encoder_.encodeHeaders(*headers_copy, end_stream); | ||
| // simulate the connection manager's behavior to handle encoding failures. | ||
| if (!status.ok()) { |
There was a problem hiding this comment.
I think this is a problem.
Is this added to fix the integration tests we had that Envoy successfully handed upstream responses with no status? I think it's now testing the upstream works, not that Envoy works, and we need some way to force the encoder to encode this for tests, even if it looks invalid.
I think this is why coverage is now failing, because we're no longer testing handling bad responses from upstream.
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
|
I'm waiting for the reply to #15658 (comment) since it might affect the other parts |
Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
|
will work on the fix of test after the comment https://github.com/envoyproxy/envoy/pull/15658/files#r608174940 has reached consensus since it also affects the other parts |
|
This pull request has been automatically marked as stale because it has not had activity in the last 30 days. It will be closed in 7 days if no further activity occurs. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions! |
|
This pull request has been automatically closed because it has not had activity in the last 37 days. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions! |
… chain. (#16745) This is the rework of the previous PR (#15658). Additional Description: Previously, Envoy hasn't been guarded from the removal of critical response headers like :status. This happens when there's misbehaving filter chan, and especially this protection is important when users run their own extensions through Wasm. Resolves #13756 and ref: #15487. Testing: new integration tests Risk: low Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
… chain. (envoyproxy#16745) This is the rework of the previous PR (envoyproxy#15658). Additional Description: Previously, Envoy hasn't been guarded from the removal of critical response headers like :status. This happens when there's misbehaving filter chan, and especially this protection is important when users run their own extensions through Wasm. Resolves envoyproxy#13756 and ref: envoyproxy#15487. Testing: new integration tests Risk: low Signed-off-by: Takeshi Yoneda <takeshi@tetrate.io>
Signed-off-by: Takeshi Yoneda takeshi@tetrate.io
Commit Message: HCM: guard Envoy from removal of critical response headers.
Additional Description: Previously, Envoy hasn't been guarded from the removal of critical response headers like :status. This happens when there's misbehaving filter chan, and especially this protection is important when users run their own extensions through Wasm. Resolves #13756 and ref: #15487.
For the request path, Envoy is already guarded by
HeaderUtility::checkRequiredHeadersand its error is gracefully handled in theonPoolReady.Risk Level: high (change in the core feature's critical code path)
Testing: unittest && integration test
The following is the current crash path with uncaught exception:
envoy/source/common/http/conn_manager_impl.cc
Line 1463 in 25d5066
envoy/source/common/http/conn_manager_impl.cc
Line 771 in 25d5066
envoy/source/common/http/utility.cc
Lines 442 to 445 in 25d5066
and we've never reached the assertion for this here:
envoy/source/common/http/http2/codec_impl.cc
Line 237 in 25d5066