Skip to content

H/2: discard Host header when :authority is present#30005

Merged
yanavlasov merged 4 commits intoenvoyproxy:mainfrom
yanavlasov:http2-discard-host-header
Dec 8, 2023
Merged

H/2: discard Host header when :authority is present#30005
yanavlasov merged 4 commits intoenvoyproxy:mainfrom
yanavlasov:http2-discard-host-header

Conversation

@yanavlasov
Copy link
Copy Markdown
Contributor

@yanavlasov yanavlasov commented Oct 6, 2023

Commit Message:
Discard the Host header if the :authority header was received to bring Envoy into compliance with https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1

This behavioral change can be reverted by setting runtime flag envoy.reloadable_features.http2_discard_host_header to false.

Risk Level: Low
Testing: Unit Tests
Fixes #31118
Docs Changes: N/A
Release Notes: Yes
Platform Specific Features: N/A
Runtime guard: envoy.reloadable_features.http2_discard_host_header

Signed-off-by: Yan Avlasov <yavlasov@google.com>
@repokitteh-read-only
Copy link
Copy Markdown

CC @envoyproxy/runtime-guard-changes: FYI only for changes made to (source/common/runtime/runtime_features.cc).

🐱

Caused by: #30005 was opened by yanavlasov.

see: more, trace.

Copy link
Copy Markdown
Contributor

@adisuissa adisuissa left a comment

Choose a reason for hiding this comment

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

Code looks good!
I've got a high-level question about the desired behavior.

