Skip to content

transport sockets: expose proxy protocol socket#12762

Merged
mattklein123 merged 27 commits intoenvoyproxy:masterfrom
wez470:master
Oct 6, 2020
Merged

transport sockets: expose proxy protocol socket#12762
mattklein123 merged 27 commits intoenvoyproxy:masterfrom
wez470:master

Conversation

@wez470
Copy link
Copy Markdown
Contributor

@wez470 wez470 commented Aug 20, 2020

Exposes proxy proto socket

Commit Message: Expose proxy protocol socket publicly.

Additional Description: Finishes off work for upstream proxy protocol header generation (possible performance pass still to come). Previous work so far includes #10548 (util funcs to generate header), #10845 (config api message), and #11584 (non-exposed transport socket). This PR hooks up tcp proxy, adds config factories to the socket, adds integration tests, and adds docs.

Risk Level: Medium

Testing: Unit + Integration + Functional

Docs Changes: IP transparency - Added to proxy protocol section to mention this socket for upstream generation, API docs
generated from proto

Release Notes: https://github.com/envoyproxy/envoy/pull/12762/files#diff-0ec5c857b4b6fbbb72b2ffc5275b15efR64

Fixes #1031

@repokitteh-read-only
Copy link
Copy Markdown

CC @envoyproxy/api-shepherds: Your approval is needed for changes made to api/envoy/.
CC @envoyproxy/api-watchers: FYI only for changes made to api/envoy/.

🐱

Caused by: #12762 was opened by wez470.

see: more, trace.

wez470 added 3 commits August 20, 2020 20:01
Signed-off-by: Weston Carlson <[email protected]>
Signed-off-by: Weston Carlson <[email protected]>
Signed-off-by: Weston Carlson <[email protected]>
wez470 added 7 commits August 30, 2020 15:27
Signed-off-by: Weston Carlson <[email protected]>
Signed-off-by: Weston Carlson <[email protected]>
Signed-off-by: Weston Carlson <[email protected]>
Signed-off-by: Weston Carlson <[email protected]>
Signed-off-by: Weston Carlson <[email protected]>
@wez470 wez470 marked this pull request as ready for review August 31, 2020 02:37
@wez470 wez470 requested a review from alyssawilk as a code owner August 31, 2020 02:37
@alyssawilk
Copy link
Copy Markdown
Contributor

@antoniovicente would you be up for taking first pass?

@antoniovicente
Copy link
Copy Markdown
Contributor

/assign @antoniovicente

:ref:`Original Src Listener Filter <arch_overview_ip_transparency_original_src_listener>`.
:ref:`Original Src Listener Filter <arch_overview_ip_transparency_original_src_listener>`. Finally,
Envoy supports generating this header using the :ref:`Proxy Protocol Transport Socket <extension_envoy.transport_sockets.upstream_proxy_protocol>`
transport socket.
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.

Can we have a full example config here?

Copy link
Copy Markdown
Contributor Author

@wez470 wez470 Sep 1, 2020

Choose a reason for hiding this comment

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

Yeah, I was thinking about that, but was unsure where to add. We don't really have a designated socket spot for config examples. At one point I thought we could maybe add a sockets section here https://www.envoyproxy.io/docs/envoy/latest/configuration/configuration? wdyt? I'm fine with just adding in this file too.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The envoy.transport_sockets.tls example in docs/root/start/start.rst could be a source of inspiration.

Copy link
Copy Markdown
Contributor

@antoniovicente antoniovicente 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 implementing this. Looks pretty good. Sorry for the delays in code review.

@rchernobelskiy
Copy link
Copy Markdown

Thank you for adding this. Testing it locally works great 👍

@wez470
Copy link
Copy Markdown
Contributor Author

wez470 commented Sep 9, 2020

Thanks for trying it out! I actually was seeing some issues with health checks when I was going through functional tests again the other day. For some reason

auto src_addr = callbacks_->connection().localAddress();
was null. I'm trying to figure out what's going on there. This is also probably an indicator that we need an integration test to cover this.

Signed-off-by: Weston Carlson <[email protected]>
@wez470
Copy link
Copy Markdown
Contributor Author

wez470 commented Sep 22, 2020

@alyssawilk I think things are ready for another look 🙂

Copy link
Copy Markdown
Contributor

@alyssawilk alyssawilk left a comment

Choose a reason for hiding this comment

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

Looking great! I think once we get the last tests sorted you're good to go.

}

// Test header is sent unencrypted using a TLS inner socket
TEST_P(ProxyProtocolIntegrationTest, TestTLSSocket) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Try an -l trace and see where it hangs? Maybe it's hitting a timeout somewhere? If this is all raw TCP I think half close semantics apply, so if you close the client the connection may persist until the idle timeout kicks in.

