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

destination: send Opaque protocol hint for opaque ports #10301

Merged
merged 6 commits into from
Apr 14, 2023
Merged

Conversation

hawkw
Copy link
Contributor

@hawkw hawkw commented Feb 9, 2023

The outbound proxy handles endpoints with the opaque_transport flag by
opening a direct connection to the inbound proxy's inbound listener
port, and sending a ProtoBuf TransportHeader including the target port
of the originating outbound connection and an (optional)
SessionProtocol describing the protocol used on that connection.

Currently, outbound proxies initiating direct connections will always
send SessionProtocol values communicating the protocol as understood
by the outbound proxy. However, this is not always the desired behavior.
Direct connections with TransportHeaders are used in two cases: for
gateway connections, and for ports which are marked as opaque. When the
inbound port is marked as opaque, the presence of a SessionProtocol
tells the inbound proxy to handle that connection as the indicated
protocol, which results in incorrect behavior when the inbound proxy's
ServerPolicy configures the target port as opaque (see #9888).

Therefore, the Destination proxy API has been updated to add a new
ProtocolHint, Opaque, which indicates that an outbound proxy should
not send a SessionProtocol when initiating a direct connection, even
if the outbound proxy handled the connection as HTTP. This hint was
added to the proxy API in linkerd/linkerd2-proxy-api#197, and released
in linkerd2-proxy-api v0.8.0.

This branch updates the Destination controller's dependency on
linkerd2-proxy-api to v0.8.0, and changes the controller to send an
Opaque protocol hint when the target port is marked as opaque on the
destination pod. This should override the H2 protocol hint that is
added when the destination is meshed. I've also added a new test for
this behavior.

Fixes #9888 (along with linkerd/linkerd2-proxy#2209, which changes the
proxy to actually handle the Opaque protocol hint).

hawkw added 3 commits February 9, 2023 11:05
it doesn't actually need any of the changes in v0.8.0, but...why not...
The outbound proxy handles endpoints with the `opaque_transport` flag by
opening a direct connection to the inbound proxy's inbound listener
port, and sending a ProtoBuf `TransportHeader` including the target port
of the originating outbound connection and an (optional)
`SessionProtocol` describing the protocol used on that connection.

Currently, outbound proxies initiating direct connections will *always*
send `SessionProtocol` values communicating the protocol as understood
by the outbound proxy. However, this is not always the desired behavior.
Direct connections with `TransportHeader`s are used in two cases: for
gateway connections, and for ports which are marked as opaque. When the
inbound port is marked as opaque, the presence of a `SessionProtocol`
tells the inbound proxy to handle that connection as the indicated
protocol, which results in incorrect behavior when the inbound proxy's
ServerPolicy configures the target port as opaque (see #9888).

Therefore, the `Destination` proxy API has been updated to add a new
`ProtocolHint`, `Opaque`, which indicates that an outbound proxy should
_not_ send a `SessionProtocol` when initiating a direct connection, even
if the outbound proxy handled the connection as HTTP. This hint was
added to the proxy API in linkerd/linkerd2-proxy-api#197, and released
in `linkerd2-proxy-api` v0.8.0.

This branch updates the Destination controller's dependency on
`linkerd2-proxy-api` to v0.8.0, and changes the controller to send an
`Opaque` protocol hint when the target port is marked as opaque on the
destination pod. This should override the `H2` protocol hint that is
added when the destination is meshed. I've also added a new test for
this behavior.

Fixes #9888 (along with linkerd/linkerd2-proxy#2209, which changes the
proxy to actually handle the `Opaque` protocol hint).
@hawkw hawkw requested a review from a team as a code owner February 9, 2023 21:34
@hawkw hawkw requested a review from adleong February 9, 2023 21:35
@@ -402,6 +403,9 @@ func createWeightedAddr(address watcher.Address, opaquePorts map[uint32]struct{}
weightedAddr.ProtocolHint.OpaqueTransport = &pb.ProtocolHint_OpaqueTransport{
InboundPort: port,
}
weightedAddr.ProtocolHint.Protocol = &pb.ProtocolHint_Opaque_{
Opaque: &pb.ProtocolHint_Opaque{},
}
}
}
Copy link
Member

Choose a reason for hiding this comment

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

I think this might read more clearly if we put the enableH2Upgrade block as an else if here. This helps communicate that these are mutually exclusive and that opaque takes precedence, rather than relying on ordering and overwriting. However, if we do that, we'll need to be a bit careful to make sure we properly handle the case when OpaqueProtocol is set but getInboundPort fails (what even IS the correct behavior in that case?).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

hmm, AFAICT if getInboundPort fails, that means that we couldn't parse a port from the pod spec's proxy container's env vars, or the pod doesn't have a proxy container. if there's no proxy at all, we shouldn't be enabling the opaque transport flag... but if the env var is malformed, what should we do? just skip the update entirely?

Copy link
Member

Choose a reason for hiding this comment

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

Previously, if getInboundPort failed, we would log and leave the protocol hint set to H2 (if enabled). Not sure if that was the right behavior to begin with, but after this change the behavior would be to leave the protocol completely unset.

Would it make sense to still set ProtocolHint_Opaque, even if the getInboundPort fails? Is that a valid configuration? Can opaque traffic be sent without the tagged transport?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Previously, if getInboundPort failed, we would log and leave the protocol hint set to H2 (if enabled). Not sure if that was the right behavior to begin with, but after this change the behavior would be to leave the protocol completely unset.

I think the new behavior is more correct. We should not be attempting an H2 upgrade if targeting a port that's marked as opaque, since we know that the proxy on the receiving end will not attempt a downgrade back to HTTP/1.