// Check if there is already the :authority header
auto result = stream->headers().get(Http::Headers::get().Host);
if (!result.empty()) {
// Discard the host header value
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

My understanding of https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1 is that discarding host is not necessary, but it must have the same value as :authority. Should the discarding be done only if the host differs from :authority?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Here are the statements related to the server:

  1. The recipient of an HTTP/2 request MUST NOT use the Host header field to determine the target URI if ":authority" is present.
  2. A server SHOULD treat a request as malformed if it contains a Host header field that identifies an entity that differs from the entity in the ":authority" pseudo-header field. (Envoy does not need to reject request with Host different from :authority)
  3. An intermediary that forwards a request over HTTP/2 MAY retain any Host header field. (Envoy does not need to retain the Host if it was present).

Note that Envoy can not have both :authority and Host headers in the header map, they are treated as one and the same. As a result discarding the Host header is compliant with RFC.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

You got me convinced. I agree it makes sense to remove the header and protect it with a runtime flag.

Comment thread source/common/http/http2/codec_impl.cc Outdated
StreamImpl* stream = getStreamUnchecked(frame->hd.stream_id);
if (stream && name == static_cast<absl::string_view>(Http::Headers::get().HostLegacy)) {
// Check if there is already the :authority header
auto result = stream->headers().get(Http::Headers::get().Host);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: const

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Done

@jmarantz
Copy link
Copy Markdown
Contributor

I think this deserves a cross-company review

if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_discard_host_header")) {
StreamImpl* stream = getStreamUnchecked(frame->hd.stream_id);
if (stream && name == static_cast<absl::string_view>(Http::Headers::get().HostLegacy)) {
// Check if there is already the :authority header
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The http2 spec says that :authority must come before regular header fields and that reversing the order should be considered a malformed request. As long as the h2 parser actually rejects the reverse order, this looks good.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is correct. I have added a test to validate this.

ASSERT(frame->headers.cat == NGHTTP2_HCAT_REQUEST || frame->headers.cat == NGHTTP2_HCAT_HEADERS);
if (Runtime::runtimeFeatureEnabled("envoy.reloadable_features.http2_discard_host_header")) {
StreamImpl* stream = getStreamUnchecked(frame->hd.stream_id);
if (stream && name == static_cast<absl::string_view>(Http::Headers::get().HostLegacy)) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit, maybe move this check above the line 2131

StreamImpl* stream = getStreamUnchecked(frame->hd.stream_id);
if (!stream) {
// We have seen 1 or 2 crashes where we get a headers callback but there is no associated
// stream data. I honestly am not sure how this can happen. However, from reading the nghttp2
// code it looks possible that inflate_header_block() can safely inflate headers for an already
// closed stream, but will still call the headers callback. Since that seems possible, we should
// ignore this case here.
// TODO(mattklein123): Figure out a test case that can hit this.
stats_.headers_cb_no_stream_.inc();
return 0;
}

although not sure that check still available for now.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

saveHeader is called from two places. I would need to duplicate this check. Maybe it is is better to keep it in one place only.

@alyssawilk
Copy link
Copy Markdown
Contributor

/wait on comments and main merge

@github-actions
Copy link
Copy Markdown

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!

@github-actions github-actions bot added the stale stalebot believes this issue/PR has not been touched recently label Nov 22, 2023
@github-actions
Copy link
Copy Markdown

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!

@yanavlasov yanavlasov reopened this Dec 6, 2023
Signed-off-by: Yan Avlasov <yavlasov@google.com>
@github-actions github-actions bot removed the stale stalebot believes this issue/PR has not been touched recently label Dec 6, 2023
Signed-off-by: Yan Avlasov <yavlasov@google.com>
@yanavlasov
Copy link
Copy Markdown
Contributor Author

Addressed comments. @zuercher @soulxu please take a look.

Copy link
Copy Markdown
Member

@soulxu soulxu left a comment

Choose a reason for hiding this comment

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

LGTM, thanks!

@yanavlasov yanavlasov merged commit a71c76d into envoyproxy:main Dec 8, 2023
@yanavlasov yanavlasov deleted the http2-discard-host-header branch December 8, 2023 19:35
@SeanKilleen
Copy link
Copy Markdown

SeanKilleen commented Dec 28, 2023

@jmarantz @zuercher @soulxu -- quick question as I try to understand the flow of envoy releases. I see this PR is merged but is not associated with a milestone. Am I correct in assuming that this would go out with 1.28.1 and possibly be back-ported to 1.27.3?

Apologies for tagging you all; I looked through the various docs but if there was anything about release flow & cadence, I missed it.

@soulxu
Copy link
Copy Markdown
Member

soulxu commented Jan 2, 2024

@jmarantz @zuercher @soulxu -- quick question as I try to understand the flow of envoy releases. I see this PR is merged but is not associated with a milestone. Am I correct in assuming that this would go out with 1.28.1 and possibly be back-ported to 1.27.3?

Apologies for tagging you all; I looked through the various docs but if there was anything about release flow & cadence, I missed it.

@SeanKilleen this is going to 1.29 release. If you want this in 1.28 or 1.27, you need to backport it.

@SeanKilleen
Copy link
Copy Markdown

Per @zirain at the Istio project, the Istio team would appreciate this change being back-ported to v1.28.x.

you can try to ping @phlax or @alyssawilk (I'm not sure who is the right person own backport) at the original PR, request to backport to v1.28.

CCing @yanavlasov in case this is something they'd be able to take on as well. I would typically be willing to attempt it myself but I'm out of my depth from a language perspective.

Sorry for the time gap on this -- a miscommunication elsewhere gave me the impression it already had been backported, despite @soulxu's comments above to the contrary. Apologies!

@phlax
Copy link
Copy Markdown
Member

phlax commented Apr 3, 2024

@SeanKilleen can you raise PRs against the other supported branches - ie:

  • release/v1.28
  • release/v1.27
  • release/v1.26

it should be backported to all supported (unless they dont have the issue it addresses)

/backport

@repokitteh-read-only repokitteh-read-only bot added the backport/review Request to backport to stable releases label Apr 3, 2024
@SeanKilleen
Copy link
Copy Markdown

SeanKilleen commented Apr 3, 2024

@phlax sure, I can try. I have no experience in this codebase or C++; I just happened to stumble upon an issue which was luckily fixed in this PR by @yanavlasov. But I'll check the contributing docs etc. and do my best.

@phlax
Copy link
Copy Markdown
Member

phlax commented Apr 3, 2024

great thanks @SeanKilleen

dont worry about c++ skills (mine are rudimentary at best)

the steps are basically this ...

$ git checkout release/v1.28
$ git checkout -b 1.28-discard-fix
$ git cherry-pick $COMMIT_ID_FROM_MAIN
$ git  push --set-upstream origin 1.28-discard-fix

this assumes you have a remote origin that you use to create PRs from

the $COMMIT_ID_FROM_MAIN can be gotten from the linked commit when the PR landed - in this case from here:

a71c76d

ie a71c76da8e6e925dd4006c7696911ddf149c0c72

when you create the PRs make sure the target branch is set to the relevant branch - in the example case release/v1.28

we should probs add this to docs somewhere

@phlax
Copy link
Copy Markdown
Member

phlax commented Apr 3, 2024

you will most likely have to resolve some conflict - if you are lucky just the changelog

@SeanKilleen
Copy link
Copy Markdown

@phlax ah, no problem. I'll add it to the docs too :)

SeanKilleen pushed a commit to SeanKilleen/envoy that referenced this pull request Apr 3, 2024
Discard the Host header if the :authority header was received to bring Envoy into compliance with https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1

This behavioral change can be reverted by setting runtime flag envoy.reloadable_features.http2_discard_host_header to false.

---------

Signed-off-by: Yan Avlasov <yavlasov@google.com>
Signed-off-by: Sean Killeen <SeanKilleen@gmail.com>
SeanKilleen pushed a commit to SeanKilleen/envoy that referenced this pull request Apr 3, 2024
Discard the Host header if the :authority header was received to bring Envoy into compliance with https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1

This behavioral change can be reverted by setting runtime flag envoy.reloadable_features.http2_discard_host_header to false.

---------

Signed-off-by: Yan Avlasov <yavlasov@google.com>
Signed-off-by: Sean Killeen <SeanKilleen@gmail.com>
SeanKilleen pushed a commit to SeanKilleen/envoy that referenced this pull request Apr 3, 2024
Discard the Host header if the :authority header was received to bring Envoy into compliance with https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1

This behavioral change can be reverted by setting runtime flag envoy.reloadable_features.http2_discard_host_header to false.

---------

Signed-off-by: Yan Avlasov <yavlasov@google.com>
SeanKilleen pushed a commit to SeanKilleen/envoy that referenced this pull request Apr 3, 2024
Discard the Host header if the :authority header was received to bring Envoy into compliance with https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1

This behavioral change can be reverted by setting runtime flag envoy.reloadable_features.http2_discard_host_header to false.

---------

Signed-off-by: Yan Avlasov <yavlasov@google.com>
Signed-off-by: Sean Killeen <SeanKilleen@gmail.com>
SeanKilleen pushed a commit to SeanKilleen/envoy that referenced this pull request Apr 3, 2024
Discard the Host header if the :authority header was received to bring Envoy into compliance with https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1

This behavioral change can be reverted by setting runtime flag envoy.reloadable_features.http2_discard_host_header to false.

---------

Signed-off-by: Yan Avlasov <yavlasov@google.com>
Signed-off-by: Sean Killeen <SeanKilleen@gmail.com>
phlax pushed a commit that referenced this pull request Apr 4, 2024
Discard the Host header if the :authority header was received to bring Envoy into compliance with https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1

This behavioral change can be reverted by setting runtime flag envoy.reloadable_features.http2_discard_host_header to false.

---------

Signed-off-by: Yan Avlasov <yavlasov@google.com>
Signed-off-by: Sean Killeen <SeanKilleen@gmail.com>
phlax pushed a commit that referenced this pull request Apr 4, 2024
Discard the Host header if the :authority header was received to bring Envoy into compliance with https://www.rfc-editor.org/rfc/rfc9113#section-8.3.1

This behavioral change can be reverted by setting runtime flag envoy.reloadable_features.http2_discard_host_header to false.

---------

Signed-off-by: Yan Avlasov <yavlasov@google.com>
Signed-off-by: Sean Killeen <SeanKilleen@gmail.com>
@phlax phlax removed the backport/review Request to backport to stable releases label Jul 9, 2025
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.

Some HTTP/2 requests fail with duplicate value in envoy authority request pseudo-header

8 participants