if
ASSERT_TRUE(fake_upstream_connection->close());
ASSERT_TRUE(fake_upstream_connection->waitForDisconnect());
tcp_client->waitForHalfClose();
tcp_client->close();
fixes, that's the problem, though I'm surprised there wouldn't be the same stall in other tests

"envoy.transport_sockets.raw_buffer");
initialize();

ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection_));
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

again maybe -l trace or cerr logging? It may be easier to snag values from other health checking tests in tcp_proxy_integration_test.cc

alyssawilk
alyssawilk previously approved these changes Sep 23, 2020
@alyssawilk
Copy link
Copy Markdown
Contributor

LGTM modulo clang tidy fix :-)

Signed-off-by: Weston Carlson <[email protected]>
@alyssawilk
Copy link
Copy Markdown
Contributor

@lizan can you take a look both as secondary reviewer and API shepherd?

@stale
Copy link
Copy Markdown

stale bot commented Oct 4, 2020

This pull request has been automatically marked as stale because it has not had activity in the last 7 days. It will be closed in 7 days if no further activity occurs. Please feel free to give a status update now, ping for review, or re-open when it's ready. Thank you for your contributions!

@stale stale bot added the stale stalebot believes this issue/PR has not been touched recently label Oct 4, 2020
@alyssawilk
Copy link
Copy Markdown
Contributor

@lizan ping? Alternately @mattklein123 could do second and Api pass?

@mattklein123 mattklein123 self-assigned this Oct 5, 2020
@stale stale bot removed the stale stalebot believes this issue/PR has not been touched recently label Oct 5, 2020
@mattklein123
Copy link
Copy Markdown
Member

I can take a look.

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.

Exciting to see this land. One small question. Also, can you merge main just to make sure we aren't too out of date? Thanks.

/wait-any

Comment on lines +418 to +429
if (!read_callbacks_->connection()
.streamInfo()
.filterState()
->hasData<Network::ProxyProtocolFilterState>(
Network::ProxyProtocolFilterState::key())) {
read_callbacks_->connection().streamInfo().filterState()->setData(
Network::ProxyProtocolFilterState::key(),
std::make_unique<Network::ProxyProtocolFilterState>(Network::ProxyProtocolData{
downstreamConnection()->remoteAddress(), downstreamConnection()->localAddress()}),
StreamInfo::FilterState::StateType::ReadOnly,
StreamInfo::FilterState::LifeSpan::Connection);
}
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.

It seems odd to me that we jam this data into filter state only to read it back in the next function call. What is the use case for nested states here? Could we just pass the addresses directly into the fromFilterState() function?

Copy link
Copy Markdown
Contributor Author

@wez470 wez470 Oct 6, 2020

Choose a reason for hiding this comment

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

It was like this at one point and changed from feedback #11584 (comment). I'm fine either way

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.

If this has already been discussed it's fine with me. We can revisit later if needed.

@repokitteh-read-only repokitteh-read-only bot removed the api label Oct 6, 2020
@mattklein123 mattklein123 merged commit b18f57e into envoyproxy:master Oct 6, 2020
@wez470
Copy link
Copy Markdown
Contributor Author

wez470 commented Oct 6, 2020

Thanks all! 🎉

@rchernobelskiy
Copy link
Copy Markdown

rchernobelskiy commented Oct 20, 2020

Doing some more testing on this, there is a common case where it's not behaving as expected from what I can tell.
The first few requests always work correctly for me, or if I never change downstream/upstream IPs everything works correctly also.
But when changing the downstream remote IP, the remote IP inserted in the proxyproto header is randomly reused from previous connections.
Logs (%DOWNSTREAM_REMOTE_ADDRESS%) always shows the correct address though.

Rough steps to reproduce:
Have envoy listen on 127.0.0.1:9000 and tcp proxy to a cluster that listens for connections, reads the proxy protocol header, and then promptly closes the connection.

ip route add local 252.0.0.0/8 dev lo
# run this about 20 times:
curl --interface 252.2.2.0 127.0.0.1:9000
# then run this about 20 times
curl --interface 252.3.3.0 127.0.0.1:9000
# then run this about 20 times
curl --interface 252.4.4.0 127.0.0.1:9000

The expectation here and what the logs show is 20 connections each from the 3 separate addresses above. What the proxy protocol sends, ends up being a random mix.

Any ideas?
I tried setting connection_pool_per_downstream_connection: true, with no luck unfortunately.

@rchernobelskiy
Copy link
Copy Markdown

rchernobelskiy commented Oct 20, 2020

I wonder if this comment, suggesting existence of connection reuse scenarios, is relevant:
#4684 (comment)

Edit: filed this issue here: #13659

@alyssawilk
Copy link
Copy Markdown
Contributor

Sadly it's not that simple but I've confirmed there's definitely a bug here. I'll pick up fixing.

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.

Proxy proto header generation

7 participants