-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
support multiple trusted proxies when processing XFF #2559
Conversation
Signed-off-by: Brian Pane <[email protected]>
Signed-off-by: Brian Pane <[email protected]>
The CI errors for Mac and TSAN seem to be due to unrelated causes. I opened issue #2561 for the TSAN error. |
…iest trusted remote address rather than immediate client address. Signed-off-by: Brian Pane <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general looks great to me. Some random comments. Thanks for the careful change here. This code scares the shit out of me so would prefer to have multiple reviews. @alyssawilk can you give some attention to this on Monday?
@@ -50,9 +50,24 @@ Network::Address::InstanceConstSharedPtr ConnectionManagerUtility::mutateRequest | |||
// our peer to have already properly set XFF, etc. | |||
Network::Address::InstanceConstSharedPtr final_remote_address; | |||
bool single_xff_address; | |||
auto xff_num_trusted_hops = config.xffNumTrustedHops(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: prefer explicit type (not auto) for this type of code for clarity.
single_xff_address = request_headers.ForwardedFor() == nullptr; | ||
// If there are any trusted proxies in front of this Envoy instance (as indicated by | ||
// the xff_num_trusted_hops configuration option), get the trusted client address | ||
// from the XFF. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: extend comment to say something like "do this before we append to XFF" (to explain the subtraction, etc.)
// the xff_num_trusted_hops configuration option), get the trusted client address | ||
// from the XFF. | ||
if (xff_num_trusted_hops > 0) { | ||
auto ret = Utility::getLastAddressFromXFF(request_headers, xff_num_trusted_hops - 1); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think you could just assign to final_remote_address directly via (...).address_
@@ -143,7 +143,8 @@ class Utility { | |||
* @return GetLastAddressFromXffInfo information about the last address in the XFF header. | |||
* @see GetLastAddressFromXffInfo for more information. | |||
*/ | |||
static GetLastAddressFromXffInfo getLastAddressFromXFF(const Http::HeaderMap& request_headers); | |||
static GetLastAddressFromXffInfo getLastAddressFromXFF(const Http::HeaderMap& request_headers, | |||
uint32_t num_to_skip = 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: update doc comments
test/common/http/utility_test.cc
Outdated
TestHeaderMapImpl request_headers{ | ||
{"x-forwarded-for", fmt::format("{0}, {0}, {1}", first_address, second_address)}}; | ||
{"x-forwarded-for", | ||
fmt::format("{0}, {1}, {2}", first_address, second_address, third_address)}}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Can you just get rid of the fmt::format here and write out the header/strings directly? I think this just makes the test harder to read.
@@ -259,7 +269,7 @@ Utility::getLastAddressFromXFF(const Http::HeaderMap& request_headers) { | |||
// TODO(mattklein123 PERF: Avoid the copy here. | |||
return { | |||
Network::Utility::parseInternetAddress(std::string(xff_string.data(), xff_string.size())), | |||
last_comma == std::string::npos}; | |||
last_comma == std::string::npos && num_to_skip == 0}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Q: Do you plan on enhancing the internal address detection behavior in the case of trusted proxies? I think the logic we have now is not-optimal. (Well, it was not optimal to begin with, now it's much less optimal). I point this out because if we change that behavior it will have to be configured and we will have to be super careful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can think of three approaches:
-
Add a configuration flag that changes the internal address detection to something like "the request is internal if and only if the trusted client address is an RFC1918/RFC4193 address."
-
When the new xff_num_trusted_hops is set, do the internal address detection based on the trusted client address. (And use the existing internal address logic if xff_num_trusted_hops is unset/zero.)
-
Leave the internal address detection unchanged.
Do you have a preference among those?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would just do (3) unless you need the behavior to be otherwise. I was just pointing it out as an open issue that might need to be worked on in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
SGTM. Assuming the IP tagging filter operates on the trusted remote address, I'll just use that to differentiate internal from external.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blerg, I'm torn on this.
I think if we're adding support for multiple xff headers, "the right thing to do" would be to consider it trusted if the direct connected host and N hops were trusted.
I was going to say given Brian's not using Envoy's notion of internal and touching auth code is scary, we could just leave it as a TODO. But it'd be uglier if we need to come around and clean this up later when someone wants it as it'd require yet another config option to avoid suddenly marking requests as internal for Envoy users.
I keep coming back to the fact the concept of "is this internal" is generally purpose useful but so far from my sample set of 3 (pinterest, lyft, google) the methods of determining "this is internal" and "what to do about it" are sufficiently user-specific I'm tempted to say we make everyone do their own filter, and move the current Lyft code to a reference implementation filter which could maybe even assert xff_num_trusted_hops was only 0 to show it was not supported. @mattklein123 WDYT? Is this more trouble than it's worth?
Signed-off-by: Brian Pane <[email protected]>
test/common/http/utility_test.cc
Outdated
TestHeaderMapImpl request_headers{ | ||
{"x-forwarded-for", fmt::format("{0}, {0}, {1}", first_address, second_address)}}; | ||
{"x-forwarded-for", first_address + ", " + second_address + ", " + third_address}}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry I actually meant remove the first_address, etc. variables and literally do:
{"x-forwarded-for", "129.0.2.10, 192.0.2.1, 10.0.0.1"}
:) But just a nit/preference if you feel otherwise.
@@ -50,9 +50,22 @@ Network::Address::InstanceConstSharedPtr ConnectionManagerUtility::mutateRequest | |||
// our peer to have already properly set XFF, etc. | |||
Network::Address::InstanceConstSharedPtr final_remote_address; | |||
bool single_xff_address; | |||
uint32_t xff_num_trusted_hops = config.xffNumTrustedHops(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: const
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM other than nits. Will defer to @alyssawilk for further review/merge.
Signed-off-by: Brian Pane <[email protected]>
Signed-off-by: Brian Pane <[email protected]> Signed-off-by: Brian Pane <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code LGTM insofar as it does what it says it does, and it's crazy well documented (yay!)
My concerns is I think we're adding more complexity to an already complex system, and even with great docs between the various combinations and permutations of use_remote_address and xff_number_trusted_hops, and inconsistency with how "trusted" works, I feel like any addition of complexity should come with some clean up :-)
Chatted with Brian offline and he had one thought for simplifying, which was only supporting xff_num_trusted_hops for nodes with use_remote_address==true which would at least remove the N vs N+1 hops confusion.
@@ -259,7 +269,7 @@ Utility::getLastAddressFromXFF(const Http::HeaderMap& request_headers) { | |||
// TODO(mattklein123 PERF: Avoid the copy here. | |||
return { | |||
Network::Utility::parseInternetAddress(std::string(xff_string.data(), xff_string.size())), | |||
last_comma == std::string::npos}; | |||
last_comma == std::string::npos && num_to_skip == 0}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Blerg, I'm torn on this.
I think if we're adding support for multiple xff headers, "the right thing to do" would be to consider it trusted if the direct connected host and N hops were trusted.
I was going to say given Brian's not using Envoy's notion of internal and touching auth code is scary, we could just leave it as a TODO. But it'd be uglier if we need to come around and clean this up later when someone wants it as it'd require yet another config option to avoid suddenly marking requests as internal for Envoy users.
I keep coming back to the fact the concept of "is this internal" is generally purpose useful but so far from my sample set of 3 (pinterest, lyft, google) the methods of determining "this is internal" and "what to do about it" are sufficiently user-specific I'm tempted to say we make everyone do their own filter, and move the current Lyft code to a reference implementation filter which could maybe even assert xff_num_trusted_hops was only 0 to show it was not supported. @mattklein123 WDYT? Is this more trouble than it's worth?
TBH I could easily see someone asking for this to work even when use_remote_address is false. My inclination is to keep it as is, but I don't feel that strongly about it. |
I think this is a great idea and something we should definitely consider. Please open a ticket to track. With that said, we absolutely cannot change the current behavior with causing chaos to not only Lyft but who knows who else (I think we all agree on that but just throwing it out there). As you point out, this is a) ridiculously complicated and b) everyone has a slightly different definition of what internal/external means. I think my vote is to move forward with @brian-pane change as-is and then consider any further changes as potentially moving into some type of filter/plug-in system. Thoughts? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, my one holding point was I liked the simplicity of not having all 4 combinations of permutations of remote / xff but if you think it's worth keeping I'm otherwise fine.
@brian-pane can you open a issue ticket for clean up? You don't have to own it but we should at least note this is an area where help is wanted :-)
Ok, I've created issue 2590 for the follow-up. |
* Update envoy-wasm sha Signed-off-by: Lizan Zhou <[email protected]> * fix due to ABI change * remove compile db Signed-off-by: Lizan Zhou <[email protected]> * fix for envoyproxy/envoy-wasm#216 Signed-off-by: Lizan Zhou <[email protected]> * fix ci * roll forward Signed-off-by: Lizan Zhou <[email protected]>
Signed-off-by: Snow Pettersen <[email protected]> Signed-off-by: JP Simard <[email protected]>
Signed-off-by: Snow Pettersen <[email protected]> Signed-off-by: JP Simard <[email protected]>
Description:
If the xff_num_trusted_hops config option is set to a number greater than zero, trust the specified number of additional addresses at the end of the
X-Forwarded-For
request header.Risk Level: High
(because this change interacts with the internal/external request security model)
Testing: New test cases included
Docs Changes: #479
Release Notes: Included
Fixes: #2503
API Changes: #459
Signed-off-by: Brian Pane [email protected]