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

[tls] Add a custom listener handshaker for TLS. #12075

Closed
wants to merge 42 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
647e23c
[tls] Add a custom listener handshaker for TLS.
ambuc Jul 14, 2020
a497d44
[misc] run fix_format
ambuc Jul 14, 2020
df9dfb2
Merge branch 'master' of github.com:envoyproxy/envoy into custom-hand…
ambuc Jul 15, 2020
22f2003
[misc] run fix_format
ambuc Jul 15, 2020
a403972
[ssl] Rename some HandshakerCallbacks methods.
ambuc Jul 20, 2020
59d5905
[tls] Rename HandOff/HandBack.
ambuc Jul 20, 2020
2168d8b
[misc] run fix_format
ambuc Jul 20, 2020
44b31b8
[misc] remove comment formatting on PemPasswordCallback, breaking che…
ambuc Jul 20, 2020
78656cc
[misc] fix typo in handshaker_test
ambuc Jul 20, 2020
2240f09
[tls] omit rwflag argument entirely
ambuc Jul 20, 2020
e6de2a1
[ssl] Remove unnecessary ctor and max_proto_version
ambuc Jul 20, 2020
bff00b6
[ssl] initialize() takes a reference
ambuc Jul 20, 2020
1c2a650
Handshakers now raise Connected as part of OnSuccessCb.
ambuc Jul 20, 2020
aaae5bf
[tls] HandshakerFactory is of category 'envoy.tls_handshakers'.
ambuc Jul 20, 2020
1266425
[tls] More specific documentation on DoHandshake.
ambuc Jul 20, 2020
d9db724
[tls] Factory should hold the config message, not context config
ambuc Jul 21, 2020
48a840e
[tls] Introduce a default HandshakerFactoryImpl
ambuc Jul 21, 2020
32c5095
[tls] requireCertificates as a method on the factory, not the handsha…
ambuc Jul 21, 2020
3b94f43
[tls] Add test for HandshakerWithOutOfProcessComponent
ambuc Jul 21, 2020
08e4183
[tls] Clarify guidance on doHandshake() handling nullptrs
ambuc Jul 21, 2020
0dc6bde
Merge branch 'master' of github.com:envoyproxy/envoy into custom-hand…
ambuc Jul 21, 2020
480e433
[misc] Run fix_format.
ambuc Jul 21, 2020
61b7d5c
[tls] Move SSL into Handshaker and remove extra HandshakerCallbacks m…
ambuc Jul 22, 2020
508ea7a
Merge branch 'master' of github.com:envoyproxy/envoy into custom-hand…
ambuc Jul 22, 2020
2e21ee8
[tls] Remove ::initialize() method
ambuc Jul 22, 2020
51ace82
[tls] Add test with example HandshakerImpl demonstrating special case…
ambuc Jul 23, 2020
5dbb557
Merge branch 'master' of github.com:envoyproxy/envoy into custom-hand…
ambuc Jul 23, 2020
4e3af18
[tls] handshaker_factory_ must be a reseatable reference
ambuc Jul 24, 2020
78e3f2f
[tls] handshaker_test.cc calls SSL_set_cert_cb in ctor
ambuc Jul 24, 2020
e4b22fd
[api] Remove typed_config stutter
ambuc Jul 24, 2020
6725f9c
Resolved merge conflicts
ambuc Jul 27, 2020
fad305b
[tls] Refactor HandshakerFactory with callback to allow for early val…
ambuc Jul 28, 2020
36c4d83
[tls] More clarity around Handshaker interface documentation.
ambuc Jul 28, 2020
7c3020d
[misc] Add bssl, rbio, wbio to dictionary.
ambuc Jul 28, 2020
cb39b1a
[misc] Run fix_format.
ambuc Jul 28, 2020
ed5f209
[tls] Clean up initialization order inside context_config_impl
ambuc Jul 29, 2020
2c7d3c8
[tls] Rename methods to onSuccessCb/onFailureCb
ambuc Jul 29, 2020
2d5ec2f
[tls] dedup HandshakerMaker with HandshakerFactoryCb
ambuc Jul 29, 2020
f32acbc
[misc] Remove wbio, rbio, bssl from dictionary
ambuc Jul 29, 2020
18dc2c6
[tls] HandshakerPtr is now a shared_ptr; SslSocketInfo retains access…
ambuc Jul 29, 2020
c7c40b5
Move HandshakerCallbacks into setCallbacks()
ambuc Jul 29, 2020
f711e26
[misc] Rename HandshakerPtr to HandshakerSharedPtr
ambuc Jul 29, 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
3 changes: 3 additions & 0 deletions api/envoy/extensions/transport_sockets/tls/v3/tls.proto
Original file line number Diff line number Diff line change
Expand Up @@ -238,4 +238,7 @@ message CommonTlsContext {
//
// There is no default for this parameter. If empty, Envoy will not expose ALPN.
repeated string alpn_protocols = 4;

// Custom TLS handshaker. If empty, defaults to native TLS handshaker.
config.core.v3.TypedExtensionConfig custom_listener_handshaker = 13;
}
3 changes: 3 additions & 0 deletions api/envoy/extensions/transport_sockets/tls/v4alpha/tls.proto

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

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

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

