Skip to content

codec: Raise max_request_headers_kb limit to 96 KiB#5859

Merged
alyssawilk merged 42 commits intoenvoyproxy:masterfrom
aunu53:maxer
Mar 5, 2019
Merged

codec: Raise max_request_headers_kb limit to 96 KiB#5859
alyssawilk merged 42 commits intoenvoyproxy:masterfrom
aunu53:maxer

Conversation

@aunu53
Copy link
Contributor

@aunu53 aunu53 commented Feb 6, 2019

Signed-off-by: Auni Ahsan auni@google.com

Bump up max configurable max_request_headers_kb to 96 KiB.
Add a check to http1/codec_impl.cc for headers size.
Raise the default library limits in http_parser (at compilation) and nghttp2 (at options) to 0x7fffffff, so we'll rely on our own codec check.

Risk Level: Medium.
Testing: Moved all the large request headers tests to ProtocolIntegrationTest.

Part of #5626.

@aunu53 aunu53 changed the title codec: Configure codec library max request header limits codec: Apply max request header limits settings to codec libraries' configurations Feb 6, 2019
Signed-off-by: Auni Ahsan <auni@google.com>
Signed-off-by: Auni Ahsan <auni@google.com>
Signed-off-by: Auni Ahsan <auni@google.com>
@aunu53
Copy link
Contributor Author

aunu53 commented Feb 6, 2019

What's missing this PR is an actual runtime call of http_parser_set_max_header_size. That will go in the Http conn manager config, but it's impossible to test AFAIK because the proto validator limit is lower than the http_parser default limit. When the limit is bumped past 80kb, i'll add an http1 integration test and a settings call to conn manager/config.cc.

Signed-off-by: Auni Ahsan <auni@google.com>
@alyssawilk
Copy link
Contributor

Hey Auni, as discussed offline, ping me when CI is happy?

/wait

@mattklein123 mattklein123 self-assigned this Feb 7, 2019
@aunu53
Copy link
Contributor Author

aunu53 commented Feb 13, 2019

Hi! I need some input on how to resolve this. Essentially, my test is failing because the nghttp2 library max request headers check, which I now manually configure, trips differently from the existing codec check. The codec check at codec_impl.cc:563 will check if the headers exceed the limit and reset the stream if so, while the nghttp2 library check will just flat-out close the stream.

How should I resolve this? Two ideas:

  1. In the onStreamClose callback check if the nghttp2 limit was reached and interrupt with a reset stream if so. (Not sure if this is possible or logical.)
  2. Set the nghttp2 limit to something arbitrarily higher and solely rely on our own check, whose behaviour we're in more control of. I was thinking either the max configurable limit from envoy; though since that would have to be edited twice each time the config limit is changed, maybe the configured limit + 1 would work.

Thoughts?

@aunu53
Copy link
Contributor Author

aunu53 commented Feb 13, 2019

