-
Notifications
You must be signed in to change notification settings - Fork 4.9k
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
connection: Set the SOL_IP,IP_TRANSPARENT option on listen sockets #2719
Conversation
// instead of REDIRECT to get both the original source and destination addresses of the | ||
// redirected socket. | ||
on = 1; | ||
rc = setsockopt(fd_, SOL_IP, IP_TRANSPARENT, &on, sizeof(on)); |
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 don't think this should be set unconditionally; this should only be done based on some configuration.
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.
+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.
Done.
This seems like it should be configurable per listener. Can you create a PR in https://github.com/envoyproxy/data-plane-api with a proposed modification to LDS configuration? |
Will do. Thanks! |
Submitted this PR to data-plane-api: envoyproxy/data-plane-api#522. |
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.
See: #2659.
// instead of REDIRECT to get both the original source and destination addresses of the | ||
// redirected socket. | ||
on = 1; | ||
rc = setsockopt(fd_, SOL_IP, IP_TRANSPARENT, &on, sizeof(on)); |
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.
AFAIK, this should be IP_TRANSPARENT
for IPv4 and IPV6_TRANSPARENT
for IPv6.
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.
Fixed in upcoming commit.
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.
Note that this won't work for IPv6 when building using Envoy's Docker image (Ubuntu 16.04.3 with libc6-dev 2.23-0ubuntu10) because IPV6_TRANSPARENT
is not defined in that image:
$ ./ci/run_envoy_docker.sh 'grep IPV6_TRANSPARENT /usr/include/x86_64-linux-gnu/bits/in.h'
returns nothing.
On Ubuntu 17.10 with libc6-dev 2.26-0ubuntu2.1, this macro is defined as expected:
$ grep IPV6_TRANSPARENT /usr/include/x86_64-linux-gnu/bits/in.h
#define IPV6_TRANSPARENT 75
rc = setsockopt(fd_, SOL_IP, IP_TRANSPARENT, &on, sizeof(on)); | ||
if (rc == -1) { | ||
if (errno == EPERM) { | ||
// Ignore. Not running with CAP_NET_ADMIN capability. |
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.
This is still a real error, if configuration requires IP_TRANSPARENT
.
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.
Fixed in upcoming commit.
@@ -37,6 +38,22 @@ TcpListenSocket::TcpListenSocket(const Address::InstanceConstSharedPtr& address, | |||
int rc = setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); | |||
RELEASE_ASSERT(rc != -1); | |||
|
|||
#if defined SOL_IP && defined IP_TRANSPARENT |
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 believe this can be moved into if (bind_to_port) { ... }
, since as far as I recall, it's useless without bind()
.
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.
One could make the same argument about the existing setsockopt(fd_, SOL_SOCKET, SO_REUSEADDR, ...)
above. Should I move all setsockopt
calls into if (bind_to_port) { ... }
?
// instead of REDIRECT to get both the original source and destination addresses of the | ||
// redirected socket. | ||
on = 1; | ||
rc = setsockopt(fd_, SOL_IP, IP_TRANSPARENT, &on, sizeof(on)); |
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.
+1
Re: #2659, this is a different, complementary issue. You need first to implement IP_TRANSPARENT in the inbound listen socket anyway, like I do here, so that you get the original source address. Without that, you won't know what address to bind to. |
The mac tests timed out. It doesn't seem to be a functional failure. Is that something I should look into? |
…#522) Add a "transparent" option to Listener to set the SOL_IP/IP_TRANSPARENT option on listen sockets, which allows using Envoy with the iptables TPROXY target. Unlike the iptables REDIRECT target, TPROXY allows preserving both the source and destination IP addresses and ports of accepted connections. API changes for: envoyproxy/envoy#2719 Signed-off-by: Romain Lenglet <romain@covalent.io>
Pull a quick-and-dirty implementation of the SOL_IP,IP_TRANSPARENT option setup on listen sockets: https://github.com/rlenglet/envoy/tree/quick-and-dirty-transparent This will be reverted when the proper, clean change is merged upstream into Envoy: envoyproxy/envoy#2719 Signed-off-by: Romain Lenglet <romain@covalent.io>
@rlenglet can you merge to resolve conflict? |
* @param bind_to_port supplies whether to actually bind the socket. | ||
* @return Network::SocketSharedPtr an initialized and potentially bound socket. | ||
*/ | ||
virtual Network::SocketSharedPtr | ||
createListenSocket(Network::Address::InstanceConstSharedPtr address, bool bind_to_port) PURE; | ||
createListenSocket(Network::Address::InstanceConstSharedPtr address, bool transparent, |
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 we foresee having more options like this in the future? If so should we switch to some type of options struct that gets passed around? That would a avoid having to change every individual callsite and test again in the future potentially. Just throwing it out there.
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 personally don't foresee more options like this. But if we think that could happen, I could use #2734 to implement this internally.
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.
If we don't foresee more options like this than I wouldn't worry about it.
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 have one more option coming that is similar/related: enabling tcp-fastopen on listening sockets.
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.
@ggreenway So what is your preference? Should we implement this with #2734?
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.
question.. how does IP_FREEBIND differ from IP_TRANSPARENT?
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.
@ggreenway Would it work for you better if I generalized the current option setting code (i.e., #2734 ) to accept multiple option setters, maintain a list of set options and have the call sites call all of them (unless one of them fails)?
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.
@dnoe OK SGTM if you want to do the struct stuff.
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.
@jrajahalme yes that sounds reasonable
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.
FREEBIND allows binding to an address that isn't configured on any interface on the machine. IP_TRANSPARENT would also allow this, but from inside Envoy should otherwise work the same as a regular listener (whereas transparent is designed around a different use case where the traffic has been redirected to Envoy and has some original destination that is meaningful).
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.
Looks good to me.
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.
Since there are no tests (due to iptables), did anybody other than the author tested that it really works?
Another questions is, how does this interact with SO_ORIGINAL_DST
and PROXY protocol?
EXPECT_TRUE(manager_->addOrUpdateListener(parseListenerFromJson(listener_foo_json), true)); | ||
checkStats(1, 0, 0, 0, 1, 0); | ||
|
||
// Update foo listener, but with a different transparent option. Should throw. |
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.
Is there a reason why we cannot disable IP_TRANSPARENT
for the new listener?
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.
@PiotrSikora I was thinking that we have an atomicity issue. How do we atomically hand off the socket to the new listener and set/unset the option? That would require that there is a period of time when no thread is accepting on the socket during the handoff. I don't think we have that (?). And that's not desirable, since that would cause connections to be rejected during that period.
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.
@rlenglet there is always a period of time when no thread is accepting new connections (i.e. when all threads are busy processing active requests), and no connections are rejected because of kernel-maintained backlog queue on the listening socket, so I don't think lack of atomicity is the problem.
Having said that, I'm fine if you want to leave it as-is.
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'm removing that verification. We know from an accepted socket's option whether it has been TPROXYed or not, so we can safely have a mix of connections with and without TPROXY in the same queue.
@PiotrSikora It should have no interaction with |
@rlenglet my point was what happens if you use |
@rlenglet can you take a look at #2922? I started with some of the pieces in this PR, but there was a bunch of cleanup I did to make it (1) avoid the #ifdef proliferation, (2) add support for upstream as well as listener and (3) work with additional options without boiletplate. I think it might make sense to merge that one first and then this present PR should be much simpler. |
@htuch Great! I'll merge from master today. |
…nsparent Signed-off-by: Romain Lenglet <romain@covalent.io>
@htuch Please review. I merged from master. |
Pull a quick-and-dirty implementation of the SOL_IP,IP_TRANSPARENT option setup on listen sockets: https://github.com/rlenglet/envoy/tree/quick-and-dirty-transparent This will be reverted when the proper, clean change is merged upstream into Envoy: envoyproxy/envoy#2719 Signed-off-by: Romain Lenglet <romain@covalent.io>
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.
This looks great, the implementation is ready to ship. Can you add a listener_manager_impl_test
similar to what is done for freebind and was done in earlier revisions?
Love, love, love how simple this all became. Great work @ggreenway @PiotrSikora @htuch @rlenglet! @rlenglet can we get the doc/release note PR redone before we merge this? Thank you. |
Signed-off-by: Romain Lenglet <romain@covalent.io>
Signed-off-by: Romain Lenglet <romain@covalent.io>
@htuch Added tests. Looks clean. |
@mattklein123 Here is the release note PR: envoyproxy/data-plane-api#593 |
The
|
@@ -82,6 +126,12 @@ TEST_F(SocketOptionImplTest, SetOptionFreeebindSuccessFalse) { | |||
} | |||
} | |||
|
|||
// Transparent settings have no effect on post-bind behavior. |
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 thought transparent does have an effect on both prebind and postbind?
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.
Right. I will relax that.
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.
Added a commit to support setting IP_TRANSPARENT
both pre-bind and post-bind.
Signed-off-by: Romain Lenglet <romain@covalent.io>
Signed-off-by: Romain Lenglet <romain@covalent.io>
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; just questioning whether we can remove the v1 support.
@@ -185,6 +185,7 @@ const std::string Json::Schema::LISTENER_SCHEMA(R"EOF( | |||
}, | |||
"drain_type": {"type" : "string", "enum" : ["default", "modify_only"]}, | |||
"ssl_context" : {"$ref" : "#/definitions/ssl_context"}, | |||
"transparent" : {"type": "boolean"}, |
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.
Is the v1 API absolutely required?
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'll need it for Istio. Cf. istio/istio#4654 (review)
Thanks @rlenglet for all your patience here. |
@htuch Thanks a lot! |
connection: Set the SOL_IP,IP_TRANSPARENT option on listen sockets
Implement the "transparent" option in the LDS Listener to set the SOL_IP/IP_TRANSPARENT option on listen sockets, which allows using Envoy with the iptables TPROXY target. Unlike the iptables REDIRECT target, TPROXY allows preserving both the source and destination IP addresses and ports of accepted connections.
Update the OriginalDst filter to restore the original destination IP address of TPROXY connections in addition to REDIRECT connections.
Signed-off-by: Romain Lenglet romain@covalent.io
Signed-off-by: Jarno Rajahalme jarno@covalent.io
Risk Level: Low