Skip to content

http: switching from XFP to scheme#17372

Merged
alyssawilk merged 5 commits intoenvoyproxy:mainfrom
alyssawilk:xfp
Jul 21, 2021
Merged

http: switching from XFP to scheme#17372
alyssawilk merged 5 commits intoenvoyproxy:mainfrom
alyssawilk:xfp

Conversation

@alyssawilk
Copy link
Copy Markdown
Contributor

@alyssawilk alyssawilk commented Jul 15, 2021

Corrects all Envoy uses of ForwardedProto which actually want request URI over to :scheme

As a reminder, XFP indicates the encryption of the (original) downstream connection where :scheme is part of the URI and the resource requested. It's legal (though unusual) to request http:// urls over a TLS connection for HTTP/2. It's possible (if ill advised) to have an internal mesh forwarding https schemed requests in the clear.

Current uses of X-Forwarded-Proto are

  1. in the HCM, clearing XFP from untrusted users (unchanged)
  2. in the HCM, setting absent XFP based on downstream transport security (unchanged)
  3. in the HCM setting absent :scheme to XFP (unchanged)
  4. in buildOriginalUri, changing from using XFP to scheme (changed. new URIs should be based on original URIs not on transport security.
  5. in the router, clearing default port based on XFP (unchanged)
  6. in the router serving redirect URLs based on scheme (changed - used to be XFP but is now based on the scheme of the original URI)
  7. in the router, applying SSL route redirect based on XFP (unchanged)
  8. in the router, using :scheme for internal redirect url checks (changed - used to use XFP. new URIs should be based on original URI)
  9. in the cache filter, using :scheme to serve content (changed we used to serve based on XFP but if http://foo.com/ differs from https://foo.com and the http version is requested over a TLS connection the http response should be served)
  10. in oath2 serving redirect URLs based on scheme (changed this used to be based on SFP but URLs should be based on original URL scheme)

Risk Level: High
Testing: updated tests
Docs Changes: inline
Release Notes: inline
Runtime guard: envoy.reloadable_features.correct_scheme_and_xfp
Fixes #14587

Signed-off-by: Alyssa Wilk <alyssar@chromium.org>
Signed-off-by: Alyssa Wilk <alyssar@chromium.org>
Copy link
Copy Markdown
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 tackling this really tough set of changes. A few high level comments.

  1. I would appreciate it if you could split out the rename from the other changes, since I think it would make the tricky stuff easier to review?
  2. In terms of documentation, here is what I would recommend:
    1. Potential changes to https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_conn_man/headers.html#scheme
    2. Maybe a new HTTP FAQ section with a new entry on scheme/XFP handling?
    3. Maybe a new section here on scheme handling? https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/http/http (FAQ and arch overview can cross link as you see fit.)

/wait

!headers.getSchemeValue().empty()) {
return headers.getSchemeValue();
}
return headers.getXForwardedProtoValue();
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.

What are the cases in which scheme is not but XFP is set? I thought we should always set scheme now early on in the request lifecycle if it's not otherwise set? Can you add more comments?

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.

They're always set at the same point in the HCM so will only be absent with buggy filters.
I can remove the failover here and there and let folks with buggy filters get buggy behavior :-P

Comment on lines +48 to +51
absl::string_view getXFPOrScheme(const Http::RequestHeaderMap& headers) {
absl::string_view xfp = headers.getXForwardedProtoValue();
return xfp.empty() ? headers.getSchemeValue() : xfp;
}
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.

Not sure if this is worth co-locating with Utility::getScheme for comment clarity? At first glance I thought it was the same vs. the inverse.

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.

removed

Comment on lines +1383 to +1384
// TODO(alyssar, mattklein123) is the goal here to force TLS, or force https:
// scheme? Maybe upstreams will get confused if seeing an http url.
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 original intent here (from a very long time ago) was definitely to force TLS, not force the scheme. So I think this is probably correct?

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.

yeah the only weirdness is then if you have an https:// request in cleartext would you then not serve a redirect to https://foo.com and functionally end up in a redirect loop?

Comment on lines +1387 to +1388
// No scheme header. This normally only happens when ActiveStream::decodeHeaders
// bails early (as it rejects a request), so there is no routing is going to happen anyway.
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.

OK this answers my question above. Maybe just move those functions to next to each other and put this comment in each one?

Comment on lines +86 to +88
absl::string_view xfp = headers.getXForwardedProtoValue();
if (Http::HeaderUtility::schemeIsValid(xfp)) {
headers.setScheme(xfp);
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.

This is hurting my brain (should have done this earlier in the day). How come this case isn't using the scheme header if we are preserving downstream scheme? More comments?

"envoy.reloadable_features.vhds_heartbeats",
"envoy.reloadable_features.wasm_cluster_name_envoy_grpc",
"envoy.reloadable_features.upstream_http2_flood_checks",
"envoy.reloadable_features.use_scheme_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.

nit: not sure what would be better, but making this a bit more descriptive would be good.

Signed-off-by: Alyssa Wilk <alyssar@chromium.org>
@mattklein123
Copy link
Copy Markdown
Member

Thanks this LGTM. Can you merge main?

/wait

Signed-off-by: Alyssa Wilk <alyssar@chromium.org>
Signed-off-by: Alyssa Wilk <alyssar@chromium.org>
@alyssawilk
Copy link
Copy Markdown
Contributor Author

merged, but alas needs a merge stamp

Copy link
Copy Markdown
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.

🤞

@alyssawilk alyssawilk merged commit 83e96c5 into envoyproxy:main Jul 21, 2021
leyao-daily pushed a commit to leyao-daily/envoy that referenced this pull request Sep 30, 2021
Corrects all Envoy uses of ForwardedProto which actually want request URI over to :scheme

As a reminder, XFP indicates the encryption of the (original) downstream connection where :scheme is part of the URI and the resource requested. It's legal (though unusual) to request http:// urls over a TLS connection for HTTP/2. It's possible (if ill advised) to have an internal mesh forwarding https schemed requests in the clear.

Current uses of X-Forwarded-Proto are

in the HCM, clearing XFP from untrusted users (unchanged)
in the HCM, setting absent XFP based on downstream transport security (unchanged)
in the HCM setting absent :scheme to XFP (unchanged)
in buildOriginalUri, changing from using XFP to scheme (changed. new URIs should be based on original URIs not on transport security.
in the router, clearing default port based on XFP (unchanged)
in the router serving redirect URLs based on scheme (changed - used to be XFP but is now based on the scheme of the original URI)
in the router, applying SSL route redirect based on XFP (unchanged)
in the router, using :scheme for internal redirect url checks (changed - used to use XFP. new URIs should be based on original URI)
in the cache filter, using :scheme to serve content (changed we used to serve based on XFP but if http://foo.com/ differs from https://foo.com and the http version is requested over a TLS connection the http response should be served)
in oath2 serving redirect URLs based on scheme (changed this used to be based on SFP but URLs should be based on original URL scheme)
Risk Level: High
Testing: updated tests
Docs Changes: inline
Release Notes: inline
Runtime guard: envoy.reloadable_features.correct_scheme_and_xfp
Fixes envoyproxy#14587

Signed-off-by: Alyssa Wilk <alyssar@chromium.org>
@alyssawilk alyssawilk deleted the xfp branch February 28, 2022 21:25
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.

Make use of :scheme and XForwardedProto clear and consistent in Envoy

3 participants