19 changes: 19 additions & 0 deletions include/envoy/ssl/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ envoy_cc_library(
hdrs = ["context_config.h"],
deps = [
":certificate_validation_context_config_interface",
":handshaker_interface",
":tls_certificate_config_interface",
],
)
Expand Down Expand Up @@ -68,3 +69,21 @@ envoy_cc_library(
deps = [
],
)

envoy_cc_library(
name = "handshaker_interface",
hdrs = ["handshaker.h"],
external_deps = ["ssl"],
deps = [
":socket_state",
"//include/envoy/config:typed_config_interface",
"//include/envoy/network:transport_socket_interface",
"//include/envoy/protobuf:message_validator_interface",
],
)

envoy_cc_library(
name = "socket_state",
hdrs = ["socket_state.h"],
deps = [],
)
12 changes: 12 additions & 0 deletions include/envoy/ssl/context_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "envoy/common/pure.h"
#include "envoy/ssl/certificate_validation_context_config.h"
#include "envoy/ssl/handshaker.h"
#include "envoy/ssl/tls_certificate_config.h"

#include "absl/types/optional.h"
Expand Down Expand Up @@ -73,6 +74,17 @@ class ContextConfig {
* @param callback callback that is executed by context config.
*/
virtual void setSecretUpdateCallback(std::function<void()> callback) PURE;

/**
* @return the handshaker to use for TLS handshakes.
*/
virtual Ssl::HandshakerSharedPtr createHandshaker(bssl::UniquePtr<SSL> ssl) const PURE;

/**
* @return whether or not this context requires certificates for TLS
* handshakes.
*/
virtual bool requireCertificates() const PURE;
};

