Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
fc67cf5
Add proxy proto tcp proxy changes. Add config factory.
wez470 Aug 3, 2020
2323504
Fix format.
wez470 Aug 3, 2020
f813691
Merge remote-tracking branch 'upstream/master'
wez470 Aug 17, 2020
0ee7c45
Add initial proxy proto integration tests.
wez470 Aug 17, 2020
71ba92d
Fix format.
wez470 Aug 17, 2020
1291529
Fix format.
wez470 Aug 21, 2020
6c91f31
Fix test.
wez470 Aug 21, 2020
4825f05
Fix proto doc comment.
wez470 Aug 21, 2020
473cbbe
Only add proxy proto filter state data if it doesn't exist.
wez470 Aug 30, 2020
00775ba
Fix format.
wez470 Aug 30, 2020
bf270c5
Fix generated shadow api.
wez470 Aug 30, 2020
07a1022
Add ip transparency docs.
wez470 Aug 30, 2020
7ebf550
Remove regex test matcher that failed on windows.
wez470 Aug 31, 2020
bd93847
Remove whitespace.
wez470 Aug 31, 2020
f89950a
Fix docs sentence.
wez470 Aug 31, 2020
1df7242
Merge remote-tracking branch 'upstream/master'
wez470 Sep 1, 2020
1fd4212
Add proxy proto conf example to docs.
wez470 Sep 13, 2020
e658e90
Merge remote-tracking branch 'upstream/master'
wez470 Sep 20, 2020
a655265
Update proxy proto security posture.
wez470 Sep 20, 2020
ffbfef5
Return nullptr if inner socket nullptr.
wez470 Sep 20, 2020
12ec394
Add health check integration test.
wez470 Sep 21, 2020
0b73ee1
Add TLS socket integration test.
wez470 Sep 22, 2020
3542aaa
Rename test.
wez470 Sep 22, 2020
df53fe7
Verify header in health check test.
wez470 Sep 23, 2020
defb718
Remove uneeded initializer.
wez470 Sep 23, 2020
55d11b3
Clang tidy proxy protocol tests.
wez470 Sep 23, 2020
4d9f748
Merge remote-tracking branch 'upstream/master'
wez470 Oct 6, 2020
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
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import "envoy/config/core/v3/base.proto";
import "envoy/config/core/v3/proxy_protocol.proto";

import "udpa/annotations/status.proto";
import "udpa/annotations/versioning.proto";
import "validate/validate.proto";

option java_package = "io.envoyproxy.envoy.extensions.transport_sockets.proxy_protocol.v3";
Expand All @@ -16,9 +15,10 @@ option (udpa.annotations.file_status).package_version_status = ACTIVE;

// [#protodoc-title: Upstream Proxy Protocol]
// [#extension: envoy.transport_sockets.upstream_proxy_protocol]
// [#not-implemented-hide:]

