Skip to content
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

filters: http original src filter #6790

Merged
merged 26 commits into from
May 29, 2019

Conversation

klarose
Copy link
Contributor

@klarose klarose commented May 2, 2019

Description: Add an http filter to extract the original source on a per-http-request basis
A while back I added the original src listener filter. That filter would use the downstream connection's source address to build a socket option that would then be applied to upstream connections, thereby forcing their source address to mirror that of the downstream connection.

This works fine for cases where Envoy is directly exposed to the internet, or a device in front of it is placing a proxy protocol header on it. However, if proxy protocol is not an option and something (e.g. another Envoy) is between Envoy and the client, then the listener filter is insufficient for two reasons:

  1. The source address of the connection will be the source address of the "last hop", not the client.
  2. Multiple http requests from different sources may be carried over the same tcp connection, meaning that we must create new options for each request, not for each connection.

The http original_src filter solves these two problems by using the http stack's knowledge of the request to determine the source address (e.g. via the XFF), and by setting the options on a per-request basis. It reuses the vast majority of the work done for the listener filter -- the connection pooling, etc, as well as some of the code build to create the options.

In short, this PR carries the entirety of the work to enable the http original src filter, which can be summarized as:

  • Refactoring some bits of the original_src listener filter out into a common library
  • Adding the concept of upstream connection options to the StreamDecoderFilterCallbacks and the LoadBalancerContext
  • Using the upstream connection options when creating the http connection pool in conjunction with the existing downstream connection options to build the full set of options to be used when creating upstream connections.
  • Adding the protobuf config for the original_src http filter
  • Writing the simple filter which creates the original_src socket options based on the downstreamRemoteAddress
  • Documenting the new filter

Risk Level: Medium
Testing: UT + Manual. I tried writing an integration test, but it needs a bit of infrastructure work to support running it, since it needs CAP_NET_ADMIN permissions. I started that a while back, but I didn't have time to complete it.
Docs Changes: Added docs for the filter
Release Notes: Will add

Other Notes:
This is quite large. Let me know if you want me to split it up somehow.

We'll want to use this option for both the http and the listener filter.
Bring it out into a common library.

Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
The original source filter is now all hooked up, but it has some
problems -- namely, it sets options on the downstream connection's
socket, which is wrong.

Signed-off-by: Kyle Larose <kyle@agilicus.com>
We need these to configure per-request socket options from the http
filters.

Signed-off-by: Kyle Larose <kyle@agilicus.com>
 - Hook in to new callback options mechanism
 - add ut

Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
We use the existing mechanism used to pass socket options to the
connection pool creation logic to pass the upstream socket options. We
do this by creating a new socket options object, then placing both sets
of socket options into it.

Signed-off-by: Kyle Larose <kyle@agilicus.com>
The active stream is what's shared between the various filters. Push the
options up to it so they're actually shared!

Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
 - build
 - docs

Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
@klarose klarose changed the title [filters] http original src filter filters: http original src filter May 3, 2019
@zuercher
Copy link
Member

zuercher commented May 3, 2019

@snowp can you have a look?

Copy link
Contributor

@snowp snowp left a comment

Choose a reason for hiding this comment

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

Looked over the source/ bits, looks good for the most part.

I think it would be good to split up the changes made to the core code (ie the new filter callbacks) and the changes made to extensions if possible, should make this a bit easier to review

@klarose
Copy link
Contributor Author

klarose commented May 3, 2019

@snowp Thanks for the review. Would you suggest I close this, then post two new ones broken up as you suggested?

The extension code will depend on the new filter code. I'm guessing for things to work out I'll likely need to wait for the first PR (the new filter code) to finish before posting the second (the extension code)?

@snowp
Copy link
Contributor

snowp commented May 3, 2019

Given the dependency it might make sense to keep this up and just put up a smaller PR with the router changes. You can then merge in the changes once they land on master into this PR (this is how changes were split out and merged back into #6228 for example).

@klarose
Copy link
Contributor Author

klarose commented May 3, 2019

Got it. I'll do that. Thanks for the suggestion.

@repokitteh-read-only
Copy link

🔨 rebuilding ci/circleci: coverage (failed build)

🐱

Caused by: a #6790 (comment) was created by @cpakulski.

see: more, trace.

Signed-off-by: Kyle Larose <kyle@agilicus.com>
Cleaned up docs and a comment

Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
@klarose
Copy link
Contributor Author

klarose commented May 8, 2019

@snowp I've merged in the changes we split out, and addressed your remaining comments.

Signed-off-by: Kyle Larose <kyle@agilicus.com>
The test didn't like tests being named the same across modules.

Signed-off-by: Kyle Larose <kyle@agilicus.com>
Copy link
Contributor

@snowp snowp left a comment

Choose a reason for hiding this comment

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

Looks solid, just a few minor comments

Have these original src destinations got a maintainer sponsor sorted out as per https://github.com/envoyproxy/envoy/blob/3573b07af1ab5c4cf687ced0f80e2ccc0a0b7ec2/EXTENSION_POLICY.md#adding-new-extensions ? If not I don't mind taking it. Make sure to encode this into CODEOWNERS as outlined in the doc I linked

Comments + style issues

Signed-off-by: Kyle Larose <kyle@agilicus.com>
@klarose
Copy link
Contributor Author

klarose commented May 9, 2019

Sorry -- I didn't notice the extension policy!

We definitely don't have a maintainer sorted out. Whoever does may also want to take on filters/listener/original_src as well, since it's very similar, and serves the same purpose.

Copy link
Contributor

@snowp snowp left a comment

Choose a reason for hiding this comment

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

Made another pass, looks pretty good. Feel free to put me down as the sponsor for this and the other original src filter

source/extensions/filters/http/original_src/config.h Outdated Show resolved Hide resolved
ASSERT_FALSE(mark_option.has_value());
}

