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

tcp-proxy: support proxy-protocol tlvs #38131

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
9 changes: 9 additions & 0 deletions api/envoy/config/core/v3/proxy_protocol.proto
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ message ProxyProtocolPassThroughTLVs {
repeated uint32 tlv_type = 2 [(validate.rules).repeated = {items {uint32 {lt: 256}}}];
}

message ProxyProtocolTLV {
// TLV type is defined as uint8_t in proxy protocol. See `the spec
// <https://www.haproxy.org/download/2.1/doc/proxy-protocol.txt>`_ for details.
uint32 type = 1 [(validate.rules).uint32 = {lt: 256}];

// TLV value.
bytes value = 2;
}

message ProxyProtocolConfig {
enum Version {
// PROXY protocol version 1. Human readable format.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package envoy.extensions.filters.network.tcp_proxy.v3;
import "envoy/config/accesslog/v3/accesslog.proto";
import "envoy/config/core/v3/base.proto";
import "envoy/config/core/v3/config_source.proto";
import "envoy/config/core/v3/proxy_protocol.proto";
import "envoy/type/v3/hash_policy.proto";

import "google/protobuf/duration.proto";
Expand All @@ -25,7 +26,7 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;
// TCP Proxy :ref:`configuration overview <config_network_filters_tcp_proxy>`.
// [#extension: envoy.filters.network.tcp_proxy]

// [#next-free-field: 18]
// [#next-free-field: 19]
message TcpProxy {
option (udpa.annotations.versioning).previous_message_type =
"envoy.config.filter.network.tcp_proxy.v2.TcpProxy";
Expand Down Expand Up @@ -255,4 +256,8 @@ message TcpProxy {

// Additional access log options for TCP Proxy.
TcpAccessLogOptions access_log_options = 17;

// If set, the given proxy protocol TLVs are passed to the upstream connection
// by the TCP proxy filter.
repeated config.core.v3.ProxyProtocolTLV proxy_protocol_tlvs = 18;
}
5 changes: 5 additions & 0 deletions changelogs/current.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,5 +95,10 @@ new_features:
change: |
Reporting a locality_stats to LRS server when rq_issued > 0, disable by setting runtime guard
``envoy.reloadable_features.report_load_with_rq_issued`` to ``false``.
- area: tcp_proxy
change: |
Added :ref:`proxy_protocol_tlvs
<envoy_v3_api_field_extensions.filters.network.tcp_proxy.v3.TcpProxy.proxy_protocol_tlvs>` to the TCP proxy filter.
This field allows specifying the list of Proxy Protocol TLVs to be sent in the Proxy Protocol header.

deprecated:
1 change: 1 addition & 0 deletions source/common/tcp_proxy/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ envoy_cc_library(
"//source/common/stream_info:uint64_accessor_lib",
"//source/common/upstream:load_balancer_context_base_lib",
"//source/common/upstream:od_cds_api_lib",
"//source/extensions/common/proxy_protocol:proxy_protocol_header_lib",
"//source/extensions/upstreams/tcp/generic:config",
"@envoy_api//envoy/config/accesslog/v3:pkg_cc_proto",
"@envoy_api//envoy/extensions/filters/network/tcp_proxy/v3:pkg_cc_proto",
Expand Down
9 changes: 8 additions & 1 deletion source/common/tcp_proxy/tcp_proxy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
#include "source/common/stream_info/stream_id_provider_impl.h"
#include "source/common/stream_info/uint64_accessor_impl.h"
#include "source/common/tracing/http_tracer_impl.h"
#include "source/extensions/common/proxy_protocol/proxy_protocol_header.h"

namespace Envoy {
namespace TcpProxy {
Expand Down Expand Up @@ -152,6 +153,11 @@ Config::SharedConfig::SharedConfig(
on_demand_config_ =
std::make_unique<OnDemandConfig>(config.on_demand(), context, *stats_scope_);
}

if (!config.proxy_protocol_tlvs().empty()) {
proxy_protocol_tlvs_ =
Extensions::Common::ProxyProtocol::parseTLVs(config.proxy_protocol_tlvs());
}
}

Config::Config(const envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy& config,
Expand Down Expand Up @@ -490,7 +496,8 @@ Network::FilterStatus Filter::establishUpstreamConnection() {
Network::ProxyProtocolFilterState::key(),
std::make_shared<Network::ProxyProtocolFilterState>(Network::ProxyProtocolData{
downstream_connection.connectionInfoProvider().remoteAddress(),
downstream_connection.connectionInfoProvider().localAddress()}),
downstream_connection.connectionInfoProvider().localAddress(),
config_->proxyProtocolTLVs()}),
StreamInfo::FilterState::StateType::ReadOnly,
StreamInfo::FilterState::LifeSpan::Connection);
}
Expand Down
7 changes: 7 additions & 0 deletions source/common/tcp_proxy/tcp_proxy.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@ class Config {
return {};
}
}
const Network::ProxyProtocolTLVVector& proxyProtocolTLVs() const {
return proxy_protocol_tlvs_;
}

private:
static TcpProxyStats generateStats(Stats::Scope& scope);
Expand All @@ -263,6 +266,7 @@ class Config {
absl::optional<std::chrono::milliseconds> access_log_flush_interval_;
std::unique_ptr<TunnelingConfigHelper> tunneling_config_helper_;
std::unique_ptr<OnDemandConfig> on_demand_config_;
Network::ProxyProtocolTLVVector proxy_protocol_tlvs_;
};

using SharedConfigSharedPtr = std::shared_ptr<SharedConfig>;
Expand Down Expand Up @@ -317,6 +321,9 @@ class Config {
Random::RandomGenerator& randomGenerator() { return random_generator_; }
bool flushAccessLogOnConnected() const { return shared_config_->flushAccessLogOnConnected(); }
Regex::Engine& regexEngine() const { return regex_engine_; }
Network::ProxyProtocolTLVVector proxyProtocolTLVs() const {
return shared_config_->proxyProtocolTLVs();
}

private:
struct SimpleRouteImpl : public Route {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,16 @@ void generateV2LocalHeader(Buffer::Instance& out) {
out.add(addr_fam_protocol_and_length, 4);
}

Network::ProxyProtocolTLVVector
parseTLVs(absl::Span<const envoy::config::core::v3::ProxyProtocolTLV* const> tlvs) {
Network::ProxyProtocolTLVVector tlv_vector;
for (const auto& tlv : tlvs) {
const std::vector<unsigned char> value(tlv->value().begin(), tlv->value().end());
tlv_vector.push_back({static_cast<uint8_t>(tlv->type()), value});
}
return tlv_vector;
}

} // namespace ProxyProtocol
} // namespace Common
} // namespace Extensions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ void generateV2LocalHeader(Buffer::Instance& out);
bool generateV2Header(const Network::ProxyProtocolData& proxy_proto_data, Buffer::Instance& out,
bool pass_all_tlvs, const absl::flat_hash_set<uint8_t>& pass_through_tlvs);

// Parses proxy protocol TLVs from the given configuration.
Network::ProxyProtocolTLVVector
parseTLVs(absl::Span<const envoy::config::core::v3::ProxyProtocolTLV* const> tlvs);

} // namespace ProxyProtocol
} // namespace Common
} // namespace Extensions
Expand Down
31 changes: 31 additions & 0 deletions test/common/tcp_proxy/tcp_proxy_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include "source/common/buffer/buffer_impl.h"
#include "source/common/network/address_impl.h"
#include "source/common/network/application_protocol.h"
#include "source/common/network/proxy_protocol_filter_state.h"
#include "source/common/network/socket_option_factory.h"
#include "source/common/network/transport_socket_options_impl.h"
#include "source/common/network/upstream_server_name.h"
Expand Down Expand Up @@ -1648,6 +1649,36 @@ TEST_P(TcpProxyTest, UpstreamStartSecureTransport) {
filter_->startUpstreamSecureTransport();
}

