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

DRAFT: Proxy Protocol Transport Socket #10682

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ extensions/filters/common/original_src @snowp @klarose
/*/extensions/transport_sockets/alts @htuch @yangminzhu
# tls transport socket extension
/*/extensions/transport_sockets/tls @PiotrSikora @lizan
# proxy protocol socket extension
/*/extensions/transport_sockets/proxy_protocol @alyssawilk @wez470
# sni_cluster extension
/*/extensions/filters/network/sni_cluster @rshriram @lizan
# sni_dynamic_forward_proxy extension
Expand Down
2 changes: 2 additions & 0 deletions api/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ proto_library(
"//envoy/config/trace/v2:pkg",
"//envoy/config/trace/v2alpha:pkg",
"//envoy/config/transport_socket/alts/v2alpha:pkg",
"//envoy/config/transport_socket/proxy_protocol/v2:pkg",
"//envoy/config/transport_socket/raw_buffer/v2:pkg",
"//envoy/config/transport_socket/tap/v2alpha:pkg",
"//envoy/data/accesslog/v2:pkg",
Expand Down Expand Up @@ -224,6 +225,7 @@ proto_library(
"//envoy/extensions/retry/host/omit_host_metadata/v3:pkg",
"//envoy/extensions/retry/priority/previous_priorities/v3:pkg",
"//envoy/extensions/transport_sockets/alts/v3:pkg",
"//envoy/extensions/transport_sockets/proxy_protocol/v3:pkg",
"//envoy/extensions/transport_sockets/raw_buffer/v3:pkg",
"//envoy/extensions/transport_sockets/tap/v3:pkg",
"//envoy/extensions/transport_sockets/tls/v3:pkg",
Expand Down
12 changes: 12 additions & 0 deletions api/envoy/config/transport_socket/proxy_protocol/v2/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# DO NOT EDIT. This file is generated by tools/proto_sync.py.

load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package")

licenses(["notice"]) # Apache 2

api_proto_package(
deps = [
"//envoy/api/v2/core:pkg",
"@com_github_cncf_udpa//udpa/annotations:pkg",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
syntax = "proto3";

package envoy.config.transport_socket.proxy_protocol.v2;

import "envoy/api/v2/core/base.proto";

import "udpa/annotations/migrate.proto";
import "validate/validate.proto";

option java_package = "io.envoyproxy.envoy.config.transport_socket.proxy_protocol.v2";
option java_outer_classname = "ProxyProtocolProto";
option java_multiple_files = true;
option (udpa.annotations.file_migrate).move_to_package =
"envoy.extensions.transport_sockets.proxy_protocol.v3";

// [#protodoc-title: Proxy Protocol]
// PROXY protocol transport socket.
// [#extension: envoy.transport_sockets.proxy_protocol]

message ProxyProtocol {
wez470 marked this conversation as resolved.
Show resolved Hide resolved
enum Version {
// PROXY protocol version 1. Human readable format.
V1 = 0;

// PROXY protocol version 2. Binary format.
V2 = 1;
}

// The PROXY protocol version to use. See https://www.haproxy.org/download/2.1/doc/proxy-protocol.txt for details
Version version = 1;

// The underlying transport socket being wrapped.
api.v2.core.TransportSocket transport_socket = 2 [(validate.rules).message = {required: true}];
}
13 changes: 13 additions & 0 deletions api/envoy/extensions/transport_sockets/proxy_protocol/v3/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# DO NOT EDIT. This file is generated by tools/proto_sync.py.

load("@envoy_api//bazel:api_build_system.bzl", "api_proto_package")

licenses(["notice"]) # Apache 2

api_proto_package(
deps = [
"//envoy/config/core/v3:pkg",
"//envoy/config/transport_socket/proxy_protocol/v2:pkg",
"@com_github_cncf_udpa//udpa/annotations:pkg",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
syntax = "proto3";

package envoy.extensions.transport_sockets.proxy_protocol.v3;

import "envoy/config/core/v3/base.proto";

import "udpa/annotations/versioning.proto";

import "validate/validate.proto";

option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.proxy_protocol.v3";
option java_outer_classname = "ProxyProtocolProto";
option java_multiple_files = true;

// [#protodoc-title: Proxy Protocol]
// PROXY protocol transport socket.
// [#extension: envoy.transport_sockets.proxy_protocol]

message ProxyProtocol {
option (udpa.annotations.versioning).previous_message_type =
"envoy.config.transport_socket.proxy_protocol.v2.ProxyProtocol";

enum Version {
// PROXY protocol version 1. Human readable format.
V1 = 0;

// PROXY protocol version 2. Binary format.
V2 = 1;
}

// The PROXY protocol version to use. See https://www.haproxy.org/download/2.1/doc/proxy-protocol.txt for details
Version version = 1;

// The underlying transport socket being wrapped.
config.core.v3.TransportSocket transport_socket = 2 [(validate.rules).message = {required: true}];
}
1 change: 1 addition & 0 deletions api/versioning/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,7 @@ proto_library(
"//envoy/config/trace/v2:pkg",
"//envoy/config/trace/v2alpha:pkg",
"//envoy/config/transport_socket/alts/v2alpha:pkg",
"//envoy/config/transport_socket/proxy_protocol/v2:pkg",
"//envoy/config/transport_socket/raw_buffer/v2:pkg",
"//envoy/config/transport_socket/tap/v2alpha:pkg",
"//envoy/data/accesslog/v2:pkg",
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions include/envoy/network/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ envoy_cc_library(
name = "transport_socket_interface",
hdrs = ["transport_socket.h"],
deps = [
":address_interface",
":io_handle_interface",
"//include/envoy/buffer:buffer_interface",
"//include/envoy/ssl:connection_interface",
Expand Down
15 changes: 15 additions & 0 deletions include/envoy/network/transport_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "envoy/buffer/buffer.h"
#include "envoy/common/pure.h"
#include "envoy/network/address.h"
#include "envoy/network/io_handle.h"
#include "envoy/ssl/connection.h"

Expand Down Expand Up @@ -162,6 +163,14 @@ using TransportSocketPtr = std::unique_ptr<TransportSocket>;
*/
class TransportSocketOptions {
public:
struct DownstreamAddresses {
const std::string remote_addr_;
wez470 marked this conversation as resolved.
Show resolved Hide resolved
const std::string local_addr_;
const uint32_t remote_port_;
const uint32_t local_port_;
const Address::IpVersion version_;
};

virtual ~TransportSocketOptions() = default;

/**
Expand All @@ -184,6 +193,12 @@ class TransportSocketOptions {
*/
virtual const std::vector<std::string>& applicationProtocolListOverride() const PURE;

/**
* @return the optional downstream address info. Can be used in upstream sockets that
* need awareness of downstream info e.g. proxy_protocol.
*/
virtual const absl::optional<DownstreamAddresses> downstreamAddresses() const PURE;
Copy link
Contributor

Choose a reason for hiding this comment

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

@envoyproxy/maintainers for future-proofing sanity check
We use our own custom proxy proto header equivalent in-house as we add some non-standard fields IMO are fairly generically useful. Do y'all know if anyone else does this? I already have some ideas of how we can do our custom stuff with stock Envoy but if we're not the only ones who play around this way I'd be inclined to do a review pass with generic extensibility in mind.

Copy link
Contributor

Choose a reason for hiding this comment

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

proxy protocol v2 has support to extensions. Can your internal version be mapped on this?

https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt

If the length specified in the PROXY protocol header indicates that additional
bytes are part of the header beyond the address information, a receiver may
choose to skip over and ignore those bytes, or attempt to interpret those
bytes.

The information in those bytes will be arranged in Type-Length-Value (TLV
vectors) in the following format.  The first byte is the Type of the vector.
The second two bytes represent the length in bytes of the value (not included
the Type and Length bytes), and following the length field is the number of
bytes specified by the length.

        struct pp2_tlv {
            uint8_t type;
            uint8_t length_hi;
            uint8_t length_lo;
            uint8_t value[0];
        };

A receiver may choose to skip over and ignore the TLVs he is not interested in
or he does not understand. Senders can generate the TLVs only for
the information they choose to publish.

The following types have already been registered for the <type> field :

        #define PP2_TYPE_ALPN           0x01
        #define PP2_TYPE_AUTHORITY      0x02
        #define PP2_TYPE_CRC32C         0x03
        #define PP2_TYPE_NOOP           0x04
        #define PP2_TYPE_UNIQUE_ID      0x05
        #define PP2_TYPE_SSL            0x20
        #define PP2_SUBTYPE_SSL_VERSION 0x21
        #define PP2_SUBTYPE_SSL_CN      0x22
        #define PP2_SUBTYPE_SSL_CIPHER  0x23
        #define PP2_SUBTYPE_SSL_SIG_ALG 0x24
        #define PP2_SUBTYPE_SSL_KEY_ALG 0x25
        #define PP2_TYPE_NETNS          0x30

Copy link
Contributor

Choose a reason for hiding this comment

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

Possibly, but if other folks are likely to use the extension I think that answers my question - that should be good enough for the common case. Thanks!


/**
* @param vector of bytes to which the option should append hash key data that will be used
* to separate connections based on the option. Any data already in the key vector must
Expand Down
12 changes: 11 additions & 1 deletion source/common/network/transport_socket_options_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ void TransportSocketOptionsImpl::hashKey(std::vector<uint8_t>& key) const {

TransportSocketOptionsSharedPtr
TransportSocketOptionsUtility::fromFilterState(const StreamInfo::FilterState& filter_state) {
return fromFilterStateWithDownstreamAddrs(filter_state, absl::nullopt);
}

TransportSocketOptionsSharedPtr TransportSocketOptionsUtility::fromFilterStateWithDownstreamAddrs(
const StreamInfo::FilterState& filter_state,
absl::optional<Network::TransportSocketOptions::DownstreamAddresses> down_addrs) {
absl::string_view server_name;
std::vector<std::string> application_protocols;
std::vector<std::string> subject_alt_names;
Expand All @@ -59,9 +65,13 @@ TransportSocketOptionsUtility::fromFilterState(const StreamInfo::FilterState& fi
needs_transport_socket_options = true;
}

if (down_addrs.has_value()) {
needs_transport_socket_options = true;
}

if (needs_transport_socket_options) {
return std::make_shared<Network::TransportSocketOptionsImpl>(
server_name, std::move(subject_alt_names), std::move(application_protocols));
server_name, std::move(subject_alt_names), std::move(application_protocols), down_addrs);
} else {
return nullptr;
}
Expand Down
22 changes: 20 additions & 2 deletions source/common/network/transport_socket_options_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@ class TransportSocketOptionsImpl : public TransportSocketOptions {
public:
TransportSocketOptionsImpl(absl::string_view override_server_name = "",
std::vector<std::string>&& override_verify_san_list = {},
std::vector<std::string>&& override_alpn = {})
std::vector<std::string>&& override_alpn = {},
absl::optional<Network::TransportSocketOptions::DownstreamAddresses>
downstream_addresses = absl::nullopt)
: override_server_name_(override_server_name.empty()
? absl::nullopt
: absl::optional<std::string>(override_server_name)),
override_verify_san_list_{std::move(override_verify_san_list)},
override_alpn_list_{std::move(override_alpn)} {}
override_alpn_list_{std::move(override_alpn)}, downstream_addresses_(downstream_addresses) {
}

// Network::TransportSocketOptions
const absl::optional<std::string>& serverNameOverride() const override {
Expand All @@ -27,12 +30,17 @@ class TransportSocketOptionsImpl : public TransportSocketOptions {
const std::vector<std::string>& applicationProtocolListOverride() const override {
return override_alpn_list_;
}
const absl::optional<Network::TransportSocketOptions::DownstreamAddresses>
downstreamAddresses() const override {
return downstream_addresses_;
}
void hashKey(std::vector<uint8_t>& key) const override;

private:
const absl::optional<std::string> override_server_name_;
const std::vector<std::string> override_verify_san_list_;
const std::vector<std::string> override_alpn_list_;
const absl::optional<Network::TransportSocketOptions::DownstreamAddresses> downstream_addresses_;
};

class TransportSocketOptionsUtility {
Expand All @@ -45,6 +53,16 @@ class TransportSocketOptionsUtility {
*/
static TransportSocketOptionsSharedPtr
fromFilterState(const StreamInfo::FilterState& stream_info);
/**
* Construct TransportSocketOptions from StreamInfo::FilterState, using UpstreamServerName
* and ApplicationProtocols key in the filter state as well as optional downstream address
* info.
* @returns TransportSocketOptionsSharedPtr a shared pointer to the transport socket options,
* nullptr if nothing is in the filter state or no downstream address info is supplied.
*/
static TransportSocketOptionsSharedPtr fromFilterStateWithDownstreamAddrs(
const StreamInfo::FilterState& stream_info,
absl::optional<Network::TransportSocketOptions::DownstreamAddresses> down_addrs);
};

} // namespace Network
Expand Down
17 changes: 15 additions & 2 deletions source/common/tcp_proxy/tcp_proxy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -406,8 +406,21 @@ Network::FilterStatus Filter::initializeUpstreamConnection() {
}

if (downstreamConnection()) {
transport_socket_options_ = Network::TransportSocketOptionsUtility::fromFilterState(
downstreamConnection()->streamInfo().filterState());
if (add_addrs_to_transport_socket_options_) {
auto down_addrs = Network::TransportSocketOptions::DownstreamAddresses{
.remote_addr_ = read_callbacks_->connection().remoteAddress()->ip()->addressAsString(),
.local_addr_ = read_callbacks_->connection().localAddress()->ip()->addressAsString(),
.remote_port_ = read_callbacks_->connection().remoteAddress()->ip()->port(),
.local_port_ = read_callbacks_->connection().localAddress()->ip()->port(),
.version_ = read_callbacks_->connection().remoteAddress()->ip()->version()};
transport_socket_options_ =
Network::TransportSocketOptionsUtility::fromFilterStateWithDownstreamAddrs(
downstreamConnection()->streamInfo().filterState(),
absl::optional<Network::TransportSocketOptions::DownstreamAddresses>(down_addrs));
} else {
transport_socket_options_ = Network::TransportSocketOptionsUtility::fromFilterState(
downstreamConnection()->streamInfo().filterState());
}
}

if (!config_->tunnelingConfig()) {
Expand Down
1 change: 1 addition & 0 deletions source/common/tcp_proxy/tcp_proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ class Filter : public Network::ReadFilter,
Network::TransportSocketOptionsSharedPtr transport_socket_options_;
uint32_t connect_attempts_{};
bool connecting_{};
bool add_addrs_to_transport_socket_options_{true};
};

// This class deals with an upstream connection that needs to finish flushing, when the downstream
Expand Down
1 change: 1 addition & 0 deletions source/extensions/extensions_build_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,7 @@ EXTENSIONS = {
#

"envoy.transport_sockets.alts": "//source/extensions/transport_sockets/alts:config",
"envoy.transport_sockets.proxy_protocol": "//source/extensions/transport_sockets/proxy_protocol:config",
"envoy.transport_sockets.raw_buffer": "//source/extensions/transport_sockets/raw_buffer:config",
"envoy.transport_sockets.tap": "//source/extensions/transport_sockets/tap:config",

Expand Down
Loading