From 27880b4a046d6c3897b5a504c14b42fe8e17dc9c Mon Sep 17 00:00:00 2001 From: Yan Xue <3491507+yxue@users.noreply.github.com> Date: Fri, 4 Oct 2019 01:59:31 -0700 Subject: [PATCH] tls: override ALPN in transport socket options (#8470) Description: Override ALPN in transport socket options Risk Level: Low Testing: Unit test Docs Changes: N/A Release Notes: N/A Fixes #Issue: Part of #8197 Signed-off-by: crazyxy --- include/envoy/network/transport_socket.h | 5 +++ include/envoy/upstream/cluster_manager.h | 3 +- source/common/http/http1/conn_pool.cc | 9 ++-- source/common/http/http1/conn_pool.h | 9 ++-- source/common/http/http2/conn_pool.cc | 9 ++-- source/common/http/http2/conn_pool.h | 4 +- source/common/network/BUILD | 10 +++++ source/common/network/application_protocol.cc | 12 ++++++ source/common/network/application_protocol.h | 24 +++++++++++ .../network/transport_socket_options_impl.cc | 10 +++-- .../network/transport_socket_options_impl.h | 10 ++++- source/common/router/BUILD | 2 + source/common/router/router.cc | 12 ++++++ source/common/router/router.h | 6 +++ source/common/tcp_proxy/BUILD | 1 + source/common/tcp_proxy/tcp_proxy.cc | 30 ++++++++++---- .../common/upstream/cluster_manager_impl.cc | 20 ++++++--- source/common/upstream/cluster_manager_impl.h | 8 ++-- .../transport_sockets/tls/context_impl.cc | 10 +++++ .../grpc_client_integration_test_harness.h | 2 +- test/common/http/http1/conn_pool_test.cc | 2 +- test/common/http/http2/conn_pool_test.cc | 2 +- test/common/router/BUILD | 1 + test/common/router/router_test.cc | 35 ++++++++++++++++ test/common/tcp_proxy/BUILD | 1 + test/common/tcp_proxy/tcp_proxy_test.cc | 37 +++++++++++++++++ .../upstream/cluster_manager_impl_test.cc | 41 ++++++++++--------- .../transport_sockets/tls/ssl_socket_test.cc | 30 ++++++++++++++ test/mocks/upstream/mocks.h | 10 +++-- 29 files changed, 294 insertions(+), 61 deletions(-) create mode 100644 source/common/network/application_protocol.cc create mode 100644 source/common/network/application_protocol.h diff --git a/include/envoy/network/transport_socket.h b/include/envoy/network/transport_socket.h index a04b711c0102..27ef9959d6cd 100644 --- a/include/envoy/network/transport_socket.h +++ b/include/envoy/network/transport_socket.h @@ -171,6 +171,11 @@ class TransportSocketOptions { */ virtual const std::vector& verifySubjectAltNameListOverride() const PURE; + /** + * @return the optional overridden application protocols. + */ + virtual const std::vector& applicationProtocolListOverride() const PURE; + /** * @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 diff --git a/include/envoy/upstream/cluster_manager.h b/include/envoy/upstream/cluster_manager.h index fd3255fc2dde..132c36c054b6 100644 --- a/include/envoy/upstream/cluster_manager.h +++ b/include/envoy/upstream/cluster_manager.h @@ -275,7 +275,8 @@ class ClusterManagerFactory { virtual Http::ConnectionPool::InstancePtr allocateConnPool(Event::Dispatcher& dispatcher, HostConstSharedPtr host, ResourcePriority priority, Http::Protocol protocol, - const Network::ConnectionSocket::OptionsSharedPtr& options) PURE; + const Network::ConnectionSocket::OptionsSharedPtr& options, + const Network::TransportSocketOptionsSharedPtr& transport_socket_options) PURE; /** * Allocate a TCP connection pool for the host. Pools are separated by 'priority' and diff --git a/source/common/http/http1/conn_pool.cc b/source/common/http/http1/conn_pool.cc index 07f96ce46e63..44245f777dd0 100644 --- a/source/common/http/http1/conn_pool.cc +++ b/source/common/http/http1/conn_pool.cc @@ -25,9 +25,10 @@ namespace Http1 { ConnPoolImpl::ConnPoolImpl(Event::Dispatcher& dispatcher, Upstream::HostConstSharedPtr host, Upstream::ResourcePriority priority, - const Network::ConnectionSocket::OptionsSharedPtr& options) + const Network::ConnectionSocket::OptionsSharedPtr& options, + const Network::TransportSocketOptionsSharedPtr& transport_socket_options) : ConnPoolImplBase(std::move(host), std::move(priority)), dispatcher_(dispatcher), - socket_options_(options), + socket_options_(options), transport_socket_options_(transport_socket_options), upstream_ready_timer_(dispatcher_.createTimer([this]() { onUpstreamReady(); })) {} ConnPoolImpl::~ConnPoolImpl() { @@ -303,8 +304,8 @@ ConnPoolImpl::ActiveClient::ActiveClient(ConnPoolImpl& parent) parent_.conn_connect_ms_ = std::make_unique( parent_.host_->cluster().stats().upstream_cx_connect_ms_, parent_.dispatcher_.timeSource()); - Upstream::Host::CreateConnectionData data = - parent_.host_->createConnection(parent_.dispatcher_, parent_.socket_options_, nullptr); + Upstream::Host::CreateConnectionData data = parent_.host_->createConnection( + parent_.dispatcher_, parent_.socket_options_, parent_.transport_socket_options_); real_host_description_ = data.host_description_; codec_client_ = parent_.createCodecClient(data); codec_client_->addConnectionCallbacks(*this); diff --git a/source/common/http/http1/conn_pool.h b/source/common/http/http1/conn_pool.h index 91f854d514ac..18c01908f1e9 100644 --- a/source/common/http/http1/conn_pool.h +++ b/source/common/http/http1/conn_pool.h @@ -32,7 +32,8 @@ class ConnPoolImpl : public ConnectionPool::Instance, public ConnPoolImplBase { public: ConnPoolImpl(Event::Dispatcher& dispatcher, Upstream::HostConstSharedPtr host, Upstream::ResourcePriority priority, - const Network::ConnectionSocket::OptionsSharedPtr& options); + const Network::ConnectionSocket::OptionsSharedPtr& options, + const Network::TransportSocketOptionsSharedPtr& transport_socket_options); ~ConnPoolImpl() override; @@ -122,6 +123,7 @@ class ConnPoolImpl : public ConnectionPool::Instance, public ConnPoolImplBase { std::list busy_clients_; std::list drained_callbacks_; const Network::ConnectionSocket::OptionsSharedPtr socket_options_; + const Network::TransportSocketOptionsSharedPtr transport_socket_options_; Event::TimerPtr upstream_ready_timer_; bool upstream_ready_enabled_{false}; }; @@ -133,8 +135,9 @@ class ProdConnPoolImpl : public ConnPoolImpl { public: ProdConnPoolImpl(Event::Dispatcher& dispatcher, Upstream::HostConstSharedPtr host, Upstream::ResourcePriority priority, - const Network::ConnectionSocket::OptionsSharedPtr& options) - : ConnPoolImpl(dispatcher, host, priority, options) {} + const Network::ConnectionSocket::OptionsSharedPtr& options, + const Network::TransportSocketOptionsSharedPtr& transport_socket_options) + : ConnPoolImpl(dispatcher, host, priority, options, transport_socket_options) {} // ConnPoolImpl CodecClientPtr createCodecClient(Upstream::Host::CreateConnectionData& data) override; diff --git a/source/common/http/http2/conn_pool.cc b/source/common/http/http2/conn_pool.cc index 62c952e48d9a..ceb125d5e567 100644 --- a/source/common/http/http2/conn_pool.cc +++ b/source/common/http/http2/conn_pool.cc @@ -17,9 +17,10 @@ namespace Http2 { ConnPoolImpl::ConnPoolImpl(Event::Dispatcher& dispatcher, Upstream::HostConstSharedPtr host, Upstream::ResourcePriority priority, - const Network::ConnectionSocket::OptionsSharedPtr& options) + const Network::ConnectionSocket::OptionsSharedPtr& options, + const Network::TransportSocketOptionsSharedPtr& transport_socket_options) : ConnPoolImplBase(std::move(host), std::move(priority)), dispatcher_(dispatcher), - socket_options_(options) {} + socket_options_(options), transport_socket_options_(transport_socket_options) {} ConnPoolImpl::~ConnPoolImpl() { if (primary_client_) { @@ -273,8 +274,8 @@ ConnPoolImpl::ActiveClient::ActiveClient(ConnPoolImpl& parent) connect_timer_(parent_.dispatcher_.createTimer([this]() -> void { onConnectTimeout(); })) { parent_.conn_connect_ms_ = std::make_unique( parent_.host_->cluster().stats().upstream_cx_connect_ms_, parent_.dispatcher_.timeSource()); - Upstream::Host::CreateConnectionData data = - parent_.host_->createConnection(parent_.dispatcher_, parent_.socket_options_, nullptr); + Upstream::Host::CreateConnectionData data = parent_.host_->createConnection( + parent_.dispatcher_, parent_.socket_options_, parent_.transport_socket_options_); real_host_description_ = data.host_description_; client_ = parent_.createCodecClient(data); client_->addConnectionCallbacks(*this); diff --git a/source/common/http/http2/conn_pool.h b/source/common/http/http2/conn_pool.h index c551299fd396..55bb4544d5a4 100644 --- a/source/common/http/http2/conn_pool.h +++ b/source/common/http/http2/conn_pool.h @@ -26,7 +26,8 @@ class ConnPoolImpl : public ConnectionPool::Instance, public ConnPoolImplBase { public: ConnPoolImpl(Event::Dispatcher& dispatcher, Upstream::HostConstSharedPtr host, Upstream::ResourcePriority priority, - const Network::ConnectionSocket::OptionsSharedPtr& options); + const Network::ConnectionSocket::OptionsSharedPtr& options, + const Network::TransportSocketOptionsSharedPtr& transport_socket_options); ~ConnPoolImpl() override; // Http::ConnectionPool::Instance @@ -96,6 +97,7 @@ class ConnPoolImpl : public ConnectionPool::Instance, public ConnPoolImplBase { ActiveClientPtr draining_client_; std::list drained_callbacks_; const Network::ConnectionSocket::OptionsSharedPtr socket_options_; + const Network::TransportSocketOptionsSharedPtr transport_socket_options_; }; /** diff --git a/source/common/network/BUILD b/source/common/network/BUILD index be7a5f610dad..ad1bfb18dce9 100644 --- a/source/common/network/BUILD +++ b/source/common/network/BUILD @@ -31,6 +31,16 @@ envoy_cc_library( ], ) +envoy_cc_library( + name = "application_protocol_lib", + srcs = ["application_protocol.cc"], + hdrs = ["application_protocol.h"], + deps = [ + "//include/envoy/stream_info:filter_state_interface", + "//source/common/common:macros", + ], +) + envoy_cc_library( name = "cidr_range_lib", srcs = ["cidr_range.cc"], diff --git a/source/common/network/application_protocol.cc b/source/common/network/application_protocol.cc new file mode 100644 index 000000000000..57d9bbb556ce --- /dev/null +++ b/source/common/network/application_protocol.cc @@ -0,0 +1,12 @@ +#include "common/network/application_protocol.h" + +#include "common/common/macros.h" + +namespace Envoy { +namespace Network { + +const std::string& ApplicationProtocols::key() { + CONSTRUCT_ON_FIRST_USE(std::string, "envoy.network.application_protocols"); +} +} // namespace Network +} // namespace Envoy diff --git a/source/common/network/application_protocol.h b/source/common/network/application_protocol.h new file mode 100644 index 000000000000..177e584a5eb5 --- /dev/null +++ b/source/common/network/application_protocol.h @@ -0,0 +1,24 @@ +#pragma once + +#include "envoy/stream_info/filter_state.h" + +namespace Envoy { +namespace Network { + +/** + * ALPN to set in the upstream connection. Filters can use this one to override the ALPN in TLS + * context. + */ +class ApplicationProtocols : public StreamInfo::FilterState::Object { +public: + explicit ApplicationProtocols(const std::vector& application_protocols) + : application_protocols_(application_protocols) {} + const std::vector& value() const { return application_protocols_; } + static const std::string& key(); + +private: + const std::vector application_protocols_; +}; + +} // namespace Network +} // namespace Envoy diff --git a/source/common/network/transport_socket_options_impl.cc b/source/common/network/transport_socket_options_impl.cc index a8f315cde1db..dba5281b1e0e 100644 --- a/source/common/network/transport_socket_options_impl.cc +++ b/source/common/network/transport_socket_options_impl.cc @@ -6,11 +6,15 @@ namespace Envoy { namespace Network { void TransportSocketOptionsImpl::hashKey(std::vector& key) const { - if (!override_server_name_.has_value()) { - return; + if (override_server_name_.has_value()) { + pushScalarToByteVector(StringUtil::CaseInsensitiveHash()(override_server_name_.value()), key); } - pushScalarToByteVector(StringUtil::CaseInsensitiveHash()(override_server_name_.value()), key); + if (!override_alpn_list_.empty()) { + for (const auto& protocol : override_alpn_list_) { + pushScalarToByteVector(StringUtil::CaseInsensitiveHash()(protocol), key); + } + } } } // namespace Network } // namespace Envoy diff --git a/source/common/network/transport_socket_options_impl.h b/source/common/network/transport_socket_options_impl.h index 0010a46d763a..14a0d6627c5c 100644 --- a/source/common/network/transport_socket_options_impl.h +++ b/source/common/network/transport_socket_options_impl.h @@ -8,11 +8,13 @@ namespace Network { class TransportSocketOptionsImpl : public TransportSocketOptions { public: TransportSocketOptionsImpl(absl::string_view override_server_name = "", - std::vector&& override_verify_san_list = {}) + std::vector&& override_verify_san_list = {}, + std::vector&& override_alpn = {}) : override_server_name_(override_server_name.empty() ? absl::nullopt : absl::optional(override_server_name)), - override_verify_san_list_{std::move(override_verify_san_list)} {} + override_verify_san_list_{std::move(override_verify_san_list)}, + override_alpn_list_{std::move(override_alpn)} {} // Network::TransportSocketOptions const absl::optional& serverNameOverride() const override { @@ -21,11 +23,15 @@ class TransportSocketOptionsImpl : public TransportSocketOptions { const std::vector& verifySubjectAltNameListOverride() const override { return override_verify_san_list_; } + const std::vector& applicationProtocolListOverride() const override { + return override_alpn_list_; + } void hashKey(std::vector& key) const override; private: const absl::optional override_server_name_; const std::vector override_verify_san_list_; + const std::vector override_alpn_list_; }; } // namespace Network diff --git a/source/common/router/BUILD b/source/common/router/BUILD index c94382b5fb4e..afe8726a297b 100644 --- a/source/common/router/BUILD +++ b/source/common/router/BUILD @@ -251,6 +251,8 @@ envoy_cc_library( "//source/common/http:headers_lib", "//source/common/http:message_lib", "//source/common/http:utility_lib", + "//source/common/network:application_protocol_lib", + "//source/common/network:transport_socket_options_lib", "//source/common/stream_info:stream_info_lib", "//source/common/tracing:http_tracer_lib", "//source/common/upstream:load_balancer_lib", diff --git a/source/common/router/router.cc b/source/common/router/router.cc index 05a7ca48112e..83e59e15d7db 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc @@ -24,6 +24,8 @@ #include "common/http/headers.h" #include "common/http/message_impl.h" #include "common/http/utility.h" +#include "common/network/application_protocol.h" +#include "common/network/transport_socket_options_impl.h" #include "common/router/config_impl.h" #include "common/router/debug_config.h" #include "common/router/retry_state_impl.h" @@ -540,6 +542,16 @@ Http::ConnectionPool::Instance* Filter::getConnPool() { protocol = (features & Upstream::ClusterInfo::Features::HTTP2) ? Http::Protocol::Http2 : Http::Protocol::Http11; } + + if (callbacks_->streamInfo().filterState().hasData( + Network::ApplicationProtocols::key())) { + const auto& alpn = + callbacks_->streamInfo().filterState().getDataReadOnly( + Network::ApplicationProtocols::key()); + transport_socket_options_ = std::make_shared( + "", std::vector{}, std::vector{alpn.value()}); + } + return config_.cm_.httpConnPoolForCluster(route_entry_->clusterName(), route_entry_->priority(), protocol, this); } diff --git a/source/common/router/router.h b/source/common/router/router.h index 8cff5071fc59..419d58fb0ccd 100644 --- a/source/common/router/router.h +++ b/source/common/router/router.h @@ -325,6 +325,10 @@ class Filter : Logger::Loggable, return callbacks_->getUpstreamSocketOptions(); } + Network::TransportSocketOptionsSharedPtr upstreamTransportSocketOptions() const override { + return transport_socket_options_; + } + /** * Set a computed cookie to be sent with the downstream headers. * @param key supplies the size of the cookie @@ -576,6 +580,8 @@ class Filter : Logger::Loggable, bool attempting_internal_redirect_with_complete_stream_ : 1; uint32_t attempt_count_{1}; uint32_t pending_retries_{0}; + + Network::TransportSocketOptionsSharedPtr transport_socket_options_; }; class ProdFilter : public Filter { diff --git a/source/common/tcp_proxy/BUILD b/source/common/tcp_proxy/BUILD index f28529e05dbb..adec34a52684 100644 --- a/source/common/tcp_proxy/BUILD +++ b/source/common/tcp_proxy/BUILD @@ -33,6 +33,7 @@ envoy_cc_library( "//source/common/common:empty_string", "//source/common/common:macros", "//source/common/common:minimal_logger_lib", + "//source/common/network:application_protocol_lib", "//source/common/network:cidr_range_lib", "//source/common/network:filter_lib", "//source/common/network:transport_socket_options_lib", diff --git a/source/common/tcp_proxy/tcp_proxy.cc b/source/common/tcp_proxy/tcp_proxy.cc index ed7a85ebbb19..11b1591533da 100644 --- a/source/common/tcp_proxy/tcp_proxy.cc +++ b/source/common/tcp_proxy/tcp_proxy.cc @@ -19,6 +19,7 @@ #include "common/common/macros.h" #include "common/common/utility.h" #include "common/config/well_known_names.h" +#include "common/network/application_protocol.h" #include "common/network/transport_socket_options_impl.h" #include "common/network/upstream_server_name.h" #include "common/router/metadatamatchcriteria_impl.h" @@ -368,14 +369,29 @@ Network::FilterStatus Filter::initializeUpstreamConnection() { return Network::FilterStatus::StopIteration; } - if (downstreamConnection() && - downstreamConnection()->streamInfo().filterState().hasData( - UpstreamServerName::key())) { - const auto& original_requested_server_name = - downstreamConnection()->streamInfo().filterState().getDataReadOnly( - UpstreamServerName::key()); + if (downstreamConnection()) { + absl::string_view server_name = ""; + std::vector application_protocols; + if (downstreamConnection()->streamInfo().filterState().hasData( + UpstreamServerName::key())) { + const auto& original_requested_server_name = + downstreamConnection()->streamInfo().filterState().getDataReadOnly( + UpstreamServerName::key()); + server_name = original_requested_server_name.value(); + } + + if (downstreamConnection()->streamInfo().filterState().hasData( + Network::ApplicationProtocols::key())) { + const auto& alpn = + downstreamConnection() + ->streamInfo() + .filterState() + .getDataReadOnly(Network::ApplicationProtocols::key()); + application_protocols = alpn.value(); + } + transport_socket_options_ = std::make_shared( - original_requested_server_name.value()); + server_name, std::vector{}, std::vector{application_protocols}); } Tcp::ConnectionPool::Instance* conn_pool = cluster_manager_.tcpConnPoolForCluster( diff --git a/source/common/upstream/cluster_manager_impl.cc b/source/common/upstream/cluster_manager_impl.cc index 1d57d737ad44..a08dc584f344 100644 --- a/source/common/upstream/cluster_manager_impl.cc +++ b/source/common/upstream/cluster_manager_impl.cc @@ -1254,6 +1254,12 @@ ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::connPool( option->hashKey(hash_key); } + bool have_transport_socket_options = false; + if (context && context->upstreamTransportSocketOptions()) { + context->upstreamTransportSocketOptions()->hashKey(hash_key); + have_transport_socket_options = true; + } + ConnPoolsContainer& container = *parent_.getHttpConnPoolsContainer(host, true); // Note: to simplify this, we assume that the factory is only called in the scope of this @@ -1262,7 +1268,8 @@ ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::connPool( container.pools_->getPool(priority, hash_key, [&]() { return parent_.parent_.factory_.allocateConnPool( parent_.thread_local_dispatcher_, host, priority, protocol, - !upstream_options->empty() ? upstream_options : nullptr); + !upstream_options->empty() ? upstream_options : nullptr, + have_transport_socket_options ? context->upstreamTransportSocketOptions() : nullptr); }); if (pool.has_value()) { @@ -1326,14 +1333,15 @@ ClusterManagerPtr ProdClusterManagerFactory::clusterManagerFromProto( Http::ConnectionPool::InstancePtr ProdClusterManagerFactory::allocateConnPool( Event::Dispatcher& dispatcher, HostConstSharedPtr host, ResourcePriority priority, - Http::Protocol protocol, const Network::ConnectionSocket::OptionsSharedPtr& options) { + Http::Protocol protocol, const Network::ConnectionSocket::OptionsSharedPtr& options, + const Network::TransportSocketOptionsSharedPtr& transport_socket_options) { if (protocol == Http::Protocol::Http2 && runtime_.snapshot().featureEnabled("upstream.use_http2", 100)) { - return Http::ConnectionPool::InstancePtr{ - new Http::Http2::ProdConnPoolImpl(dispatcher, host, priority, options)}; + return std::make_unique(dispatcher, host, priority, options, + transport_socket_options); } else { - return Http::ConnectionPool::InstancePtr{ - new Http::Http1::ProdConnPoolImpl(dispatcher, host, priority, options)}; + return std::make_unique(dispatcher, host, priority, options, + transport_socket_options); } } diff --git a/source/common/upstream/cluster_manager_impl.h b/source/common/upstream/cluster_manager_impl.h index 7c969984ca7c..068ab7dafe4c 100644 --- a/source/common/upstream/cluster_manager_impl.h +++ b/source/common/upstream/cluster_manager_impl.h @@ -56,10 +56,10 @@ class ProdClusterManagerFactory : public ClusterManagerFactory { // Upstream::ClusterManagerFactory ClusterManagerPtr clusterManagerFromProto(const envoy::config::bootstrap::v2::Bootstrap& bootstrap) override; - Http::ConnectionPool::InstancePtr - allocateConnPool(Event::Dispatcher& dispatcher, HostConstSharedPtr host, - ResourcePriority priority, Http::Protocol protocol, - const Network::ConnectionSocket::OptionsSharedPtr& options) override; + Http::ConnectionPool::InstancePtr allocateConnPool( + Event::Dispatcher& dispatcher, HostConstSharedPtr host, ResourcePriority priority, + Http::Protocol protocol, const Network::ConnectionSocket::OptionsSharedPtr& options, + const Network::TransportSocketOptionsSharedPtr& transport_socket_options) override; Tcp::ConnectionPool::InstancePtr allocateTcpConnPool(Event::Dispatcher& dispatcher, HostConstSharedPtr host, ResourcePriority priority, diff --git a/source/extensions/transport_sockets/tls/context_impl.cc b/source/extensions/transport_sockets/tls/context_impl.cc index ede655319cb2..ed8e54c08449 100644 --- a/source/extensions/transport_sockets/tls/context_impl.cc +++ b/source/extensions/transport_sockets/tls/context_impl.cc @@ -802,6 +802,16 @@ bssl::UniquePtr ClientContextImpl::newSsl(const Network::TransportSocketOpt SSL_set_verify(ssl_con.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nullptr); } + if (options && !options->applicationProtocolListOverride().empty()) { + std::vector parsed_override_alpn = + parseAlpnProtocols(absl::StrJoin(options->applicationProtocolListOverride(), ",")); + if (!parsed_override_alpn.empty()) { + int rc = SSL_set_alpn_protos(ssl_con.get(), parsed_override_alpn.data(), + parsed_override_alpn.size()); + RELEASE_ASSERT(rc == 0, ""); + } + } + if (allow_renegotiation_) { SSL_set_renegotiate_mode(ssl_con.get(), ssl_renegotiate_freely); } diff --git a/test/common/grpc/grpc_client_integration_test_harness.h b/test/common/grpc/grpc_client_integration_test_harness.h index 1d02a8e81de6..89284f8f5a73 100644 --- a/test/common/grpc/grpc_client_integration_test_harness.h +++ b/test/common/grpc/grpc_client_integration_test_harness.h @@ -282,7 +282,7 @@ class GrpcClientIntegrationTest : public GrpcClientIntegrationParamTest { EXPECT_CALL(*mock_host_, cluster()).WillRepeatedly(ReturnRef(*cluster_info_ptr_)); EXPECT_CALL(*mock_host_description_, locality()).WillRepeatedly(ReturnRef(host_locality_)); http_conn_pool_ = std::make_unique( - *dispatcher_, host_ptr_, Upstream::ResourcePriority::Default, nullptr); + *dispatcher_, host_ptr_, Upstream::ResourcePriority::Default, nullptr, nullptr); EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, _, _)) .WillRepeatedly(Return(http_conn_pool_.get())); http_async_client_ = std::make_unique( diff --git a/test/common/http/http1/conn_pool_test.cc b/test/common/http/http1/conn_pool_test.cc index a667ea493898..7eda2144e976 100644 --- a/test/common/http/http1/conn_pool_test.cc +++ b/test/common/http/http1/conn_pool_test.cc @@ -45,7 +45,7 @@ class ConnPoolImplForTest : public ConnPoolImpl { Upstream::ClusterInfoConstSharedPtr cluster, NiceMock* upstream_ready_timer) : ConnPoolImpl(dispatcher, Upstream::makeTestHost(cluster, "tcp://127.0.0.1:9000"), - Upstream::ResourcePriority::Default, nullptr), + Upstream::ResourcePriority::Default, nullptr, nullptr), api_(Api::createApiForTest()), mock_dispatcher_(dispatcher), mock_upstream_ready_timer_(upstream_ready_timer) {} diff --git a/test/common/http/http2/conn_pool_test.cc b/test/common/http/http2/conn_pool_test.cc index 8ac3f66a31b8..1cd23dee8fc3 100644 --- a/test/common/http/http2/conn_pool_test.cc +++ b/test/common/http/http2/conn_pool_test.cc @@ -64,7 +64,7 @@ class Http2ConnPoolImplTest : public testing::Test { Http2ConnPoolImplTest() : api_(Api::createApiForTest(stats_store_)), - pool_(dispatcher_, host_, Upstream::ResourcePriority::Default, nullptr) {} + pool_(dispatcher_, host_, Upstream::ResourcePriority::Default, nullptr, nullptr) {} ~Http2ConnPoolImplTest() override { EXPECT_TRUE(TestUtility::gaugesZeroed(cluster_->stats_store_.gauges())); diff --git a/test/common/router/BUILD b/test/common/router/BUILD index 48304edb719a..aa1385416405 100644 --- a/test/common/router/BUILD +++ b/test/common/router/BUILD @@ -233,6 +233,7 @@ envoy_cc_test( deps = [ "//source/common/buffer:buffer_lib", "//source/common/http:context_lib", + "//source/common/network:application_protocol_lib", "//source/common/network:utility_lib", "//source/common/router:router_lib", "//source/common/upstream:upstream_includes", diff --git a/test/common/router/router_test.cc b/test/common/router/router_test.cc index c05e23793075..becdedc624db 100644 --- a/test/common/router/router_test.cc +++ b/test/common/router/router_test.cc @@ -7,6 +7,7 @@ #include "common/config/metadata.h" #include "common/config/well_known_names.h" #include "common/http/context_impl.h" +#include "common/network/application_protocol.h" #include "common/network/socket_option_factory.h" #include "common/network/utility.h" #include "common/router/config_impl.h" @@ -4238,6 +4239,40 @@ TEST_F(RouterTest, UpstreamSocketOptionsReturnedNonEmpty) { EXPECT_EQ(to_return, options); } +TEST_F(RouterTest, ApplicationProtocols) { + callbacks_.streamInfo().filterState().setData( + Network::ApplicationProtocols::key(), + std::make_unique(std::vector{"foo", "bar"}), + StreamInfo::FilterState::StateType::ReadOnly); + + EXPECT_CALL(cm_, httpConnPoolForCluster(_, _, _, _)) + .WillOnce( + Invoke([&](const std::string&, Upstream::ResourcePriority, Http::Protocol, + Upstream::LoadBalancerContext* context) -> Http::ConnectionPool::Instance* { + Network::TransportSocketOptionsSharedPtr transport_socket_options = + context->upstreamTransportSocketOptions(); + EXPECT_NE(transport_socket_options, nullptr); + EXPECT_FALSE(transport_socket_options->applicationProtocolListOverride().empty()); + EXPECT_EQ(transport_socket_options->applicationProtocolListOverride().size(), 2); + EXPECT_EQ(transport_socket_options->applicationProtocolListOverride()[0], "foo"); + EXPECT_EQ(transport_socket_options->applicationProtocolListOverride()[1], "bar"); + return &cm_.conn_pool_; + })); + EXPECT_CALL(cm_.conn_pool_, newStream(_, _)).WillOnce(Return(&cancellable_)); + + expectResponseTimerCreate(); + + Http::TestHeaderMapImpl headers; + HttpTestUtility::addDefaultHeaders(headers); + EXPECT_CALL(span_, injectContext(_)); + router_.decodeHeaders(headers, true); + + // When the router filter gets reset we should cancel the pool request. + EXPECT_CALL(cancellable_, cancel()); + router_.onDestroy(); + EXPECT_TRUE(verifyHostUpstreamStats(0, 0)); +} + class WatermarkTest : public RouterTest { public: void sendRequest(bool header_only_request = true, bool pool_ready = true) { diff --git a/test/common/tcp_proxy/BUILD b/test/common/tcp_proxy/BUILD index d820b93414be..279261e02583 100644 --- a/test/common/tcp_proxy/BUILD +++ b/test/common/tcp_proxy/BUILD @@ -16,6 +16,7 @@ envoy_cc_test( "//source/common/config:filter_json_lib", "//source/common/event:dispatcher_lib", "//source/common/network:address_lib", + "//source/common/network:application_protocol_lib", "//source/common/network:transport_socket_options_lib", "//source/common/network:upstream_server_name_lib", "//source/common/stats:stats_lib", diff --git a/test/common/tcp_proxy/tcp_proxy_test.cc b/test/common/tcp_proxy/tcp_proxy_test.cc index 85af207ede4f..f8e5188e146e 100644 --- a/test/common/tcp_proxy/tcp_proxy_test.cc +++ b/test/common/tcp_proxy/tcp_proxy_test.cc @@ -7,6 +7,7 @@ #include "common/buffer/buffer_impl.h" #include "common/config/filter_json.h" #include "common/network/address_impl.h" +#include "common/network/application_protocol.h" #include "common/network/transport_socket_options_impl.h" #include "common/network/upstream_server_name.h" #include "common/router/metadatamatchcriteria_impl.h" @@ -1259,6 +1260,42 @@ TEST_F(TcpProxyRoutingTest, UpstreamServerName) { filter_->onNewConnection(); } +// Test that the tcp proxy override ALPN from FilterState if set +TEST_F(TcpProxyRoutingTest, ApplicationProtocols) { + setup(); + + NiceMock stream_info; + stream_info.filterState().setData( + Network::ApplicationProtocols::key(), + std::make_unique(std::vector{"foo", "bar"}), + StreamInfo::FilterState::StateType::ReadOnly); + + ON_CALL(connection_, streamInfo()).WillByDefault(ReturnRef(stream_info)); + EXPECT_CALL(Const(connection_), streamInfo()).WillRepeatedly(ReturnRef(stream_info)); + + // Expect filter to try to open a connection to a cluster with the transport socket options with + // override-application-protocol + EXPECT_CALL(factory_context_.cluster_manager_, tcpConnPoolForCluster(_, _, _)) + .WillOnce( + Invoke([](const std::string& cluster, Upstream::ResourcePriority, + Upstream::LoadBalancerContext* context) -> Tcp::ConnectionPool::Instance* { + EXPECT_EQ(cluster, "fake_cluster"); + Network::TransportSocketOptionsSharedPtr transport_socket_options = + context->upstreamTransportSocketOptions(); + EXPECT_NE(transport_socket_options, nullptr); + EXPECT_FALSE(transport_socket_options->applicationProtocolListOverride().empty()); + EXPECT_EQ(transport_socket_options->applicationProtocolListOverride().size(), 2); + EXPECT_EQ(transport_socket_options->applicationProtocolListOverride()[0], "foo"); + EXPECT_EQ(transport_socket_options->applicationProtocolListOverride()[1], "bar"); + return nullptr; + })); + + // Port 9999 is within the specified destination port range. + connection_.local_address_ = std::make_shared("1.2.3.4", 9999); + + filter_->onNewConnection(); +} + } // namespace } // namespace TcpProxy } // namespace Envoy diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index 4ea0c9080cd1..fd1ac0d0db2f 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -80,10 +80,12 @@ class TestClusterManagerFactory : public ClusterManagerFactory { })); } - Http::ConnectionPool::InstancePtr - allocateConnPool(Event::Dispatcher&, HostConstSharedPtr host, ResourcePriority, Http::Protocol, - const Network::ConnectionSocket::OptionsSharedPtr& options) override { - return Http::ConnectionPool::InstancePtr{allocateConnPool_(host, options)}; + Http::ConnectionPool::InstancePtr allocateConnPool( + Event::Dispatcher&, HostConstSharedPtr host, ResourcePriority, Http::Protocol, + const Network::ConnectionSocket::OptionsSharedPtr& options, + const Network::TransportSocketOptionsSharedPtr& transport_socket_options) override { + return Http::ConnectionPool::InstancePtr{ + allocateConnPool_(host, options, transport_socket_options)}; } Tcp::ConnectionPool::InstancePtr @@ -114,9 +116,10 @@ class TestClusterManagerFactory : public ClusterManagerFactory { MOCK_METHOD1(clusterManagerFromProto_, ClusterManager*(const envoy::config::bootstrap::v2::Bootstrap& bootstrap)); - MOCK_METHOD2(allocateConnPool_, + MOCK_METHOD3(allocateConnPool_, Http::ConnectionPool::Instance*(HostConstSharedPtr host, - Network::ConnectionSocket::OptionsSharedPtr)); + Network::ConnectionSocket::OptionsSharedPtr, + Network::TransportSocketOptionsSharedPtr)); MOCK_METHOD1(allocateTcpConnPool_, Tcp::ConnectionPool::Instance*(HostConstSharedPtr host)); MOCK_METHOD4(clusterFromProto_, std::pair( @@ -1461,7 +1464,7 @@ TEST_F(ClusterManagerImplTest, DynamicAddRemove) { EXPECT_EQ(cluster2->info_, cluster_manager_->get("fake_cluster")->info()); EXPECT_EQ(1UL, cluster_manager_->clusters().size()); Http::ConnectionPool::MockInstance* cp = new Http::ConnectionPool::MockInstance(); - EXPECT_CALL(factory_, allocateConnPool_(_, _)).WillOnce(Return(cp)); + EXPECT_CALL(factory_, allocateConnPool_(_, _, _)).WillOnce(Return(cp)); EXPECT_EQ(cp, cluster_manager_->httpConnPoolForCluster("fake_cluster", ResourcePriority::Default, Http::Protocol::Http11, nullptr)); @@ -1618,7 +1621,7 @@ TEST_F(ClusterManagerImplTest, CloseHttpConnectionsOnHealthFailure) { })); create(parseBootstrapFromV2Json(json)); - EXPECT_CALL(factory_, allocateConnPool_(_, _)).WillOnce(Return(cp1)); + EXPECT_CALL(factory_, allocateConnPool_(_, _, _)).WillOnce(Return(cp1)); cluster_manager_->httpConnPoolForCluster("some_cluster", ResourcePriority::Default, Http::Protocol::Http11, nullptr); @@ -1629,7 +1632,7 @@ TEST_F(ClusterManagerImplTest, CloseHttpConnectionsOnHealthFailure) { test_host->healthFlagSet(Host::HealthFlag::FAILED_OUTLIER_CHECK); outlier_detector.runCallbacks(test_host); - EXPECT_CALL(factory_, allocateConnPool_(_, _)).WillOnce(Return(cp2)); + EXPECT_CALL(factory_, allocateConnPool_(_, _, _)).WillOnce(Return(cp2)); cluster_manager_->httpConnPoolForCluster("some_cluster", ResourcePriority::High, Http::Protocol::Http11, nullptr); } @@ -1892,7 +1895,7 @@ TEST_F(ClusterManagerImplTest, DynamicHostRemove) { EXPECT_CALL(initialized, ready()); cluster_manager_->setInitializedCb([&]() -> void { initialized.ready(); }); - EXPECT_CALL(factory_, allocateConnPool_(_, _)) + EXPECT_CALL(factory_, allocateConnPool_(_, _, _)) .Times(4) .WillRepeatedly(ReturnNew()); @@ -2051,7 +2054,7 @@ TEST_F(ClusterManagerImplTest, DynamicHostRemoveWithTls) { EXPECT_CALL(initialized, ready()); cluster_manager_->setInitializedCb([&]() -> void { initialized.ready(); }); - EXPECT_CALL(factory_, allocateConnPool_(_, _)) + EXPECT_CALL(factory_, allocateConnPool_(_, _, _)) .Times(4) .WillRepeatedly(ReturnNew()); @@ -2239,7 +2242,7 @@ TEST_F(ClusterManagerImplTest, DynamicHostRemoveDefaultPriority) { dns_callback(TestUtility::makeDnsResponse({"127.0.0.2"})); - EXPECT_CALL(factory_, allocateConnPool_(_, _)) + EXPECT_CALL(factory_, allocateConnPool_(_, _, _)) .WillOnce(ReturnNew()); EXPECT_CALL(factory_, allocateTcpConnPool_(_)) @@ -2319,7 +2322,7 @@ TEST_F(ClusterManagerImplTest, ConnPoolDestroyWithDraining) { dns_callback(TestUtility::makeDnsResponse({"127.0.0.2"})); MockConnPoolWithDestroy* mock_cp = new MockConnPoolWithDestroy(); - EXPECT_CALL(factory_, allocateConnPool_(_, _)).WillOnce(Return(mock_cp)); + EXPECT_CALL(factory_, allocateConnPool_(_, _, _)).WillOnce(Return(mock_cp)); MockTcpConnPoolWithDestroy* mock_tcp = new MockTcpConnPoolWithDestroy(); EXPECT_CALL(factory_, allocateTcpConnPool_(_)).WillOnce(Return(mock_tcp)); @@ -2766,7 +2769,7 @@ TEST_F(ClusterManagerImplTest, UpstreamSocketOptionsPassedToConnPool) { Network::SocketOptionFactory::buildIpTransparentOptions(); EXPECT_CALL(context, upstreamSocketOptions()).WillOnce(Return(options_to_return)); - EXPECT_CALL(factory_, allocateConnPool_(_, _)).WillOnce(Return(to_create)); + EXPECT_CALL(factory_, allocateConnPool_(_, _, _)).WillOnce(Return(to_create)); Http::ConnectionPool::Instance* cp = cluster_manager_->httpConnPoolForCluster( "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, &context); @@ -2788,12 +2791,12 @@ TEST_F(ClusterManagerImplTest, UpstreamSocketOptionsUsedInConnPoolHash) { EXPECT_CALL(context1, upstreamSocketOptions()).WillRepeatedly(Return(options1)); EXPECT_CALL(context2, upstreamSocketOptions()).WillRepeatedly(Return(options2)); - EXPECT_CALL(factory_, allocateConnPool_(_, _)).WillOnce(Return(to_create1)); + EXPECT_CALL(factory_, allocateConnPool_(_, _, _)).WillOnce(Return(to_create1)); Http::ConnectionPool::Instance* cp1 = cluster_manager_->httpConnPoolForCluster( "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, &context1); - EXPECT_CALL(factory_, allocateConnPool_(_, _)).WillOnce(Return(to_create2)); + EXPECT_CALL(factory_, allocateConnPool_(_, _, _)).WillOnce(Return(to_create2)); Http::ConnectionPool::Instance* cp2 = cluster_manager_->httpConnPoolForCluster( "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, &context2); @@ -2818,7 +2821,7 @@ TEST_F(ClusterManagerImplTest, UpstreamSocketOptionsNullIsOkay) { Network::Socket::OptionsSharedPtr options_to_return = nullptr; EXPECT_CALL(context, upstreamSocketOptions()).WillOnce(Return(options_to_return)); - EXPECT_CALL(factory_, allocateConnPool_(_, _)).WillOnce(Return(to_create)); + EXPECT_CALL(factory_, allocateConnPool_(_, _, _)).WillOnce(Return(to_create)); Http::ConnectionPool::Instance* cp = cluster_manager_->httpConnPoolForCluster( "cluster_1", ResourcePriority::Default, Http::Protocol::Http11, &context); @@ -3553,7 +3556,7 @@ TEST_F(ClusterManagerImplTest, ConnPoolsDrainedOnHostSetChange) { EXPECT_EQ(0, factory_.stats_.counter("cluster_manager.cluster_updated_via_merge").value()); EXPECT_EQ(0, factory_.stats_.counter("cluster_manager.update_merge_cancelled").value()); - EXPECT_CALL(factory_, allocateConnPool_(_, _)) + EXPECT_CALL(factory_, allocateConnPool_(_, _, _)) .Times(3) .WillRepeatedly(ReturnNew()); @@ -3657,7 +3660,7 @@ TEST_F(ClusterManagerImplTest, ConnPoolsNotDrainedOnHostSetChange) { 0, HostSetImpl::partitionHosts(hosts_ptr, HostsPerLocalityImpl::empty()), nullptr, hosts, {}, 100); - EXPECT_CALL(factory_, allocateConnPool_(_, _)) + EXPECT_CALL(factory_, allocateConnPool_(_, _, _)) .Times(1) .WillRepeatedly(ReturnNew()); diff --git a/test/extensions/transport_sockets/tls/ssl_socket_test.cc b/test/extensions/transport_sockets/tls/ssl_socket_test.cc index aff65278f087..f6226d7841a6 100644 --- a/test/extensions/transport_sockets/tls/ssl_socket_test.cc +++ b/test/extensions/transport_sockets/tls/ssl_socket_test.cc @@ -3795,6 +3795,36 @@ TEST_P(SslSocketTest, OverrideRequestedServerNameWithoutSniInUpstreamTlsContext) .setTransportSocketOptions(transport_socket_options)); } +TEST_P(SslSocketTest, OverrideApplicationProtocols) { + envoy::api::v2::Listener listener; + envoy::api::v2::listener::FilterChain* filter_chain = listener.add_filter_chains(); + envoy::api::v2::auth::TlsCertificate* server_cert = + filter_chain->mutable_tls_context()->mutable_common_tls_context()->add_tls_certificates(); + server_cert->mutable_certificate_chain()->set_filename(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_cert.pem")); + server_cert->mutable_private_key()->set_filename(TestEnvironment::substitute( + "{{ test_rundir }}/test/extensions/transport_sockets/tls/test_data/san_dns_key.pem")); + envoy::api::v2::auth::CommonTlsContext* server_ctx = + filter_chain->mutable_tls_context()->mutable_common_tls_context(); + + envoy::api::v2::auth::UpstreamTlsContext client; + TestUtilOptionsV2 test_options(listener, client, true, GetParam()); + + // Client connects without ALPN to a server with "test" ALPN, no ALPN is negotiated. + server_ctx->add_alpn_protocols("test"); + testUtilV2(test_options); + server_ctx->clear_alpn_protocols(); + + // Override client side ALPN, "test" ALPN is used. + server_ctx->add_alpn_protocols("test"); + Network::TransportSocketOptionsSharedPtr transport_socket_options( + new Network::TransportSocketOptionsImpl("", {}, {"foo", "test", "bar"})); + + testUtilV2(test_options.setExpectedALPNProtocol("test").setTransportSocketOptions( + transport_socket_options)); + server_ctx->clear_alpn_protocols(); +} + // Validate that if downstream secrets are not yet downloaded from SDS server, Envoy creates // NotReadySslSocket object to handle downstream connection. TEST_P(SslSocketTest, DownstreamNotReadySslSocket) { diff --git a/test/mocks/upstream/mocks.h b/test/mocks/upstream/mocks.h index 39e2be2bb5a2..7b6b22f8cf78 100644 --- a/test/mocks/upstream/mocks.h +++ b/test/mocks/upstream/mocks.h @@ -251,10 +251,12 @@ class MockClusterManagerFactory : public ClusterManagerFactory { MOCK_METHOD1(clusterManagerFromProto, ClusterManagerPtr(const envoy::config::bootstrap::v2::Bootstrap& bootstrap)); - MOCK_METHOD5(allocateConnPool, Http::ConnectionPool::InstancePtr( - Event::Dispatcher& dispatcher, HostConstSharedPtr host, - ResourcePriority priority, Http::Protocol protocol, - const Network::ConnectionSocket::OptionsSharedPtr& options)); + MOCK_METHOD6(allocateConnPool, + Http::ConnectionPool::InstancePtr( + Event::Dispatcher& dispatcher, HostConstSharedPtr host, + ResourcePriority priority, Http::Protocol protocol, + const Network::ConnectionSocket::OptionsSharedPtr& options, + const Network::TransportSocketOptionsSharedPtr& transport_socket_options)); MOCK_METHOD5(allocateTcpConnPool, Tcp::ConnectionPool::InstancePtr( Event::Dispatcher& dispatcher, HostConstSharedPtr host,