// Configuration for PROXY protocol socket
message ProxyProtocolUpstreamTransport {
// The PROXY protocol settings
config.core.v3.ProxyProtocolConfig config = 1;

// The underlying transport socket being wrapped.
Expand Down
24 changes: 22 additions & 2 deletions docs/root/intro/arch_overview/other_features/ip_transparency.rst
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,33 @@ metadata includes the source IP. Envoy supports consuming this information using
the downstream remote address for propagation into an
:ref:`x-forwarded-for <config_http_conn_man_headers_x-forwarded-for>` header. It can also be used in
conjunction with the
: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>`.
Here is an example config for setting up the socket:

.. code-block:: yaml

clusters:
- name: service1
connect_timeout: 0.25s
type: strict_dns
lb_policy: round_robin
transport_socket:
name: envoy.transport_sockets.upstream_proxy_protocol
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.proxy_protocol.v3.ProxyProtocolUpstreamTransport
config:
version: V1
transport_socket:
name: envoy.transport_sockets.raw_buffer
...

Note: If you are wrapping a TLS socket, the header will be sent before the TLS handshake occurs.

Some drawbacks to Proxy Protocol:

* It only supports TCP protocols.
* It requires upstream host support.
* Envoy cannot yet send it to the upstream.

.. _arch_overview_ip_transparency_original_src_listener:

Expand Down
1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ New Features
* lua: added new :ref:`source_code <envoy_v3_api_field_extensions.filters.http.lua.v3.LuaPerRoute.source_code>` field to support the dispatching of inline Lua code in per route configuration of Lua filter.
* overload management: add :ref:`scaling <envoy_v3_api_field_config.overload.v3.Trigger.scaled>` trigger for OverloadManager actions.
* postgres network filter: :ref:`metadata <config_network_filters_postgres_proxy_dynamic_metadata>` is produced based on SQL query.
* proxy protocol: added support for generating the header upstream using :ref:`Proxy Protocol Transport Socket <extension_envoy.transport_sockets.upstream_proxy_protocol>`.
* ratelimit: added :ref:`enable_x_ratelimit_headers <envoy_v3_api_msg_extensions.filters.http.ratelimit.v3.RateLimit>` option to enable `X-RateLimit-*` headers as defined in `draft RFC <https://tools.ietf.org/id/draft-polli-ratelimit-headers-03.html>`_.
* ratelimit: added :ref:`per route config <envoy_v3_api_msg_extensions.filters.http.ratelimit.v3.RateLimitPerRoute>` for rate limit filter.
* ratelimit: added support for optional :ref:`descriptor_key <envoy_v3_api_field_config.route.v3.RateLimit.Action.generic_key>` to Generic Key action.
Expand Down

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

1 change: 1 addition & 0 deletions source/common/tcp_proxy/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ envoy_cc_library(
"//source/common/network:cidr_range_lib",
"//source/common/network:filter_lib",
"//source/common/network:hash_policy_lib",
"//source/common/network:proxy_protocol_filter_state_lib",
"//source/common/network:transport_socket_options_lib",
"//source/common/network:upstream_server_name_lib",
"//source/common/network:utility_lib",
Expand Down
13 changes: 13 additions & 0 deletions source/common/tcp_proxy/tcp_proxy.cc
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "common/common/utility.h"
#include "common/config/well_known_names.h"
#include "common/network/application_protocol.h"
#include "common/network/proxy_protocol_filter_state.h"
#include "common/network/transport_socket_options_impl.h"
#include "common/network/upstream_server_name.h"
#include "common/router/metadatamatchcriteria_impl.h"
Expand Down Expand Up @@ -414,6 +415,18 @@ Network::FilterStatus Filter::initializeUpstreamConnection() {
}

if (downstreamConnection()) {
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);
}
Comment on lines +418 to +429
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.

transport_socket_options_ = Network::TransportSocketOptionsUtility::fromFilterState(
downstreamConnection()->streamInfo().filterState());
}
Expand Down
2 changes: 1 addition & 1 deletion source/extensions/extensions_build_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ EXTENSIONS = {
#

"envoy.transport_sockets.alts": "//source/extensions/transport_sockets/alts:config",
"envoy.transport_sockets.upstream_proxy_protocol": "//source/extensions/transport_sockets/proxy_protocol:upstream_proxy_protocol",
"envoy.transport_sockets.upstream_proxy_protocol": "//source/extensions/transport_sockets/proxy_protocol:upstream_config",
"envoy.transport_sockets.raw_buffer": "//source/extensions/transport_sockets/raw_buffer:config",
"envoy.transport_sockets.tap": "//source/extensions/transport_sockets/tap:config",
"envoy.transport_sockets.quic": "//source/extensions/quic_listeners/quiche:quic_factory_lib",
Expand Down
19 changes: 17 additions & 2 deletions source/extensions/transport_sockets/proxy_protocol/BUILD
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_extension",
"envoy_cc_library",
"envoy_extension_package",
)

Expand All @@ -9,11 +10,25 @@ licenses(["notice"]) # Apache 2
envoy_extension_package()

envoy_cc_extension(
name = "upstream_config",
srcs = ["config.cc"],
hdrs = ["config.h"],
security_posture = "robust_to_untrusted_downstream_and_upstream", # header generated in Envoy, so can't be faked
deps = [
":upstream_proxy_protocol",
"//include/envoy/network:transport_socket_interface",
"//include/envoy/registry",
"//include/envoy/server:transport_socket_config_interface",
"//source/common/config:utility_lib",
"//source/extensions/transport_sockets:well_known_names",
"@envoy_api//envoy/extensions/transport_sockets/proxy_protocol/v3:pkg_cc_proto",
],
)