Would it make sense to still set ProtocolHint_Opaque, even if the getInboundPort fails? Is that a valid configuration? Can opaque traffic be sent without the tagged transport?

The Opaque protocol hint is meaningless without tagged transport, as it only configures what SessionProtocol is sent when using tagged transport. perhaps we should still send it in this case, but we should not be populating the ProtocolHint.OpaqueTransport field, as we don't know the port that connections should be directed to, and therefore cannot use opaque transport.

Copy link
Contributor

@kleimkuhler kleimkuhler left a comment

Choose a reason for hiding this comment

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

Looks good!

hawkw added a commit to linkerd/linkerd2-proxy that referenced this pull request Apr 13, 2023
Currently, when the outbound proxy makes a direct connection prefixed
with a `TransportHeader` in order to send HTTP traffic, it will always
send a `SessionProtocol` hint with the HTTP version as part of the
header. This instructs the inbound proxy to use that protocol, even if
the target port has a ServerPolicy that marks that port as opaque, which
can result in incorrect handling of that connection. See
linkerd/linkerd2#9888 for details.

In order to prevent this, linkerd/linkerd2-proxy-api#197 adds a new
`ProtocolHint` value to the protobuf endpoint metadata message. This
will allow the Destination controller to explicitly indicate to the
outbound proxy that a given endpoint is known to handle all connections
to a port as an opaque TCP stream, and that the proxy should not perform
a protocol upgrade or send a `SessionProtocol` in the transport header.
This branch updates the proxy to handle this new hint value, and adds
tests that the outbound proxy behaves as expected.

Along with linkerd/linkerd2#10301, this will fix linkerd/linkerd2#9888.

I opened a new PR for this change rather than attempting to rebase my
previous PR #2209, as it felt a bit easier to start with a new branch
and just make the changes that were still relevant. Therefore, this
closes #2209.
@hawkw hawkw merged commit ed4d240 into main Apr 14, 2023
@hawkw hawkw deleted the eliza/9888 branch April 14, 2023 23:48
risingspiral pushed a commit that referenced this pull request May 4, 2023
The outbound proxy handles endpoints with the `opaque_transport` flag by
opening a direct connection to the inbound proxy's inbound listener
port, and sending a ProtoBuf `TransportHeader` including the target port
of the originating outbound connection and an (optional)
`SessionProtocol` describing the protocol used on that connection.

Currently, outbound proxies initiating direct connections will *always*
send `SessionProtocol` values communicating the protocol as understood
by the outbound proxy. However, this is not always the desired behavior.
Direct connections with `TransportHeader`s are used in two cases: for
gateway connections, and for ports which are marked as opaque. When the
inbound port is marked as opaque, the presence of a `SessionProtocol`
tells the inbound proxy to handle that connection as the indicated
protocol, which results in incorrect behavior when the inbound proxy's
ServerPolicy configures the target port as opaque (see #9888).

Therefore, the `Destination` proxy API has been updated to add a new
`ProtocolHint`, `Opaque`, which indicates that an outbound proxy should
_not_ send a `SessionProtocol` when initiating a direct connection, even
if the outbound proxy handled the connection as HTTP. This hint was
added to the proxy API in linkerd/linkerd2-proxy-api#197, and released
in `linkerd2-proxy-api` v0.8.0.

This branch updates the Destination controller's dependency on
`linkerd2-proxy-api` to v0.8.0, and changes the controller to send an
`Opaque` protocol hint when the target port is marked as opaque on the
destination pod. This should override the `H2` protocol hint that is
added when the destination is meshed. I've also added a new test for
this behavior.

Fixes #9888 (along with linkerd/linkerd2-proxy#2209, which changes the
proxy to actually handle the `Opaque` protocol hint).
risingspiral pushed a commit that referenced this pull request May 5, 2023
The outbound proxy handles endpoints with the `opaque_transport` flag by
opening a direct connection to the inbound proxy's inbound listener
port, and sending a ProtoBuf `TransportHeader` including the target port
of the originating outbound connection and an (optional)
`SessionProtocol` describing the protocol used on that connection.

Currently, outbound proxies initiating direct connections will *always*
send `SessionProtocol` values communicating the protocol as understood
by the outbound proxy. However, this is not always the desired behavior.
Direct connections with `TransportHeader`s are used in two cases: for
gateway connections, and for ports which are marked as opaque. When the
inbound port is marked as opaque, the presence of a `SessionProtocol`
tells the inbound proxy to handle that connection as the indicated
protocol, which results in incorrect behavior when the inbound proxy's
ServerPolicy configures the target port as opaque (see #9888).

Therefore, the `Destination` proxy API has been updated to add a new
`ProtocolHint`, `Opaque`, which indicates that an outbound proxy should
_not_ send a `SessionProtocol` when initiating a direct connection, even
if the outbound proxy handled the connection as HTTP. This hint was
added to the proxy API in linkerd/linkerd2-proxy-api#197, and released
in `linkerd2-proxy-api` v0.8.0.

This branch updates the Destination controller's dependency on
`linkerd2-proxy-api` to v0.8.0, and changes the controller to send an
`Opaque` protocol hint when the target port is marked as opaque on the
destination pod. This should override the `H2` protocol hint that is
added when the destination is meshed. I've also added a new test for
this behavior.

Fixes #9888 (along with linkerd/linkerd2-proxy#2209, which changes the
proxy to actually handle the `Opaque` protocol hint).

Signed-off-by: Eric Anderson <eric@buoyant.io>
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 returns 404 when HTTP connection is marked as opaque
4 participants