Skip to content
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
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
10 changes: 10 additions & 0 deletions include/envoy/network/filter.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@ class ReadFilterCallbacks {
* Set the currently selected upstream host for the connection.
*/
virtual void upstreamHost(Upstream::HostDescriptionConstSharedPtr host) PURE;

/**
* Return the requested server name (e.g. SNI in TLS) of the network level, if any.
*/
virtual absl::string_view networkLevelRequestedServerName() PURE;

/**
* Set the requested server name (e.g. SNI in TLS) of the network level, if any.
*/
virtual void networkLevelRequestedServerName(absl::string_view name) PURE;
};

/**
Expand Down
9 changes: 9 additions & 0 deletions source/common/network/filter_manager_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,14 @@ class FilterManagerImpl {
void upstreamHost(Upstream::HostDescriptionConstSharedPtr host) override {
parent_.host_description_ = host;
}
absl::string_view networkLevelRequestedServerName() override {
// TODO: return an empty string and write a warning to log when inner SNI reader is not
// configured.
return parent_.network_level_requested_server_name_;
}
void networkLevelRequestedServerName(absl::string_view name) override {
parent_.network_level_requested_server_name_ = name;
}

FilterManagerImpl& parent_;
ReadFilterSharedPtr filter_;
Expand All @@ -73,6 +81,7 @@ class FilterManagerImpl {

Connection& connection_;
BufferSource& buffer_source_;
absl::string_view network_level_requested_server_name_;
Upstream::HostDescriptionConstSharedPtr host_description_;
std::list<ActiveReadFilterPtr> upstream_filters_;
std::list<WriteFilterSharedPtr> downstream_filters_;
Expand Down
4 changes: 4 additions & 0 deletions source/common/ssl/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,8 @@ envoy_cc_library(
external_deps = [
"ssl",
],
deps = [
"//include/envoy/stats:stats_macros",
"//source/common/common:assert_lib",
],
)
53 changes: 53 additions & 0 deletions source/common/ssl/utility.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
#include "common/ssl/utility.h"

#include "common/common/assert.h"

#include "openssl/bytestring.h"
#include "openssl/ssl.h"

namespace Envoy {
namespace Ssl {

Expand All @@ -18,5 +23,53 @@ std::string Utility::getSerialNumberFromCertificate(X509& cert) {
return "";
}

void Utility::parseClientHello(const void* data, size_t len, bssl::UniquePtr<SSL>& ssl,
uint64_t read, uint32_t maxClientHelloSize,
const Ssl::Utility::TlsStats& stats, std::function<void(bool)> done,
bool& alpn_found, bool& clienthello_success,
std::function<void()> onSuccess) {
// Ownership is passed to ssl in SSL_set_bio()
bssl::UniquePtr<BIO> bio(BIO_new_mem_buf(data, len));

// Make the mem-BIO return that there is more data
// available beyond it's end
BIO_set_mem_eof_return(bio.get(), -1);

SSL_set_bio(ssl.get(), bio.get(), bio.get());
bio.release();

int ret = SSL_do_handshake(ssl.get());

// This should never succeed because an error is always returned from the SNI callback.
ASSERT(ret <= 0);
switch (SSL_get_error(ssl.get(), ret)) {
case SSL_ERROR_WANT_READ:
if (read == maxClientHelloSize) {
// We've hit the specified size limit. This is an unreasonably large ClientHello;
// indicate failure.
stats.client_hello_too_large_.inc();
done(false);
}
break;
case SSL_ERROR_SSL:
if (clienthello_success) {
stats.tls_found_.inc();
if (alpn_found) {
stats.alpn_found_.inc();
} else {
stats.alpn_not_found_.inc();
}
onSuccess();
} else {
stats.tls_not_found_.inc();
}
done(true);
break;
default:
done(false);
break;
}
}

} // namespace Ssl
} // namespace Envoy
29 changes: 29 additions & 0 deletions source/common/ssl/utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,36 @@

#include <string>

#include "envoy/stats/stats_macros.h"

#include "openssl/ssl.h"

namespace Envoy {
namespace Ssl {
namespace Utility {

/**
* All stats for the TLS inspector. @see stats_macros.h
*/
#define TLS_STATS(COUNTER) \
COUNTER(connection_closed) \
COUNTER(client_hello_too_large) \
COUNTER(read_error) \
COUNTER(read_timeout) \
COUNTER(tls_found) \
COUNTER(tls_not_found) \
COUNTER(alpn_found) \
COUNTER(alpn_not_found) \
COUNTER(sni_found) \
COUNTER(sni_not_found)

