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

proxy_proto: fixing hashing bug #13768

Merged
merged 9 commits into from
Nov 4, 2020
Merged
Show file tree
Hide file tree
Changes from 8 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
1 change: 1 addition & 0 deletions docs/root/version_history/current.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Bug Fixes
* dns: fix a bug where custom resolvers provided in configuration were not preserved after network issues.
* http: fixed URL parsing for HTTP/1.1 fully qualified URLs and connect requests containing IPv6 addresses.
* http: sending CONNECT_ERROR for HTTP/2 where appropriate during CONNECT requests.
* proxy_proto: fixed a bug where the wrong downstream address got sent to upstream connections.
* tls: fix read resumption after triggering buffer high-watermark and all remaining request/response bytes are stored in the SSL connection's internal buffers.

Removed Config or Runtime
Expand Down
6 changes: 5 additions & 1 deletion include/envoy/network/proxy_protocol.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ namespace Network {
struct ProxyProtocolData {
const Network::Address::InstanceConstSharedPtr src_addr_;
const Network::Address::InstanceConstSharedPtr dst_addr_;
std::string asStringForHash() const {
return std::string(src_addr_ ? src_addr_->asString() : "null") +
(dst_addr_ ? dst_addr_->asString() : "null");
}
};

} // namespace Network
} // namespace Envoy
} // namespace Envoy
16 changes: 12 additions & 4 deletions include/envoy/network/transport_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
namespace Envoy {
namespace Network {

class TransportSocketFactory;
class Connection;
enum class ConnectionEvent;

Expand Down Expand Up @@ -198,11 +199,13 @@ class TransportSocketOptions {
virtual absl::optional<Network::ProxyProtocolData> proxyProtocolOptions() 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
* not be modified.
* @param key supplies a 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 not be modified.
* @param factory supplies the factor which will be used for creating the transport socket.
*/
virtual void hashKey(std::vector<uint8_t>& key) const PURE;
virtual void hashKey(std::vector<uint8_t>& key,
const Network::TransportSocketFactory& factory) const PURE;
};

// TODO(mattklein123): Rename to TransportSocketOptionsConstSharedPtr in a dedicated follow up.
Expand All @@ -227,6 +230,11 @@ class TransportSocketFactory {
virtual TransportSocketPtr
createTransportSocket(TransportSocketOptionsSharedPtr options) const PURE;

/**
* @return bool whether the transport socket will use proxy protocol options.
*/
virtual bool usesProxyProtocolOptions() const PURE;

/**
* Check whether matched transport socket which required to use secret information is available.
*/
Expand Down
1 change: 1 addition & 0 deletions source/common/network/raw_buffer_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ class RawBufferSocketFactory : public TransportSocketFactory {
// Network::TransportSocketFactory
TransportSocketPtr createTransportSocket(TransportSocketOptionsSharedPtr options) const override;
bool implementsSecureTransport() const override;
bool usesProxyProtocolOptions() const override { return false; }
bool isReady() const override;
};

Expand Down
22 changes: 17 additions & 5 deletions source/common/network/transport_socket_options_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
namespace Envoy {
namespace Network {
namespace {
void commonHashKey(const TransportSocketOptions& options, std::vector<std::uint8_t>& key) {
void commonHashKey(const TransportSocketOptions& options, std::vector<std::uint8_t>& key,
const Network::TransportSocketFactory& factory) {
const auto& server_name_overide = options.serverNameOverride();
if (server_name_overide.has_value()) {
pushScalarToByteVector(StringUtil::CaseInsensitiveHash()(server_name_overide.value()), key);
Expand All @@ -35,19 +36,30 @@ void commonHashKey(const TransportSocketOptions& options, std::vector<std::uint8
pushScalarToByteVector(StringUtil::CaseInsensitiveHash()(protocol), key);
}
}

const auto& alpn_fallback = options.applicationProtocolFallback();
if (alpn_fallback.has_value()) {
pushScalarToByteVector(StringUtil::CaseInsensitiveHash()(*alpn_fallback), key);
}

// Proxy protocol options should only be included in the hash if the upstream
// socket intends to use them.
const auto& proxy_protocol_options = options.proxyProtocolOptions();
if (proxy_protocol_options.has_value() && factory.usesProxyProtocolOptions()) {
pushScalarToByteVector(
StringUtil::CaseInsensitiveHash()(proxy_protocol_options.value().asStringForHash()), key);
}
}
} // namespace

void AlpnDecoratingTransportSocketOptions::hashKey(std::vector<uint8_t>& key) const {
commonHashKey(*this, key);
void AlpnDecoratingTransportSocketOptions::hashKey(
std::vector<uint8_t>& key, const Network::TransportSocketFactory& factory) const {
commonHashKey(*this, key, factory);
}

void TransportSocketOptionsImpl::hashKey(std::vector<uint8_t>& key) const {
commonHashKey(*this, key);
void TransportSocketOptionsImpl::hashKey(std::vector<uint8_t>& key,
const Network::TransportSocketFactory& factory) const {
commonHashKey(*this, key, factory);
}

TransportSocketOptionsSharedPtr
Expand Down
6 changes: 4 additions & 2 deletions source/common/network/transport_socket_options_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ class AlpnDecoratingTransportSocketOptions : public TransportSocketOptions {
absl::optional<Network::ProxyProtocolData> proxyProtocolOptions() const override {
return inner_options_->proxyProtocolOptions();
}
void hashKey(std::vector<uint8_t>& key) const override;
void hashKey(std::vector<uint8_t>& key,
const Network::TransportSocketFactory& factory) const override;

private:
const absl::optional<std::string> alpn_fallback_;
Expand Down Expand Up @@ -67,7 +68,8 @@ class TransportSocketOptionsImpl : public TransportSocketOptions {
absl::optional<Network::ProxyProtocolData> proxyProtocolOptions() const override {
return proxy_protocol_options_;
}
void hashKey(std::vector<uint8_t>& key) const override;
void hashKey(std::vector<uint8_t>& key,
const Network::TransportSocketFactory& factory) const override;

private:
const absl::optional<std::string> override_server_name_;
Expand Down
4 changes: 2 additions & 2 deletions source/common/upstream/cluster_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1376,7 +1376,7 @@ ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::connPool(

bool have_transport_socket_options = false;
if (context && context->upstreamTransportSocketOptions()) {
context->upstreamTransportSocketOptions()->hashKey(hash_key);
context->upstreamTransportSocketOptions()->hashKey(hash_key, host->transportSocketFactory());
have_transport_socket_options = true;
}

Expand Down Expand Up @@ -1436,7 +1436,7 @@ ClusterManagerImpl::ThreadLocalClusterManagerImpl::ClusterEntry::tcpConnPool(
bool have_transport_socket_options = false;
if (context != nullptr && context->upstreamTransportSocketOptions() != nullptr) {
have_transport_socket_options = true;
context->upstreamTransportSocketOptions()->hashKey(hash_key);
context->upstreamTransportSocketOptions()->hashKey(hash_key, host->transportSocketFactory());
}

TcpConnPoolsContainer& container = parent_.host_tcp_conn_pool_map_[host];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class QuicTransportSocketFactoryBase : public Network::TransportSocketFactory {
NOT_REACHED_GCOVR_EXCL_LINE;
}
bool implementsSecureTransport() const override { return true; }
bool usesProxyProtocolOptions() const override { return false; }
bool isReady() const override { return true; }
};

Expand Down
1 change: 1 addition & 0 deletions source/extensions/transport_sockets/alts/tsi_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class TsiSocketFactory : public Network::TransportSocketFactory {
TsiSocketFactory(HandshakerFactory handshaker_factory, HandshakeValidator handshake_validator);

bool implementsSecureTransport() const override;
bool usesProxyProtocolOptions() const override { return false; }
Network::TransportSocketPtr
createTransportSocket(Network::TransportSocketOptionsSharedPtr options) const override;
bool isReady() const override;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class UpstreamProxyProtocolSocketFactory : public Network::TransportSocketFactor
Network::TransportSocketPtr
createTransportSocket(Network::TransportSocketOptionsSharedPtr options) const override;
bool implementsSecureTransport() const override;
bool usesProxyProtocolOptions() const override { return true; }
bool isReady() const override;

private:
Expand All @@ -59,4 +60,4 @@ class UpstreamProxyProtocolSocketFactory : public Network::TransportSocketFactor
} // namespace ProxyProtocol
} // namespace TransportSockets
} // namespace Extensions
} // namespace Envoy
} // namespace Envoy
4 changes: 4 additions & 0 deletions source/extensions/transport_sockets/tap/tap.cc
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ bool TapSocketFactory::implementsSecureTransport() const {
return transport_socket_factory_->implementsSecureTransport();
}

bool TapSocketFactory::usesProxyProtocolOptions() const {
return transport_socket_factory_->usesProxyProtocolOptions();
}

bool TapSocketFactory::isReady() const { return transport_socket_factory_->isReady(); }

} // namespace Tap
Expand Down
1 change: 1 addition & 0 deletions source/extensions/transport_sockets/tap/tap.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class TapSocketFactory : public Network::TransportSocketFactory,
Network::TransportSocketPtr
createTransportSocket(Network::TransportSocketOptionsSharedPtr options) const override;
bool implementsSecureTransport() const override;
bool usesProxyProtocolOptions() const override;
bool isReady() const override;

private:
Expand Down
4 changes: 4 additions & 0 deletions source/extensions/transport_sockets/tls/ssl_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@ class ClientSslSocketFactory : public Network::TransportSocketFactory,
Network::TransportSocketPtr
createTransportSocket(Network::TransportSocketOptionsSharedPtr options) const override;
bool implementsSecureTransport() const override;
bool usesProxyProtocolOptions() const override { return false; }
bool isReady() const override;

// Secret::SecretCallbacks
void onAddOrUpdateSecret() override;

Expand All @@ -134,7 +136,9 @@ class ServerSslSocketFactory : public Network::TransportSocketFactory,
Network::TransportSocketPtr
createTransportSocket(Network::TransportSocketOptionsSharedPtr options) const override;
bool implementsSecureTransport() const override;
bool usesProxyProtocolOptions() const override { return false; }
bool isReady() const override;

// Secret::SecretCallbacks
void onAddOrUpdateSecret() override;

Expand Down
9 changes: 9 additions & 0 deletions test/common/network/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,15 @@ envoy_cc_test(
],
)

envoy_cc_test(
name = "raw_buffer_socket_test",
srcs = ["raw_buffer_socket_test.cc"],
deps = [
"//source/common/network:raw_buffer_socket_lib",
"//test/test_common:network_utility_lib",
],
)

envoy_cc_test_library(
name = "udp_listener_impl_test_base_lib",
hdrs = ["udp_listener_impl_test_base.h"],
Expand Down
16 changes: 16 additions & 0 deletions test/common/network/raw_buffer_socket_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include "common/network/raw_buffer_socket.h"

#include "test/test_common/network_utility.h"

#include "gtest/gtest.h"

namespace Envoy {
namespace Network {

TEST(RawBufferSocketFactory, RawBufferSocketFactory) {
TransportSocketFactoryPtr factory = Envoy::Network::Test::createRawBufferSocketFactory();
EXPECT_FALSE(factory->usesProxyProtocolOptions());
}

} // namespace Network
} // namespace Envoy
2 changes: 2 additions & 0 deletions test/common/upstream/transport_socket_matcher_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ namespace {
class FakeTransportSocketFactory : public Network::TransportSocketFactory {
public:
MOCK_METHOD(bool, implementsSecureTransport, (), (const));
MOCK_METHOD(bool, usesProxyProtocolOptions, (), (const));
MOCK_METHOD(Network::TransportSocketPtr, createTransportSocket,
(Network::TransportSocketOptionsSharedPtr), (const));
MOCK_METHOD(bool, isReady, (), (const));
Expand All @@ -47,6 +48,7 @@ class FooTransportSocketFactory
Logger::Loggable<Logger::Id::upstream> {
public:
MOCK_METHOD(bool, implementsSecureTransport, (), (const));
MOCK_METHOD(bool, usesProxyProtocolOptions, (), (const));
MOCK_METHOD(Network::TransportSocketPtr, createTransportSocket,
(Network::TransportSocketOptionsSharedPtr), (const));
MOCK_METHOD(bool, isReady, (), (const));
Expand Down
7 changes: 6 additions & 1 deletion test/extensions/transport_sockets/alts/tsi_socket_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,6 @@ class TsiSocketTest : public testing::Test {
client_.tsi_socket_ =
std::make_unique<TsiSocket>(client_.handshaker_factory_, client_validator,
Network::TransportSocketPtr{client_.raw_socket_});

ON_CALL(client_.callbacks_.connection_, dispatcher()).WillByDefault(ReturnRef(dispatcher_));
ON_CALL(server_.callbacks_.connection_, dispatcher()).WillByDefault(ReturnRef(dispatcher_));

Expand Down Expand Up @@ -194,6 +193,7 @@ static const std::string ClientToServerData = "hello from client";
TEST_F(TsiSocketTest, DoesNotHaveSsl) {
initialize(nullptr, nullptr);
EXPECT_EQ(nullptr, client_.tsi_socket_->ssl());
EXPECT_FALSE(client_.tsi_socket_->canFlushClose());

const auto& socket_ = *client_.tsi_socket_;
EXPECT_EQ(nullptr, socket_.ssl());
Expand Down Expand Up @@ -406,6 +406,11 @@ TEST_F(TsiSocketFactoryTest, ImplementsSecureTransport) {
EXPECT_TRUE(socket_factory_->implementsSecureTransport());
}

TEST_F(TsiSocketFactoryTest, UsesProxyProtocolOptions) {
EXPECT_FALSE(socket_factory_->usesProxyProtocolOptions());
EXPECT_TRUE(socket_factory_->isReady());
}

} // namespace
} // namespace Alts
} // namespace TransportSockets
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,36 @@ TEST_P(ProxyProtocolIntegrationTest, TestV1ProxyProtocol) {
ASSERT_TRUE(fake_upstream_connection_->waitForDisconnect());
}

TEST_P(ProxyProtocolIntegrationTest, TestV1ProxyProtocolMultipleConnections) {
if (GetParam() != Network::Address::IpVersion::v4) {
return;
}

setup(envoy::config::core::v3::ProxyProtocolConfig::V1, false,
"envoy.transport_sockets.raw_buffer");
initialize();
auto listener_port = lookupPort("listener_0");

auto loopback2 = Network::Utility::resolveUrl("tcp://127.0.0.2:0");
auto tcp_client2 = makeTcpConnection(listener_port, nullptr, loopback2);

auto tcp_client = makeTcpConnection(listener_port);

ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection_));
FakeRawConnectionPtr conn2;
ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(conn2));

std::string data1, data2;
ASSERT_TRUE(
fake_upstream_connection_->waitForData(FakeRawConnection::waitForAtLeastBytes(32), &data1));
ASSERT_TRUE(conn2->waitForData(FakeRawConnection::waitForAtLeastBytes(32), &data2));

EXPECT_NE(data1, data2);

tcp_client->close();
tcp_client2->close();
}

// Test header is sent unencrypted using a TLS inner socket
TEST_P(ProxyProtocolIntegrationTest, TestTLSSocket) {
setup(envoy::config::core::v3::ProxyProtocolConfig::V1, false, "envoy.transport_sockets.tls");
Expand Down Expand Up @@ -201,4 +231,4 @@ TEST_P(ProxyProtocolIntegrationTest, TestV2ProxyProtocol) {
}

} // namespace
} // namespace Envoy
} // namespace Envoy
16 changes: 15 additions & 1 deletion test/extensions/transport_sockets/tap/tap_config_impl_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,11 @@ class PerSocketTapperImplTest : public testing::Test {
EXPECT_CALL(matcher_, onNewStream(_))
.WillOnce(Invoke([this](TapCommon::Matcher::MatchStatusVector& statuses) {
statuses_ = &statuses;
statuses[0].matches_ = true;
if (fail_match_) {
statuses[0].matches_ = false;
} else {
statuses[0].matches_ = true;
}
statuses[0].might_change_status_ = false;
}));
EXPECT_CALL(*config_, streaming()).WillRepeatedly(Return(streaming));
Expand All @@ -79,6 +83,7 @@ class PerSocketTapperImplTest : public testing::Test {
TapCommon::Matcher::MatchStatusVector* statuses_;
NiceMock<Network::MockConnection> connection_;
Event::SimulatedTimeSystem time_system_;
bool fail_match_{};
};

// Verify the full streaming flow.
Expand Down Expand Up @@ -139,6 +144,15 @@ TEST_F(PerSocketTapperImplTest, StreamingFlow) {
tapper_->closeSocket(Network::ConnectionEvent::RemoteClose);
}

TEST_F(PerSocketTapperImplTest, NonMatchingFlow) {
fail_match_ = true;
setup(true);

EXPECT_CALL(*sink_manager_, submitTrace_(_)).Times(0);
time_system_.setSystemTime(std::chrono::seconds(2));
tapper_->closeSocket(Network::ConnectionEvent::RemoteClose);
}

} // namespace
} // namespace Tap
} // namespace TransportSockets
Expand Down
5 changes: 3 additions & 2 deletions test/integration/base_integration_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -190,9 +190,10 @@ void BaseIntegrationTest::setUpstreamProtocol(FakeHttpConnection::Type protocol)

IntegrationTcpClientPtr
BaseIntegrationTest::makeTcpConnection(uint32_t port,
const Network::ConnectionSocket::OptionsSharedPtr& options) {
const Network::ConnectionSocket::OptionsSharedPtr& options,
Network::Address::InstanceConstSharedPtr source_address) {
return std::make_unique<IntegrationTcpClient>(*dispatcher_, *mock_buffer_factory_, port, version_,
enable_half_close_, options);
enable_half_close_, options, source_address);
}

void BaseIntegrationTest::registerPort(const std::string& key, uint32_t port) {
Expand Down
Loading