// Test that the proxy protocol TLV is set.
TEST_P(TcpProxyTest, SetTLV) {
envoy::extensions::filters::network::tcp_proxy::v3::TcpProxy config = defaultConfig();
auto* tlv = config.add_proxy_protocol_tlvs();
tlv->set_type(0xF1);
tlv->set_value("tst");

setup(1, config);
raiseEventUpstreamConnected(0);

// Verify the downstream TLV is set.
auto& downstream_info = filter_callbacks_.connection_.streamInfo();
auto header =
downstream_info.filterState()->getDataReadOnly<Envoy::Network::ProxyProtocolFilterState>(
Envoy::Network::ProxyProtocolFilterState::key());
ASSERT_TRUE(header != nullptr);
auto& tlvs = header->value().tlv_vector_;
ASSERT_EQ(tlvs.size(), 1);
ASSERT_EQ(tlvs[0].type, 0xF1);
ASSERT_EQ(std::string(tlvs[0].value.begin(), tlvs[0].value.end()), "tst");

// Verify the upstream TLV is set.
auto upstreamHeader = filter_->upstreamTransportSocketOptions()->proxyProtocolOptions();
ASSERT_TRUE(upstreamHeader.has_value());
auto& upstreamTlvs = upstreamHeader->tlv_vector_;
ASSERT_EQ(upstreamTlvs.size(), 1);
ASSERT_EQ(upstreamTlvs[0].type, 0xF1);
ASSERT_EQ(std::string(upstreamTlvs[0].value.begin(), upstreamTlvs[0].value.end()), "tst");
}

INSTANTIATE_TEST_SUITE_P(WithOrWithoutUpstream, TcpProxyTest, ::testing::Bool());

TEST(PerConnectionCluster, ObjectFactory) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,23 @@ TEST(ProxyProtocolHeaderTest, GeneratesV2WithTLVExceedingLengthLimit) {
generateV2Header(proxy_proto_data, buff, true, {}));
}

TEST(ProxyProtocolHeaderTest, ParseTLVs) {
Protobuf::RepeatedPtrField<envoy::config::core::v3::ProxyProtocolTLV> tlvs;
auto* tlv = tlvs.Add();
tlv->set_type(0x1);
tlv->set_value("tlv1");
tlv = tlvs.Add();
tlv->set_type(0xE1);
tlv->set_value("tlv2");
const auto tlv_vector = parseTLVs(tlvs);

EXPECT_EQ(2, tlv_vector.size());
EXPECT_EQ(0x1, tlv_vector[0].type);
EXPECT_EQ(std::vector<unsigned char>({'t', 'l', 'v', '1'}), tlv_vector[0].value);
EXPECT_EQ(0xE1, tlv_vector[1].type);
EXPECT_EQ(std::vector<unsigned char>({'t', 'l', 'v', '2'}), tlv_vector[1].value);
}

} // namespace
} // namespace ProxyProtocol
} // namespace Common
Expand Down