/**
* Definition of stats for the TLS. @see stats_macros.h
*/
struct TlsStats {
TLS_STATS(GENERATE_COUNTER_STRUCT)
};

/**
* Retrieve the serial number of a certificate.
* @param ssl the certificate
Expand All @@ -16,6 +40,11 @@ namespace Utility {
*/
std::string getSerialNumberFromCertificate(X509& cert);

void parseClientHello(const void* data, size_t len, bssl::UniquePtr<SSL>& ssl, uint64_t read,
uint32_t maxClientHelloSize, const Ssl::Utility::TlsStats& stats,
std::function<void(bool)> done, bool& alpn_found, bool& clienthello_success,
std::function<void()> onSuccess);

} // namespace Utility
} // namespace Ssl
} // namespace Envoy
1 change: 1 addition & 0 deletions source/extensions/extensions_build_config.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ EXTENSIONS = {
"envoy.filters.network.echo": "//source/extensions/filters/network/echo:config",
"envoy.filters.network.ext_authz": "//source/extensions/filters/network/ext_authz:config",
"envoy.filters.network.http_connection_manager": "//source/extensions/filters/network/http_connection_manager:config",
"envoy.filters.network.network_level_sni_reader": "//source/extensions/filters/network/network_level_sni_reader:config",
"envoy.filters.network.mongo_proxy": "//source/extensions/filters/network/mongo_proxy:config",
"envoy.filters.network.ratelimit": "//source/extensions/filters/network/ratelimit:config",
"envoy.filters.network.rbac": "//source/extensions/filters/network/rbac:config",
Expand Down
1 change: 1 addition & 0 deletions source/extensions/filters/listener/tls_inspector/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ envoy_cc_library(
"//source/common/api:os_sys_calls_lib",
"//source/common/common:assert_lib",
"//source/common/common:minimal_logger_lib",
"//source/common/ssl:utility_lib",
"//source/extensions/transport_sockets:well_known_names",
],
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace ListenerFilters {
namespace TlsInspector {

Config::Config(Stats::Scope& scope, uint32_t max_client_hello_size)
: stats_{ALL_TLS_INSPECTOR_STATS(POOL_COUNTER_PREFIX(scope, "tls_inspector."))},
: stats_{TLS_STATS(POOL_COUNTER_PREFIX(scope, "tls_inspector."))},
ssl_ctx_(SSL_CTX_new(TLS_with_buffers_method())),
max_client_hello_size_(max_client_hello_size) {

Expand Down Expand Up @@ -161,7 +161,13 @@ void Filter::onRead() {
const uint8_t* data = buf_ + read_;
const size_t len = result.rc_ - read_;
read_ = result.rc_;
parseClientHello(data, len);
Ssl::Utility::parseClientHello(data, len, ssl_, read_, config_->maxClientHelloSize(),
config_->stats(), [&](bool success) -> void { done(success); },
alpn_found_, clienthello_success_,
[&]() -> void {
cb_->socket().setDetectedTransportProtocol(
TransportSockets::TransportSocketNames::get().Tls);
});
}
}

Expand Down
28 changes: 3 additions & 25 deletions source/extensions/filters/listener/tls_inspector/tls_inspector.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
#include "envoy/event/timer.h"
#include "envoy/network/filter.h"
#include "envoy/stats/scope.h"
#include "envoy/stats/stats_macros.h"

#include "common/common/logger.h"
#include "common/ssl/utility.h"

#include "openssl/bytestring.h"
#include "openssl/ssl.h"
Expand All @@ -16,43 +16,21 @@ namespace Extensions {
namespace ListenerFilters {
namespace TlsInspector {

/**
* All stats for the TLS inspector. @see stats_macros.h
*/
#define ALL_TLS_INSPECTOR_STATS(COUNTER) \
COUNTER(connection_closed) \
COUNTER(client_hello_too_large) \
COUNTER(read_error) \
COUNTER(read_timeout) \
COUNTER(tls_found) \
COUNTER(tls_not_found) \
COUNTER(alpn_found) \
COUNTER(alpn_not_found) \
COUNTER(sni_found) \
COUNTER(sni_not_found)

/**
* Definition of all stats for the TLS inspector. @see stats_macros.h
*/
struct TlsInspectorStats {
ALL_TLS_INSPECTOR_STATS(GENERATE_COUNTER_STRUCT)
};

/**
* Global configuration for TLS inspector.
*/
class Config {
public:
Config(Stats::Scope& scope, uint32_t max_client_hello_size = TLS_MAX_CLIENT_HELLO);

const TlsInspectorStats& stats() const { return stats_; }
const Ssl::Utility::TlsStats& stats() const { return stats_; }
bssl::UniquePtr<SSL> newSsl();
uint32_t maxClientHelloSize() const { return max_client_hello_size_; }

static constexpr size_t TLS_MAX_CLIENT_HELLO = 64 * 1024;

private:
TlsInspectorStats stats_;
Ssl::Utility::TlsStats stats_;
bssl::UniquePtr<SSL_CTX> ssl_ctx_;
const uint32_t max_client_hello_size_;
};
Expand Down
35 changes: 35 additions & 0 deletions source/extensions/filters/network/network_level_sni_reader/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
licenses(["notice"]) # Apache 2

load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_package",
)

envoy_package()

envoy_cc_library(
name = "network_level_sni_reader",
srcs = ["network_level_sni_reader.cc"],
hdrs = ["network_level_sni_reader.h"],
external_deps = ["ssl"],
deps = [
"//include/envoy/buffer:buffer_interface",
"//include/envoy/network:connection_interface",
"//include/envoy/network:filter_interface",
"//source/common/common:assert_lib",
"//source/common/common:minimal_logger_lib",
"//source/common/ssl:utility_lib",
],
)

envoy_cc_library(
name = "config",
srcs = ["config.cc"],
deps = [
":network_level_sni_reader",
"//include/envoy/registry",
"//include/envoy/server:filter_config_interface",
"//source/extensions/filters/network:well_known_names",
],
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#include "envoy/registry/registry.h"
#include "envoy/server/filter_config.h"

#include "extensions/filters/network/network_level_sni_reader/network_level_sni_reader.h"
#include "extensions/filters/network/well_known_names.h"

namespace Envoy {
namespace Extensions {
namespace NetworkFilters {
namespace NetworkLevelSniReader {

/**
* Config registration for the network level SNI reader filter. @see
* NamedNetworkFilterConfigFactory.
*/
class NetworkLevelSniReaderConfigFactory
: public Server::Configuration::NamedNetworkFilterConfigFactory {
public:
// NamedNetworkFilterConfigFactory
Network::FilterFactoryCb
createFilterFactory(const Json::Object&,
Server::Configuration::FactoryContext& context) override {
// TODO: call createFilterFactoryFromProto and remove duplicate code
ConfigSharedPtr filter_config(new Config(context.scope()));
return [filter_config](Network::FilterManager& filter_manager) -> void {
filter_manager.addReadFilter(std::make_shared<NetworkLevelSniReaderFilter>(filter_config));
};
}

Network::FilterFactoryCb
createFilterFactoryFromProto(const Protobuf::Message&,
Server::Configuration::FactoryContext& context) override {
ConfigSharedPtr filter_config(new Config(context.scope()));
return [filter_config](Network::FilterManager& filter_manager) -> void {
filter_manager.addReadFilter(std::make_shared<NetworkLevelSniReaderFilter>(filter_config));
};
}

ProtobufTypes::MessagePtr createEmptyConfigProto() override {
return ProtobufTypes::MessagePtr{new Envoy::ProtobufWkt::Empty()};
}

std::string name() override { return NetworkFilterNames::get().NetworkLevelSniReader; }
};

/**
* Static registration for the echo filter. @see RegisterFactory.
*/
static Registry::RegisterFactory<NetworkLevelSniReaderConfigFactory,
Server::Configuration::NamedNetworkFilterConfigFactory>
registered_;

} // namespace NetworkLevelSniReader
} // namespace NetworkFilters
} // namespace Extensions
} // namespace Envoy
Loading