envoy_cc_library(
name = "upstream_proxy_protocol",
srcs = ["proxy_protocol.cc"],
hdrs = ["proxy_protocol.h"],
security_posture = "robust_to_untrusted_downstream",
undocumented = True,
deps = [
"//include/envoy/network:connection_interface",
"//include/envoy/network:transport_socket_interface",
Expand Down
46 changes: 46 additions & 0 deletions source/extensions/transport_sockets/proxy_protocol/config.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#include "extensions/transport_sockets/proxy_protocol/config.h"

#include "envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.pb.h"
#include "envoy/extensions/transport_sockets/proxy_protocol/v3/upstream_proxy_protocol.pb.validate.h"
#include "envoy/registry/registry.h"

#include "common/config/utility.h"

#include "extensions/transport_sockets/proxy_protocol/proxy_protocol.h"

namespace Envoy {
namespace Extensions {
namespace TransportSockets {
namespace ProxyProtocol {

Network::TransportSocketFactoryPtr
UpstreamProxyProtocolSocketConfigFactory::createTransportSocketFactory(
const Protobuf::Message& message,
Server::Configuration::TransportSocketFactoryContext& context) {
const auto& outer_config =
MessageUtil::downcastAndValidate<const envoy::extensions::transport_sockets::proxy_protocol::
v3::ProxyProtocolUpstreamTransport&>(
message, context.messageValidationVisitor());
auto& inner_config_factory = Config::Utility::getAndCheckFactory<
Server::Configuration::UpstreamTransportSocketConfigFactory>(outer_config.transport_socket());
ProtobufTypes::MessagePtr inner_factory_config = Config::Utility::translateToFactoryConfig(
outer_config.transport_socket(), context.messageValidationVisitor(), inner_config_factory);
auto inner_transport_factory =
inner_config_factory.createTransportSocketFactory(*inner_factory_config, context);
return std::make_unique<UpstreamProxyProtocolSocketFactory>(std::move(inner_transport_factory),
outer_config.config());
}

ProtobufTypes::MessagePtr UpstreamProxyProtocolSocketConfigFactory::createEmptyConfigProto() {
return std::make_unique<
envoy::extensions::transport_sockets::proxy_protocol::v3::ProxyProtocolUpstreamTransport>();
;
}

REGISTER_FACTORY(UpstreamProxyProtocolSocketConfigFactory,
Server::Configuration::UpstreamTransportSocketConfigFactory);

} // namespace ProxyProtocol
} // namespace TransportSockets
} // namespace Extensions
} // namespace Envoy
29 changes: 29 additions & 0 deletions source/extensions/transport_sockets/proxy_protocol/config.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include "envoy/server/transport_socket_config.h"

#include "extensions/transport_sockets/well_known_names.h"

namespace Envoy {
namespace Extensions {
namespace TransportSockets {
namespace ProxyProtocol {

/**
* Config registration for the proxy protocol wrapper for transport socket factory.
* @see TransportSocketConfigFactory.
*/
class UpstreamProxyProtocolSocketConfigFactory
: public Server::Configuration::UpstreamTransportSocketConfigFactory {
public:
std::string name() const override { return TransportSocketNames::get().UpstreamProxyProtocol; }
ProtobufTypes::MessagePtr createEmptyConfigProto() override;
Network::TransportSocketFactoryPtr createTransportSocketFactory(
const Protobuf::Message& config,
Server::Configuration::TransportSocketFactoryContext& context) override;
};

} // namespace ProxyProtocol
} // namespace TransportSockets
} // namespace Extensions
} // namespace Envoy
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

#include "extensions/common/proxy_protocol/proxy_protocol_header.h"

using envoy::config::core::v3::ProxyProtocolConfig;
using envoy::config::core::v3::ProxyProtocolConfig_Version;

namespace Envoy {
Expand All @@ -26,7 +27,6 @@ void UpstreamProxyProtocolSocket::setTransportSocketCallbacks(
Network::TransportSocketCallbacks& callbacks) {
transport_socket_->setTransportSocketCallbacks(callbacks);
callbacks_ = &callbacks;
generateHeader();
}

Network::IoResult UpstreamProxyProtocolSocket::doWrite(Buffer::Instance& buffer, bool end_stream) {
Expand All @@ -51,7 +51,7 @@ void UpstreamProxyProtocolSocket::generateHeader() {
}

void UpstreamProxyProtocolSocket::generateHeaderV1() {
// Default to local addresses
// Default to local addresses (used if no downstream connection exists e.g. health checks)
auto src_addr = callbacks_->connection().localAddress();
auto dst_addr = callbacks_->connection().remoteAddress();

Expand Down Expand Up @@ -100,6 +100,29 @@ Network::IoResult UpstreamProxyProtocolSocket::writeHeader() {
return {action, bytes_written, false};
}

void UpstreamProxyProtocolSocket::onConnected() {
generateHeader();
transport_socket_->onConnected();
}

UpstreamProxyProtocolSocketFactory::UpstreamProxyProtocolSocketFactory(
Network::TransportSocketFactoryPtr transport_socket_factory, ProxyProtocolConfig config)
: transport_socket_factory_(std::move(transport_socket_factory)), config_(config) {}

Network::TransportSocketPtr UpstreamProxyProtocolSocketFactory::createTransportSocket(
Network::TransportSocketOptionsSharedPtr options) const {
auto inner_socket = transport_socket_factory_->createTransportSocket(options);
if (inner_socket == nullptr) {
return nullptr;
}
return std::make_unique<UpstreamProxyProtocolSocket>(std::move(inner_socket), options,
config_.version());
}

bool UpstreamProxyProtocolSocketFactory::implementsSecureTransport() const {
return transport_socket_factory_->implementsSecureTransport();
}

} // namespace ProxyProtocol
} // namespace TransportSockets
} // namespace Extensions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

#include "extensions/transport_sockets/common/passthrough.h"

using envoy::config::core::v3::ProxyProtocolConfig;
using envoy::config::core::v3::ProxyProtocolConfig_Version;

namespace Envoy {
Expand All @@ -25,6 +26,7 @@ class UpstreamProxyProtocolSocket : public TransportSockets::PassthroughSocket,

void setTransportSocketCallbacks(Network::TransportSocketCallbacks& callbacks) override;
Network::IoResult doWrite(Buffer::Instance& buffer, bool end_stream) override;
void onConnected() override;

private:
void generateHeader();
Expand All @@ -38,6 +40,21 @@ class UpstreamProxyProtocolSocket : public TransportSockets::PassthroughSocket,
ProxyProtocolConfig_Version version_{ProxyProtocolConfig_Version::ProxyProtocolConfig_Version_V1};
};

class UpstreamProxyProtocolSocketFactory : public Network::TransportSocketFactory {
public:
UpstreamProxyProtocolSocketFactory(Network::TransportSocketFactoryPtr transport_socket_factory,
ProxyProtocolConfig config);

// Network::TransportSocketFactory
Network::TransportSocketPtr
createTransportSocket(Network::TransportSocketOptionsSharedPtr options) const override;
bool implementsSecureTransport() const override;

private:
Network::TransportSocketFactoryPtr transport_socket_factory_;
ProxyProtocolConfig config_;
};

} // namespace ProxyProtocol
} // namespace TransportSockets
} // namespace Extensions
Expand Down
2 changes: 2 additions & 0 deletions test/common/network/transport_socket_options_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ TEST_F(TransportSocketOptionsImplTest, UpstreamServer) {
auto transport_socket_options = TransportSocketOptionsUtility::fromFilterState(filter_state_);
EXPECT_EQ(absl::make_optional<std::string>("www.example.com"),
transport_socket_options->serverNameOverride());
EXPECT_EQ("202.168.0.13:52000",
transport_socket_options->proxyProtocolOptions()->src_addr_->asStringView());
EXPECT_TRUE(transport_socket_options->applicationProtocolListOverride().empty());
}

Expand Down
14 changes: 14 additions & 0 deletions test/extensions/transport_sockets/proxy_protocol/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,19 @@ envoy_extension_cc_test(
"//test/mocks/network:io_handle_mocks",
"//test/mocks/network:network_mocks",
"//test/mocks/network:transport_socket_mocks",
"@envoy_api//envoy/config/core/v3:pkg_cc_proto",
],
)

envoy_extension_cc_test(
name = "proxy_protocol_integration_test",
srcs = ["proxy_protocol_integration_test.cc"],
extension_name = "envoy.transport_sockets.upstream_proxy_protocol",
deps = [
"//source/extensions/filters/network/tcp_proxy:config",
"//source/extensions/transport_sockets/proxy_protocol:upstream_config",
"//test/integration:integration_lib",
"@envoy_api//envoy/config/core/v3:pkg_cc_proto",
"@envoy_api//envoy/extensions/transport_sockets/proxy_protocol/v3:pkg_cc_proto",
],
)
Loading