Ahh, some more details. The library check triggers before any header bytes are dispatched to the library, so server_callbacks_.newStream() is never called (and so it's never reset, which is why my test fails). So yeah again, I could configure the library check up high as I suggested to never get hit, or we could decide that behaviour is useful.

Signed-off-by: Auni Ahsan <auni@google.com>
@aunu53
Copy link
Contributor Author

aunu53 commented Feb 13, 2019

OK so I went with an arbitrarily hard-coded nghttp2 max-header-len setting of 100kb. This can be easily changed or be configurable later, but this changes no envoy behaviour. (Both before and after this PR, the nghttp2 max check will not ever be triggered.)

Signed-off-by: Auni Ahsan <auni@google.com>
@aunu53
Copy link
Contributor Author

aunu53 commented Feb 13, 2019

/retest

@repokitteh-read-only
Copy link

🔨 rebuilding ci/circleci: mac (failed build)

🐱

Caused by: a #5859 (comment) was created by @auni53.

see: more, trace.

Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

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

Thanks for continuing to plug away at these!

// of kept alive HTTP/2 connections.
nghttp2_option_set_no_closed_streams(options_, 1);
nghttp2_option_set_no_auto_window_update(options_, 1);
nghttp2_option_set_max_send_header_block_length(options_,
Copy link
Contributor

Choose a reason for hiding this comment

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

Isn't this the setting for the size of an individual header frame? I think we want to change the total allowed headers, not the per-frame limits.

Copy link
Member

Choose a reason for hiding this comment

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

IIRC this is not very well named, but I think this does actually set the header send limit, and the library will chunk it into frames using continuation frames if necessary. @auni53 to check me here.

Copy link
Contributor

Choose a reason for hiding this comment

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

Could be. I'm not seeing any other size-related fields to tweak. I'd checked
https://nghttp2.org/documentation/nghttp2_option_set_max_send_header_block_length.html
"This option sets the maximum length of header block (a set of header fields per one HEADERS frame) to send. "
It was the "per one HEADERS frame" that got me thinking it might be the wrong field.
in code it's used in prep_frame which I thought was after the chunking but I could easily be misremembering. I'm happy to let Auni do the code sleuthing though :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ahh,

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeeee, I'm pretty certain this is the right call. Also because I did this TDD-style and the testing behaviour checks out.

request_encoder_->encodeHeaders(request_headers, true);
}

TEST_P(Http2CodecImplTest, TestSingleLargeHeadersWayOverDefaultCodecLimit) {
Copy link
Contributor

Choose a reason for hiding this comment

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

If your goal is to test an unexpectedly large header frame, you could have several keys with fairly long strings.
It makes sense to both limit the size of overall headers as well as the size of any individual header, and I think rejecting a single header key or value of 65536 bytes is totally reasonable :-)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Already had a test with several long strings! But I rearranged/reworded this stuff. Discussed offline, and I'm going to turn up this test to fail on single-large-header and document the nghttp2 limit in the max_request_headers_kb api.

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

Thanks for working on this, some comments/questions.

/wait

// of kept alive HTTP/2 connections.
nghttp2_option_set_no_closed_streams(options_, 1);
nghttp2_option_set_no_auto_window_update(options_, 1);
nghttp2_option_set_max_send_header_block_length(options_,
Copy link
Member

Choose a reason for hiding this comment

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

IIRC this is not very well named, but I think this does actually set the header send limit, and the library will chunk it into frames using continuation frames if necessary. @auni53 to check me here.

Signed-off-by: Auni Ahsan <auni@google.com>
Signed-off-by: Auni Ahsan <auni@google.com>
Signed-off-by: Auni Ahsan <auni@google.com>
Signed-off-by: Auni Ahsan <auni@google.com>
This reverts commit 999b247.

Signed-off-by: Auni Ahsan <auni@google.com>
@aunu53
Copy link
Contributor Author

aunu53 commented Feb 19, 2019

Hi @mattklein123, working on some changes to this. I'm having an issue with the http codec configuration I could use some input on.

Basically, to set the http parser limits you need to call http_parser_set_max_header_size(x). What I'm finding from my testing is that this setting persists across test runs, i.e. making that call in one http1/codec_impl test affects the success of a different test. I had thought previously that each envoy codec would be instantiating its http parser separately, but it looks like these settings are global. Does that make sense?

Signed-off-by: Auni Ahsan <auni@google.com>
@aunu53
Copy link
Contributor Author

aunu53 commented Mar 4, 2019

Oky I changed the ClientConnectionImpl max response headers from a default parameter to a class constant, added a test for large response headers rejection, and tweaked the API comment.

aunu53 added 2 commits March 4, 2019 12:15
Signed-off-by: Auni Ahsan <auni@google.com>
Signed-off-by: Auni Ahsan <auni@google.com>
@aunu53
Copy link
Contributor Author

aunu53 commented Mar 4, 2019

Ok, updated with Matt's suggestion on the API commenting + added a note on nghttp2 to the test file. I got a mysql_integration_test failure, hoping that was a flake bc idk how I'd have touched that; will look further into it if that persists.

@alyssawilk
Copy link
Contributor

Yep, that's a known and fixed flake. If you merge master you'll pick up the fix

Signed-off-by: Auni Ahsan <auni@google.com>
mattklein123
mattklein123 previously approved these changes Mar 4, 2019
Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

Thank you LGTM assuming it passes @alyssawilk muster!

Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

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

Looking great - just a few more minor nits from me and you should be good to go.

header_parsing_state_ = HeaderParsingState::Value;
current_header_value_.append(data, length);

uint32_t total =
Copy link
Contributor

Choose a reason for hiding this comment

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

const uint32_t?

// Set true between receiving 100-Continue headers and receiving the spurious onMessageComplete.
bool ignore_message_complete_for_100_continue_{};

// The default limit of 80 KiB is the vanilla http_parser behaviour.
Copy link
Contributor

Choose a reason for hiding this comment

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

The default limit is 80Kb (Kilobytes) not KiB (Kibibytes), no?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Wow, I forgot kibibytes were a thing. In any case, I checked http_parser.h:63, looks like it's indeed KiB.

Copy link
Contributor

Choose a reason for hiding this comment

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

Oh jeez, trusted the http_parser issue which was incorrect - looks like they were saying KB when they meant KiB. You can ignore this one.

"http_parser.h",
],
hdrs = ["http_parser.h"],
copts = ["-DHTTP_MAX_HEADER_SIZE=0x7fffffff"],
Copy link
Contributor

Choose a reason for hiding this comment

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

Actually sorry, on second thought I'm having a moment of paranoia. We don't have anywhere else in the Envoy code base where we use http_parser so this is 100% OK today but what do we think of adding a limit just in case? If we added a 200KB limit or some such it'd be very safely above our window such that we should never hit the http_parser limits in the codec, without allowing total infinite memory if someone else uses http parser.
Thoughts?


// The max send header block length is configured to an arbitrarily high number so as to never
// trigger the check within nghttp2, as we check request headers length in codec_impl::saveHeader.
nghttp2_option_set_max_send_header_block_length(options_, 0x7fffffff);
Copy link
Contributor

Choose a reason for hiding this comment

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

Ditto here on limits

Copy link
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

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

Bah, click fail

Signed-off-by: Auni Ahsan <auni@google.com>
@aunu53
Copy link
Contributor Author

aunu53 commented Mar 5, 2019

/retest

@repokitteh-read-only
Copy link

🔨 rebuilding ci/circleci: asan (failed build)

🐱

Caused by: a #5859 (comment) was created by @auni53.

see: more, trace.

@alyssawilk alyssawilk merged commit df3d47f into envoyproxy:master Mar 5, 2019
fredlas pushed a commit to fredlas/envoy that referenced this pull request Mar 5, 2019
Bump up max configurable max_request_headers_kb to 96 KiB.
Add a check to http1/codec_impl.cc for headers size.
Raise the default library limits in http_parser nghttp2 so we'll rely on our own codec check.

Risk Level: Medium.
Testing: Moved all the large request headers tests to ProtocolIntegrationTest.
Part of envoyproxy#5626.

Signed-off-by: Auni Ahsan <auni@google.com>
Signed-off-by: Fred Douglas <fredlas@google.com>
@aunu53 aunu53 deleted the maxer branch March 10, 2020 21:06
alyssawilk pushed a commit that referenced this pull request Apr 27, 2021
…iB (#15921)

Commit Message:
Raise max configurable max_request_headers_kb limit to 8192 KiB (8MiB) from 96 KiB in http connection manager protobuf.

Additional Description:
Added/Updated relevant unit tests and integration tests. This change will allow increasing max_request_headers_kb to 8MiB from http connection manager's configuration. The change is mainly updating the limit in a validation check in the protobuf. Also, the old (merged) PR #5859 is read, no nghttp2 library-related issues are observed on raising the max_request_headers_kb beyond 96 KiB.
Risk Level: Low
Testing: Unit, Integration, Manual
Docs Changes: Inline in proto file.

Signed-off-by: Anirudha Singh <ps.anirudha@gmail.com>
gokulnair pushed a commit to gokulnair/envoy that referenced this pull request May 6, 2021
…iB (envoyproxy#15921)

Commit Message:
Raise max configurable max_request_headers_kb limit to 8192 KiB (8MiB) from 96 KiB in http connection manager protobuf.

Additional Description:
Added/Updated relevant unit tests and integration tests. This change will allow increasing max_request_headers_kb to 8MiB from http connection manager's configuration. The change is mainly updating the limit in a validation check in the protobuf. Also, the old (merged) PR envoyproxy#5859 is read, no nghttp2 library-related issues are observed on raising the max_request_headers_kb beyond 96 KiB.
Risk Level: Low
Testing: Unit, Integration, Manual
Docs Changes: Inline in proto file.

Signed-off-by: Anirudha Singh <ps.anirudha@gmail.com>
Signed-off-by: Gokul Nair <gnair@twitter.com>
@ramaraochavali
Copy link
Contributor

@mattklein123 @alyssawilk Sorry to tag on a old issue. Since it has context - Couple of questions

  • Would it be OK to make the default value read from a runtime config instead of hardcoding it to 60Kib?
  • If we introduce a runtime config - Can the default of the runtime config be changed to a higher value? What would be the considerations (security , memory etc) for choosing that default in Envoy?

@mattklein123
Copy link
Member

cc @RyanTheOptimist @yanavlasov @KBaichoo also on ^

@RyanTheOptimist
Copy link
Contributor

@ramaraochavali Can you file an issue about there were we can discuss the details? I think that's probably better than a 4 year old already committed PR :) Please feel free to link to this PR or any related issues there.

@ramaraochavali
Copy link
Contributor

@RyanTheOptimist created #26690

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.

6 participants