Skip to content

Commit

Permalink
tls: add functionality to override requested server name in the upstr…
Browse files Browse the repository at this point in the history
…eam cluster (#4973)

Add functionality to override requested server name in the upstream cluster

Risk Level: medium
Testing: unit tests
Docs Changes: yes
Release Notes: yes

Related to #4076

Signed-off-by: Vadim Eisenberg <[email protected]>
  • Loading branch information
vadimeisenbergibm authored and htuch committed Nov 21, 2018
1 parent 08310eb commit 48b161e
Show file tree
Hide file tree
Showing 68 changed files with 889 additions and 293 deletions.
32 changes: 31 additions & 1 deletion include/envoy/network/transport_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "envoy/common/pure.h"
#include "envoy/ssl/connection.h"

#include "absl/types/optional.h"

namespace Envoy {
namespace Network {

Expand Down Expand Up @@ -136,6 +138,32 @@ class TransportSocket {

typedef std::unique_ptr<TransportSocket> TransportSocketPtr;

/**
* Options for creating transport sockets.
*/
class TransportSocketOptions {
public:
virtual ~TransportSocketOptions() {}

/**
* @return the const optional server name to set in the transport socket, for example SNI for
* SSL, regardless of the upstream cluster configuration. Filters that influence
* upstream connection selection, such as tcp_proxy, should take this option into account
* and should pass it through to the connection pool to ensure the correct endpoints are
* selected and the upstream connection is set up accordingly.
*/
virtual const absl::optional<std::string>& serverNameOverride() 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.
*/
virtual void hashKey(std::vector<uint8_t>& key) const PURE;
};

typedef std::shared_ptr<TransportSocketOptions> TransportSocketOptionsSharedPtr;

/**
* A factory for creating transport socket. It will be associated to filter chains and clusters.
*/
Expand All @@ -149,9 +177,11 @@ class TransportSocketFactory {
virtual bool implementsSecureTransport() const PURE;

/**
* @param options for creating the transport socket
* @return Network::TransportSocketPtr a transport socket to be passed to connection.
*/
virtual TransportSocketPtr createTransportSocket() const PURE;
virtual TransportSocketPtr
createTransportSocket(TransportSocketOptionsSharedPtr options) const PURE;
};

typedef std::unique_ptr<TransportSocketFactory> TransportSocketFactoryPtr;
Expand Down
15 changes: 9 additions & 6 deletions include/envoy/upstream/cluster_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,10 @@ class ClusterManager {
* Can return nullptr if there is no host available in the cluster or if the cluster does not
* exist.
*/
virtual Tcp::ConnectionPool::Instance* tcpConnPoolForCluster(const std::string& cluster,
ResourcePriority priority,
LoadBalancerContext* context) PURE;
virtual Tcp::ConnectionPool::Instance*
tcpConnPoolForCluster(const std::string& cluster, ResourcePriority priority,
LoadBalancerContext* context,
Network::TransportSocketOptionsSharedPtr transport_socket_options) PURE;

/**
* Allocate a load balanced TCP connection for a cluster. The created connection is already
Expand All @@ -143,8 +144,9 @@ class ClusterManager {
* Returns both a connection and the host that backs the connection. Both can be nullptr if there
* is no host available in the cluster.
*/
virtual Host::CreateConnectionData tcpConnForCluster(const std::string& cluster,
LoadBalancerContext* context) PURE;
virtual Host::CreateConnectionData
tcpConnForCluster(const std::string& cluster, LoadBalancerContext* context,
Network::TransportSocketOptionsSharedPtr transport_socket_options) PURE;

/**
* Returns a client that can be used to make async HTTP calls against the given cluster. The
Expand Down Expand Up @@ -271,7 +273,8 @@ class ClusterManagerFactory {
virtual Tcp::ConnectionPool::InstancePtr
allocateTcpConnPool(Event::Dispatcher& dispatcher, HostConstSharedPtr host,
ResourcePriority priority,
const Network::ConnectionSocket::OptionsSharedPtr& options) PURE;
const Network::ConnectionSocket::OptionsSharedPtr& options,
Network::TransportSocketOptionsSharedPtr transport_socket_options) PURE;

/**
* Allocate a cluster from configuration proto.
Expand Down
3 changes: 2 additions & 1 deletion include/envoy/upstream/upstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ class Host : virtual public HostDescription {
*/
virtual CreateConnectionData
createConnection(Event::Dispatcher& dispatcher,
const Network::ConnectionSocket::OptionsSharedPtr& options) const PURE;
const Network::ConnectionSocket::OptionsSharedPtr& options,
Network::TransportSocketOptionsSharedPtr transport_socket_options) const PURE;

/**
* Create a health check connection for this host.
Expand Down
5 changes: 5 additions & 0 deletions source/common/common/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,11 @@ envoy_cc_library(
],
)

envoy_cc_library(
name = "scalar_to_byte_vector_lib",
hdrs = ["scalar_to_byte_vector.h"],
)

envoy_cc_library(
name = "token_bucket_impl_lib",
srcs = ["token_bucket_impl.cc"],
Expand Down
14 changes: 14 additions & 0 deletions source/common/common/scalar_to_byte_vector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <inttypes.h>

#include <vector>

namespace Envoy {
template <typename T> void pushScalarToByteVector(T val, std::vector<uint8_t>& bytes) {
uint8_t* byte_ptr = reinterpret_cast<uint8_t*>(&val);
for (uint32_t byte_index = 0; byte_index < sizeof val; byte_index++) {
bytes.push_back(*byte_ptr++);
}
}
} // namespace Envoy
2 changes: 1 addition & 1 deletion source/common/http/http1/conn_pool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ ConnPoolImpl::ActiveClient::ActiveClient(ConnPoolImpl& parent)
parent_.conn_connect_ms_ = std::make_unique<Stats::Timespan>(
parent_.host_->cluster().stats().upstream_cx_connect_ms_, parent_.dispatcher_.timeSystem());
Upstream::Host::CreateConnectionData data =
parent_.host_->createConnection(parent_.dispatcher_, parent_.socket_options_);
parent_.host_->createConnection(parent_.dispatcher_, parent_.socket_options_, nullptr);
real_host_description_ = data.host_description_;
codec_client_ = parent_.createCodecClient(data);
codec_client_->addConnectionCallbacks(*this);
Expand Down
2 changes: 1 addition & 1 deletion source/common/http/http2/conn_pool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,7 @@ ConnPoolImpl::ActiveClient::ActiveClient(ConnPoolImpl& parent)
parent_.conn_connect_ms_ = std::make_unique<Stats::Timespan>(
parent_.host_->cluster().stats().upstream_cx_connect_ms_, parent_.dispatcher_.timeSystem());
Upstream::Host::CreateConnectionData data =
parent_.host_->createConnection(parent_.dispatcher_, parent_.socket_options_);
parent_.host_->createConnection(parent_.dispatcher_, parent_.socket_options_, nullptr);
real_host_description_ = data.host_description_;
client_ = parent_.createCodecClient(data);
client_->addConnectionCallbacks(*this);
Expand Down
21 changes: 21 additions & 0 deletions source/common/network/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -240,3 +240,24 @@ envoy_cc_library(
"@envoy_api//envoy/api/v2/core:base_cc",
],
)

envoy_cc_library(
name = "transport_socket_options_lib",
srcs = ["transport_socket_options_impl.cc"],
hdrs = ["transport_socket_options_impl.h"],
deps = [
"//include/envoy/network:transport_socket_interface",
"//source/common/common:scalar_to_byte_vector_lib",
"//source/common/common:utility_lib",
],
)

envoy_cc_library(
name = "upstream_server_name_lib",
srcs = ["upstream_server_name.cc"],
hdrs = ["upstream_server_name.h"],
deps = [
"//include/envoy/stream_info:filter_state_interface",
"//source/common/common:macros",
],
)
3 changes: 2 additions & 1 deletion source/common/network/raw_buffer_socket.cc
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,8 @@ std::string RawBufferSocket::protocol() const { return EMPTY_STRING; }

void RawBufferSocket::onConnected() { callbacks_->raiseEvent(ConnectionEvent::Connected); }

TransportSocketPtr RawBufferSocketFactory::createTransportSocket() const {
TransportSocketPtr
RawBufferSocketFactory::createTransportSocket(TransportSocketOptionsSharedPtr) const {
return std::make_unique<RawBufferSocket>();
}

Expand Down
2 changes: 1 addition & 1 deletion source/common/network/raw_buffer_socket.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class RawBufferSocket : public TransportSocket, protected Logger::Loggable<Logge
class RawBufferSocketFactory : public TransportSocketFactory {
public:
// Network::TransportSocketFactory
TransportSocketPtr createTransportSocket() const override;
TransportSocketPtr createTransportSocket(TransportSocketOptionsSharedPtr options) const override;
bool implementsSecureTransport() const override;
};

Expand Down
16 changes: 16 additions & 0 deletions source/common/network/transport_socket_options_impl.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#include "common/network/transport_socket_options_impl.h"

#include "common/common/scalar_to_byte_vector.h"
#include "common/common/utility.h"

namespace Envoy {
namespace Network {
void TransportSocketOptionsImpl::hashKey(std::vector<uint8_t>& key) const {
if (!override_server_name_.has_value()) {
return;
}

pushScalarToByteVector(StringUtil::CaseInsensitiveHash()(override_server_name_.value()), key);
}
} // namespace Network
} // namespace Envoy
26 changes: 26 additions & 0 deletions source/common/network/transport_socket_options_impl.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include "envoy/network/transport_socket.h"

namespace Envoy {
namespace Network {

class TransportSocketOptionsImpl : public TransportSocketOptions {
public:
TransportSocketOptionsImpl(absl::string_view override_server_name = "")
: override_server_name_(override_server_name.empty()
? absl::nullopt
: absl::optional<std::string>(override_server_name)) {}

// Network::TransportSocketOptions
const absl::optional<std::string>& serverNameOverride() const override {
return override_server_name_;
}
void hashKey(std::vector<uint8_t>& key) const override;

private:
const absl::optional<std::string> override_server_name_;
};

} // namespace Network
} // namespace Envoy
12 changes: 12 additions & 0 deletions source/common/network/upstream_server_name.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#include "common/network/upstream_server_name.h"

#include "common/common/macros.h"

namespace Envoy {
namespace Network {

const std::string& UpstreamServerName::key() {
CONSTRUCT_ON_FIRST_USE(std::string, "envoy.network.upstream_server_name");
}
} // namespace Network
} // namespace Envoy
26 changes: 26 additions & 0 deletions source/common/network/upstream_server_name.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#pragma once

#include "envoy/stream_info/filter_state.h"

#include "absl/strings/string_view.h"

namespace Envoy {
namespace Network {

/**
* Server name to set in the upstream connection. The filters like tcp_proxy should use this
* value to override the server name specified in the upstream cluster, for example to override
* the SNI value in the upstream TLS context.
*/
class UpstreamServerName : public StreamInfo::FilterState::Object {
public:
UpstreamServerName(absl::string_view server_name) : server_name_(server_name) {}
const std::string& value() const { return server_name_; }
static const std::string& key();

private:
const std::string server_name_;
};

} // namespace Network
} // namespace Envoy
14 changes: 9 additions & 5 deletions source/common/ssl/context_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ std::vector<uint8_t> ContextImpl::parseAlpnProtocols(const std::string& alpn_pro
return out;
}

bssl::UniquePtr<SSL> ContextImpl::newSsl() const {
bssl::UniquePtr<SSL> ContextImpl::newSsl(absl::optional<std::string>) const {
return bssl::UniquePtr<SSL>(SSL_new(ctx_.get()));
}

Expand Down Expand Up @@ -498,11 +498,15 @@ ClientContextImpl::ClientContextImpl(Stats::Scope& scope, const ClientContextCon
}
}

bssl::UniquePtr<SSL> ClientContextImpl::newSsl() const {
bssl::UniquePtr<SSL> ssl_con(ContextImpl::newSsl());
bssl::UniquePtr<SSL>
ClientContextImpl::newSsl(absl::optional<std::string> override_server_name) const {
bssl::UniquePtr<SSL> ssl_con(ContextImpl::newSsl(absl::nullopt));

if (!server_name_indication_.empty()) {
int rc = SSL_set_tlsext_host_name(ssl_con.get(), server_name_indication_.c_str());
std::string server_name_indication =
override_server_name.has_value() ? override_server_name.value() : server_name_indication_;

if (!server_name_indication.empty()) {
int rc = SSL_set_tlsext_host_name(ssl_con.get(), server_name_indication.c_str());
RELEASE_ASSERT(rc, "");
}

Expand Down
5 changes: 3 additions & 2 deletions source/common/ssl/context_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include "common/ssl/context_manager_impl.h"

#include "absl/types/optional.h"
#include "openssl/ssl.h"

namespace Envoy {
Expand Down Expand Up @@ -41,7 +42,7 @@ struct SslStats {

class ContextImpl : public virtual Context {
public:
virtual bssl::UniquePtr<SSL> newSsl() const;
virtual bssl::UniquePtr<SSL> newSsl(absl::optional<std::string> override_server_name) const;

/**
* Logs successful TLS handshake and updates stats.
Expand Down Expand Up @@ -142,7 +143,7 @@ class ClientContextImpl : public ContextImpl, public ClientContext {
ClientContextImpl(Stats::Scope& scope, const ClientContextConfig& config,
TimeSource& time_source);

bssl::UniquePtr<SSL> newSsl() const override;
bssl::UniquePtr<SSL> newSsl(absl::optional<std::string> override_server_name) const override;

private:
const std::string server_name_indication_;
Expand Down
19 changes: 13 additions & 6 deletions source/common/ssl/ssl_socket.cc
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,12 @@ class NotReadySslSocket : public Network::TransportSocket {
};
} // namespace

SslSocket::SslSocket(ContextSharedPtr ctx, InitialState state)
: ctx_(std::dynamic_pointer_cast<ContextImpl>(ctx)), ssl_(ctx_->newSsl()) {
SslSocket::SslSocket(ContextSharedPtr ctx, InitialState state,
Network::TransportSocketOptionsSharedPtr transport_socket_options)
: ctx_(std::dynamic_pointer_cast<ContextImpl>(ctx)),
ssl_(ctx_->newSsl(transport_socket_options != nullptr
? transport_socket_options->serverNameOverride()
: absl::nullopt)) {
if (state == InitialState::Client) {
SSL_set_connect_state(ssl_.get());
} else {
Expand Down Expand Up @@ -370,7 +374,8 @@ ClientSslSocketFactory::ClientSslSocketFactory(ClientContextConfigPtr config,
config_->setSecretUpdateCallback([this]() { onAddOrUpdateSecret(); });
}

Network::TransportSocketPtr ClientSslSocketFactory::createTransportSocket() const {
Network::TransportSocketPtr ClientSslSocketFactory::createTransportSocket(
Network::TransportSocketOptionsSharedPtr transport_socket_options) const {
// onAddOrUpdateSecret() could be invoked in the middle of checking the existence of ssl_ctx and
// creating SslSocket using ssl_ctx. Capture ssl_ctx_ into a local variable so that we check and
// use the same ssl_ctx to create SslSocket.
Expand All @@ -380,7 +385,8 @@ Network::TransportSocketPtr ClientSslSocketFactory::createTransportSocket() cons
ssl_ctx = ssl_ctx_;
}
if (ssl_ctx) {
return std::make_unique<Ssl::SslSocket>(std::move(ssl_ctx), Ssl::InitialState::Client);
return std::make_unique<Ssl::SslSocket>(std::move(ssl_ctx), Ssl::InitialState::Client,
transport_socket_options);
} else {
ENVOY_LOG(debug, "Create NotReadySslSocket");
stats_.upstream_context_secrets_not_ready_.inc();
Expand Down Expand Up @@ -409,7 +415,8 @@ ServerSslSocketFactory::ServerSslSocketFactory(ServerContextConfigPtr config,
config_->setSecretUpdateCallback([this]() { onAddOrUpdateSecret(); });
}

Network::TransportSocketPtr ServerSslSocketFactory::createTransportSocket() const {
Network::TransportSocketPtr
ServerSslSocketFactory::createTransportSocket(Network::TransportSocketOptionsSharedPtr) const {
// onAddOrUpdateSecret() could be invoked in the middle of checking the existence of ssl_ctx and
// creating SslSocket using ssl_ctx. Capture ssl_ctx_ into a local variable so that we check and
// use the same ssl_ctx to create SslSocket.
Expand All @@ -419,7 +426,7 @@ Network::TransportSocketPtr ServerSslSocketFactory::createTransportSocket() cons
ssl_ctx = ssl_ctx_;
}
if (ssl_ctx) {
return std::make_unique<Ssl::SslSocket>(std::move(ssl_ctx), Ssl::InitialState::Server);
return std::make_unique<Ssl::SslSocket>(std::move(ssl_ctx), Ssl::InitialState::Server, nullptr);
} else {
ENVOY_LOG(debug, "Create NotReadySslSocket");
stats_.downstream_context_secrets_not_ready_.inc();
Expand Down
Loading

0 comments on commit 48b161e

Please sign in to comment.