TEST_F(OriginalSrcHttpTest, DecodeDataDoesNothing) {
Copy link
Contributor

Choose a reason for hiding this comment

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

These tests that just verify nothing seem a bit strange to me because they're exercising call orders that will never happen: decodeData will never be called before decodeHeaders, so normally I see tests run through the expected order of of decode* calls.

It's common to maintain state between the callbacks, e.g. for decodeHeaders to decide whether something happens in decodeData, which means that calling decodeData on its own often doesn't prove much. This test in particular that calls decodeData(, true) followed by decodeData(, false) especially stands out as a bit strange.

I don't feel super strongly about it in the context of this filter due to its simplicity, just wanted to point it out.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm actually implicitly asserting on nothing being invoked on callbacks -- it's a strict mock with no expectations set.

Any thoughts on how to make that more obvious? Some way of telling the mock that it should expect no calls at all (without needing to enumerate the disallowed calls)? I agree that it does look odd.

Copy link
Contributor

Choose a reason for hiding this comment

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

What I've done in the past is run through the entire filter lifecycle in a single test: decodeHeaders/decodeData/decodeTrailers, using strict mocks to verify that nothing happens on data/trailers. I don't really feel strongly about this so up to you if you want to change this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That's reasonable. I'll do that.

Signed-off-by: Kyle Larose <kyle@agilicus.com>
- const nits
- explicit constructor
- capitalization
- simplified a few tests

Also added myself as a reviewer.

Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
@klarose
Copy link
Contributor Author

klarose commented May 15, 2019

I think I got the sponsorship right -- the extension policy didn't explicitly call out what it meant to be a sponsor, only how to add reviewers for an extension. :)

Either way, I added you as the first reviewer, then figured that since two are required, and contributors are allowed, I could take that on as well. Let me know if I misinterpreted something there.

Copy link
Contributor

@snowp snowp left a comment

Choose a reason for hiding this comment

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

LGTM just one unresolved question on one of the comments (wether to rework the unit tests a bit)

ASSERT_FALSE(mark_option.has_value());
}

TEST_F(OriginalSrcHttpTest, DecodeDataDoesNothing) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What I've done in the past is run through the entire filter lifecycle in a single test: decodeHeaders/decodeData/decodeTrailers, using strict mocks to verify that nothing happens on data/trailers. I don't really feel strongly about this so up to you if you want to change this

Signed-off-by: Kyle Larose <kyle@agilicus.com>
For the trailers and decodedata tests, run through the normal flow taken
by an http filter.

Signed-off-by: Kyle Larose <kyle@agilicus.com>
@klarose
Copy link
Contributor Author

klarose commented May 21, 2019

/retest

@repokitteh-read-only
Copy link

🔨 rebuilding ci/circleci: coverage (failed build)

🐱

Caused by: a #6790 (comment) was created by @klarose.

see: more, trace.

Signed-off-by: Kyle Larose <kyle@agilicus.com>
@klarose
Copy link
Contributor Author

klarose commented May 22, 2019

/retest

@repokitteh-read-only
Copy link

🔨 rebuilding ci/circleci: tsan (failed build)

🐱

Caused by: a #6790 (comment) was created by @klarose.

see: more, trace.

@klarose
Copy link
Contributor Author

klarose commented May 28, 2019

@snowp What's next on this? (Aside from fixing the merge conflict). I think I've addressed all your issues.

snowp
snowp previously approved these changes May 28, 2019
Copy link
Contributor

@snowp snowp left a comment

Choose a reason for hiding this comment

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

Sorry for the delay. This LGTM but it seems like you have a merge conflict, could you resolve and then we'll merge?

Signed-off-by: Kyle Larose <kyle@agilicus.com>
Copy link
Contributor

@snowp snowp 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!

@snowp snowp merged commit 712a72a into envoyproxy:master May 29, 2019
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.

3 participants