class ClientContextConfig : public virtual ContextConfig {
Expand Down
101 changes: 101 additions & 0 deletions include/envoy/ssl/handshaker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#pragma once

#include "envoy/api/api.h"
#include "envoy/common/pure.h"
#include "envoy/config/typed_config.h"
#include "envoy/network/transport_socket.h"
#include "envoy/protobuf/message_validator.h"
#include "envoy/ssl/socket_state.h"

#include "openssl/ssl.h"

namespace Envoy {
namespace Ssl {

class HandshakerCallbacks {
public:
virtual ~HandshakerCallbacks() = default;

/**
* Called when a handshake is successfully performed.
*/
virtual void onSuccessCb(SSL* ssl) PURE;
/**
* Called when a handshake fails.
*/
virtual void onFailureCb() PURE;
};

/*
* Interface for a Handshaker which is responsible for owning the
* `bssl::UniquePtr<SSL>` and performing handshakes.
*/
class Handshaker {
public:
virtual ~Handshaker() = default;

/**
* Do the handshake.
*
* NB: |state| is a mutable reference.
*/
virtual Network::PostIoAction doHandshake(SocketState& state) PURE;

/**
* Set internal pointers to Network::TransportSocketCallbacks and
* Ssl::HandshakerCallbacks.
* Depending on impl, these callbacks can be invoked to access connection
* state, raise connection events, etc.
*/
virtual void setCallbacks(Network::TransportSocketCallbacks& callbacks,
Ssl::HandshakerCallbacks& handshaker_callbacks) PURE;

/*
* Access the held SSL object as a ptr. Callsites should handle nullptr
* gracefully.
*/
virtual SSL* ssl() PURE;
};

using HandshakerSharedPtr = std::shared_ptr<Handshaker>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems like we should statically know the lifetime of this, and should be able to use a unique_ptr + references.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the alternative here? @lizan pointed out (#12075 (comment)) that the SslSocketInfo struct expects to have access to the SSL object after the connection has been destroyed, for logging purposes. What do you think of explicitly std::move()ing the SSL object from the handshaker to the SslSocketInfo just before ~SslSocket(), perhaps during SslSocket::shutdown()? @antoniovicente

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might make sense for Handshaker to implement Ssl::ConnectionInfo, as the socket info class is basically represent the result of handshake.

Copy link
Contributor

@antoniovicente antoniovicente Aug 3, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handshaker providing Ssl::ConnectionInfo makes sense.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PTAL @ #12571, I am attempting to move the handshaker behavior into SslSocketInfo before adding an extension point.


class HandshakerFactoryContext {
public:
virtual ~HandshakerFactoryContext() = default;

/**
* @return reference to the Api object
*/
virtual Api::Api& api() PURE;

/**
* The list of supported protocols exposed via ALPN, from ContextConfig.
*/
virtual absl::string_view alpnProtocols() const PURE;
};

using HandshakerFactoryCb = std::function<HandshakerSharedPtr(bssl::UniquePtr<SSL>)>;

class HandshakerFactory : public Config::TypedFactory {
public:
/**
* @returns a callback (of type HandshakerFactoryCb). Accepts the |config| and
* |validation_visitor| for early config validation. This virtual base doesn't
* perform MessageUtil::downcastAndValidate, but an implementation should.
*/
virtual HandshakerFactoryCb
createHandshakerCb(const Protobuf::Message& message,
HandshakerFactoryContext& handshaker_factory_context,
ProtobufMessage::ValidationVisitor& validation_visitor) PURE;

std::string category() const override { return "envoy.tls_handshakers"; }

/**
* Implementations should return true if the tls context accompanying this
* handshaker expects certificates.
*/
virtual bool requireCertificates() const PURE;
};

} // namespace Ssl
} // namespace Envoy
9 changes: 9 additions & 0 deletions include/envoy/ssl/socket_state.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#pragma once

namespace Envoy {
namespace Ssl {

enum class SocketState { PreHandshake, HandshakeInProgress, HandshakeComplete, ShutdownSent };

} // namespace Ssl
} // namespace Envoy
33 changes: 33 additions & 0 deletions source/extensions/transport_sockets/tls/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,12 @@ envoy_cc_library(
deps = [
":context_config_lib",
":context_lib",
":handshaker_lib",
":utility_lib",
"//include/envoy/network:connection_interface",
"//include/envoy/network:transport_socket_interface",
"//include/envoy/ssl:handshaker_interface",
"//include/envoy/ssl:socket_state",
"//include/envoy/ssl:ssl_socket_extended_info_interface",
"//include/envoy/ssl/private_key:private_key_callbacks_interface",
"//include/envoy/ssl/private_key:private_key_interface",
Expand All @@ -63,6 +66,7 @@ envoy_cc_library(
"ssl",
],
deps = [
":handshaker_lib",
"//include/envoy/secret:secret_callbacks_interface",
"//include/envoy/secret:secret_provider_interface",
"//include/envoy/server:transport_socket_config_interface",
Expand Down Expand Up @@ -118,6 +122,35 @@ envoy_cc_library(
],
)

envoy_cc_library(
name = "handshaker_lib",
srcs = ["handshaker_impl.cc"],
hdrs = ["handshaker_impl.h"],
external_deps = [
"ssl",
],
deps = [
"//include/envoy/ssl:context_config_interface",
"//include/envoy/ssl:context_interface",
"//include/envoy/ssl:context_manager_interface",
"//include/envoy/ssl:handshaker_interface",
"//include/envoy/ssl:socket_state",
"//include/envoy/ssl:ssl_socket_extended_info_interface",
"//include/envoy/ssl/private_key:private_key_interface",
"//include/envoy/stats:stats_interface",
"//include/envoy/stats:stats_macros",
"//source/common/common:assert_lib",
"//source/common/common:base64_lib",
"//source/common/common:hex_lib",
"//source/common/common:utility_lib",
"//source/common/network:address_lib",
"//source/common/protobuf:utility_lib",
"//source/common/stats:symbol_table_lib",
"//source/common/stats:utility_lib",
"//source/extensions/transport_sockets/tls/private_key:private_key_manager_lib",
],
)

envoy_cc_library(
name = "utility_lib",
srcs = ["utility.cc"],
Expand Down
27 changes: 26 additions & 1 deletion source/extensions/transport_sockets/tls/context_config_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,25 @@ ContextConfigImpl::ContextConfigImpl(
}
}
}

HandshakerFactoryContextImpl handshaker_factory_context(api_, alpnProtocols());
Ssl::HandshakerFactory* handshaker_factory;
if (config.has_custom_listener_handshaker()) {
// If a custom handshaker is configured, derive the factory from the config.
const auto& handshaker_config = config.custom_listener_handshaker();
handshaker_factory =
&Config::Utility::getAndCheckFactory<Ssl::HandshakerFactory>(handshaker_config);
handshaker_factory_cb_ = handshaker_factory->createHandshakerCb(
handshaker_config.typed_config(), handshaker_factory_context,
factory_context.messageValidationVisitor());
} else {
// Otherwise, derive the config from the (default) factory).
handshaker_factory = HandshakerFactoryImpl::getDefaultHandshakerFactory();
handshaker_factory_cb_ = handshaker_factory->createHandshakerCb(
*handshaker_factory->createEmptyConfigProto(), handshaker_factory_context,
factory_context.messageValidationVisitor());
}
require_certificates_ = handshaker_factory->requireCertificates();
}

Ssl::CertificateValidationContextConfigPtr ContextConfigImpl::getCombinedValidationContextConfig(
Expand All @@ -224,6 +243,10 @@ Ssl::CertificateValidationContextConfigPtr ContextConfigImpl::getCombinedValidat
return std::make_unique<Envoy::Ssl::CertificateValidationContextConfigImpl>(combined_cvc, api_);
}

Ssl::HandshakerSharedPtr ContextConfigImpl::createHandshaker(bssl::UniquePtr<SSL> ssl) const {
return handshaker_factory_cb_(std::move(ssl));
}

void ContextConfigImpl::setSecretUpdateCallback(std::function<void()> callback) {
if (!tls_certificate_providers_.empty()) {
if (tc_update_callback_handle_) {
Expand Down Expand Up @@ -404,7 +427,9 @@ ServerContextConfigImpl::ServerContextConfigImpl(

if ((config.common_tls_context().tls_certificates().size() +
config.common_tls_context().tls_certificate_sds_secret_configs().size()) == 0) {
throw EnvoyException("No TLS certificates found for server context");
if (requireCertificates()) {
throw EnvoyException("No TLS certificates found for server context");
}
} else if (!config.common_tls_context().tls_certificates().empty() &&
!config.common_tls_context().tls_certificate_sds_secret_configs().empty()) {
throw EnvoyException("SDS and non-SDS TLS certificates may not be mixed in server contexts");
Expand Down
9 changes: 9 additions & 0 deletions source/extensions/transport_sockets/tls/context_config_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include "common/json/json_loader.h"
#include "common/ssl/tls_certificate_config_impl.h"

#include "extensions/transport_sockets/tls/handshaker_impl.h"

namespace Envoy {
namespace Extensions {
namespace TransportSockets {
Expand Down Expand Up @@ -60,6 +62,10 @@ class ContextConfigImpl : public virtual Ssl::ContextConfig {
const envoy::extensions::transport_sockets::tls::v3::CertificateValidationContext&
dynamic_cvc);

Ssl::HandshakerSharedPtr createHandshaker(bssl::UniquePtr<SSL> ssl) const override;

bool requireCertificates() const override { return require_certificates_; }

protected:
ContextConfigImpl(const envoy::extensions::transport_sockets::tls::v3::CommonTlsContext& config,
const unsigned default_min_protocol_version,
Expand Down Expand Up @@ -94,6 +100,9 @@ class ContextConfigImpl : public virtual Ssl::ContextConfig {
Envoy::Common::CallbackHandle* cvc_validation_callback_handle_{};
const unsigned min_protocol_version_;
const unsigned max_protocol_version_;

Ssl::HandshakerFactoryCb handshaker_factory_cb_;
bool require_certificates_;
};

class ClientContextConfigImpl : public ContextConfigImpl, public Envoy::Ssl::ClientContextConfig {
Expand Down
Loading