-
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
[filters] original source listener filter beginnings #5337
[filters] original source listener filter beginnings #5337
Conversation
Signed-off-by: Kyle Larose <kyle@agilicus.com>
Use the new interface. Signed-off-by: Kyle Larose <kyle@agilicus.com>
If the socket for a ClientConnection has a local address, bind to it. This address may be set by a socket option in the prebind phase. Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
You can't inherit the implementation of an interface from two different hierarchies, sadly. Given that we just have a bunch of mocks here, it's easier to just put the mock implementation in both places, than to refactor into a base class then call forward into the mocks. Signed-off-by: Kyle Larose <kyle@agilicus.com>
Makes this far more convenient to use. Signed-off-by: Kyle Larose <kyle@agilicus.com>
We can use virtual inheritence to get access to the mocks from the parent... Not sure how I feel about this... Signed-off-by: Kyle Larose <kyle@agilicus.com>
All Sockets have Local addresses. There's no reason to not allow setting it in them all. Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
For some reason I put it in the Network namespace originally. Fixed it up. Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
@lizan @jrajahalme Here's an example of the alternative implementation, as discussed. |
On Dec 17, 2018, at 3:25 PM, Kyle Larose ***@***.***> wrote:
@klarose commented on this pull request.
In include/envoy/network/listen_socket.h <#5337 (comment)>:
> @@ -25,6 +25,21 @@ class Socket {
*/
virtual const Address::InstanceConstSharedPtr& localAddress() const PURE;
+ /**
+ * Set the local address of the socket. On accepted sockets the local address defaults to the
+ * one at which the connection was received at, which is the same as the listener's address, if
+ * the listener is bound to a specific address.
+ *
+ * @param local_address the new local address.
+ * @param restored a flag marking the local address as being restored to a value that is
+ * different from the one the socket was initially accepted at. This should only be set
+ * to 'true' when restoring the original destination address of a connection redirected
+ * by iptables REDIRECT. The caller is responsible for making sure the new address is
+ * actually different when passing restored as 'true'.
+ */
+ virtual void setLocalAddress(const Address::InstanceConstSharedPtr& local_address,
+ bool restored) PURE;
+
As discussed in #5255 <#5255>, I've moved this into the base Socket class.
Not sure if the “restored” semantics is needed for `Socket`. If not could also leave this in `ConnectionSocket` and add a method without the 2nd parameter to `Socket`.
Jarno
|
At a first glance I prefer this than #5255, this seems cleaner and provides more extensibility to {listener,network,http} filters. Didn't look at details yet though. @jrajahalme @mattklein123 WDYT? |
Sorry I promise I will look through all of this tomorrow morning. |
I was getting some failures in the code coverage test. This may help. Signed-off-by: Kyle Larose <kyle@agilicus.com>
The anonymous namespace isn't suspicious to avoid coverage test collisions. :( Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
We don't want to set the port unless we've been configured to, and we're properly hashing based on it. Signed-off-by: Kyle Larose <kyle@agilicus.com>
source/extensions/filters/listener/original_src/original_src_socket_option.cc
Outdated
Show resolved
Hide resolved
Signed-off-by: Kyle Larose <kyle@agilicus.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com>
As per review feedback Signed-off-by: Kyle Larose <kyle@agilicus.com>
/retest |
🙀 Error while processing event:
|
/retest |
🔨 rebuilding |
As requested. Signed-off-by: Kyle Larose <kyle@agilicus.com>
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.
We have a list of follow up items but this PR LGTM as they are tracked. @mattklein123?
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 with one question. Thank you!
|
||
// [#protodoc-title: Original Src Filter] | ||
// Use the Original source address on upstream connections. | ||
// TODO(klarose): Add ref to docs |
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.
Do you plan on doing the full doc flow in a follow up? We should have complete config docs for this filter and it would be great to have some more general overview of all of the features we have that help with transparency?
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.
Yes, I do. I already have it ready for review -- I was waiting for this to get merged before submitting a PR. Here's what it looks like right now: klarose@48e56e8
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.
That said, I haven't covered the last bit -- an architectural overview of the feature(s). I can certainly do that as part of that PR.
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.
Sounds good on a follow up. Yes please on some type of arch overview!
@klarose @lizan @mattklein123 So is it possible now to set the upstream SNI by a downstream HOST header? I would like to use Header-to-Metadata filter to instruct Envoy to use the Host header for grouping connections in the connection pool and for setting SNI for the upstream connection, when the upstream connection performs TLS origination. If it is not possible, what is missing? |
@vadimeisenbergibm This doesn't add that feature. All I've done here is cause an upstream connection to use the same source IP as the downstream. Setting the SNI is at another level. I think @mattklein123 was considering the best way of communicating the SNI to the connection pool in that other thread. Here I have used a socket option to do so, leveraging the existing socket options pass to it. However, soon I will need to add yet another set of socket options. I thin Matt was wondering whether we could kill two birds with one stone and somehow provide the ability to pass both the SNI override and the new socket options forward... I haven't quite thought that far ahead yet. As for your use-case, you want to set the upstream SNI based on some form of downstream information (related to istio MTLS, right)? I have a similar use-case: I want to set the upstream SNI based on some information I have gleaned dynamically from another downstream filter, similar to the original dst filter. So, I would certainly like to see something like this worked out if possible. |
@klarose My use case: Envoy performs TLS origination for HTTP traffic and sets upstream SNI according to the HOST header from the downstream. @klarose @mattklein123 @lizan So how the feature above can be implemented? How should we proceed? |
@vadimeisenbergibm I'm about to hack something up for an internal prototype on this. Let me try that, and then push it to my github repo. Maybe that can serve as a starting point? I don't plan on it being pretty enough to merge in as it as, and I won't have time to drive it to completion until I've finished my original_src work, but it should serve as an example somebody else could take over. |
@vadimeisenbergibm @mattklein123 @lizan See https://github.com/klarose/envoy/tree/upstream_sni_setter/source/extensions/filters/http/upstream_sni_from_header It's pretty rough. Needs a lot of cleanup and a bit more design, but it works. I used a hacked up version of the header to metadata filter. We'll want to clean up the data model. I had to make some changes to the core conn pool creation code. Not sure how I feel about that, but I couldn't think of a nice alternative. If there are better suggestions, I'm all ears. :) Note: I didn't finish the http2 implementation. |
@vadimeisenbergibm one of my changes passes a list of transport socket options to the connection pool on creation. It then passes that to the new connection created when the active client is instantisted. That takes care of setting the upstream ani, since the transport socket option will be applied, doesn't it? I mean, it certainly worked for me. I proved it both functionally and with captures. |
@klarose So where is this code? |
https://github.com/klarose/envoy/commits/upstream_sni_setter It's the "random cleanup" commit, coupled with the stream info one. Tl;dr: we add the option to the stream info. The lb context exposes the stream info. The connection manager uses that to pass the options to the connection pool. |
@klarose I see. I was confused by "Random cleanup" and did not look into it. I would propose to extract that commit into a separate PR and call it "support passing TransportSocketOptions in HTTP filters", or "Pass TransportSockets to HTTP connection pool and to a new connection". I think that PR will be valuable on its own. Then we can discuss how to implement the upstream_sni_setter filter. |
IP Source transparency involves non-local IP addresses being routed as though they were local. This requires some magic in the stack to ensure that those flows are sent back to envoy from the upstream host, rather than back to the original source IP address. We plan on using SO_MARK to do this. So, add it into the socket option factory. My intention is for it to be used by a follow-up PR to envoyproxy#5337. This was cherry-picked from PR envoyproxy#5035 where it was already reviewed. I plan on closing that PR. Risk Level: Low. No code invoked in production yet. Testing: Ran newly added UT and other UT in network. Docs Changes: None until we expose this through config. Release Notes: None Signed-off-by: Kyle Larose <kyle@agilicus.com> Signed-off-by: Fred Douglas <fredlas@google.com>
Signed-off-by: Kyle Larose <kyle@agilicus.com> Signed-off-by: Fred Douglas <fredlas@google.com>
Description: This adds an original_src listener filter to provide IP source transparency on upstream connections. This is an alternative implementation to PR #5255. It uses a socket option to set the local address on the upstream connection to the downstream remote address, as well as to ensure that it all connections from the same source IP go to the same connection pool. Since socket options are copied between downstream and upstream connections, we can be sure that the options will be applied to the upstream connection.
By setting the local address on the upstream connection, we can force it to bind to the original downstream remote address. We need to ensure that connection pools only handle traffic for a given source IP because they have no concept of the mapping between a downstream request and an upstream connection. The socketOption hashKey() handles this for us.
Currently this solution only works when there is no NAT/proxy in front of the downstream, or a PROXY protocol filter is run prior to the original_src filter. I want to add an http filter to run before the envoy.router filter so we can use http headers to get the address.
One possible issue with this solution is that we could leak connection pools, or the map we use to track them could grow too large; I'm not aware of any resource constraints on the number of connection pools created by the hash key mechanism).
TODO:
Risk Level: Medium. By default this shouldn't do much, though I have made a small change to the connection code. The majority of this is in a filter that won't be brought in unless configured.
Testing: Lots of UT. Ran a simple client/server + envoy in docker to show that the transparency worked.
Docs Changes: TODO