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: support BoringSSL private key async functionality #6326

Merged
merged 63 commits into from
Aug 22, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
3ab2e1d
tls: support BoringSSL private key async functionality.
ipuustin Mar 13, 2019
84b6fe1
tls: add BoringSSL private key operations provider manager support.
ipuustin Mar 27, 2019
127bff5
tests: add mock privateKeyOperationsManager() method.
ipuustin Mar 27, 2019
e58ddfd
tls: use typed config for private key provider configuration.
ipuustin Mar 28, 2019
8d91d59
tls: fixed handling of getPrivateKeyMethods() failure.
ipuustin Apr 3, 2019
0bd6f25
tls: change BoringSSL private key support to be certificate-based.
ipuustin Apr 15, 2019
5e01b30
tls: remove associateWithSsl(), fix tests.
ipuustin Apr 26, 2019
8717cb2
tls: rename the private key API to be more in line with BoringSSL.
ipuustin Apr 26, 2019
7f0e0bc
tls: make a failed private key method association fatal.
ipuustin Apr 30, 2019
4855c9e
tls: typo fix.
ipuustin May 6, 2019
b25f6a6
test: start testing private key methods (context and ssl_socket).
ipuustin May 6, 2019
b7f4fa3
dictionary: added words.
ipuustin May 9, 2019
f67c1a1
test: add a missing "override".
ipuustin May 10, 2019
b24ceb2
Merge remote-tracking branch 'origin/master' into private-key-pr-1.3-…
ipuustin May 13, 2019
ce312ec
tls: close connection if async handshake fails.
ipuustin May 14, 2019
3eacd75
tests: add more tests for private key method error cases.
ipuustin May 14, 2019
81afc58
tls: remove status from private key method complete() callback.
ipuustin May 15, 2019
ccc8fbe
tests: update tests to the new API + cleanups.
ipuustin May 15, 2019
3e8a3c3
tls: add a check for having both private_key and private_key_method.
ipuustin May 15, 2019
59d93f6
tests: test having both private_key and private_key_method in certifi…
ipuustin May 15, 2019
eb1feaa
cert.proto: fix numbering to be contiguous.
ipuustin May 15, 2019
f7ccf0b
test: fail if provider doesn't return private key methods.
ipuustin May 27, 2019
dace6bb
test: rewrote rsa private key method logic.
ipuustin May 27, 2019
1c5630d
tests: fix a typo.
ipuustin May 27, 2019
c92dfd6
tests: private key method multi-cert tests.
ipuustin May 27, 2019
86ecba9
dispatcher: add a function for returning current thread id.
ipuustin May 28, 2019
3cbe3a5
ssl_socket: check the private key method callback thread id.
ipuustin May 28, 2019
976a469
tests: unique pointers, vectors, and cleanups.
ipuustin Jun 3, 2019
789368a
tls: cleanups.
ipuustin Jun 3, 2019
50d6450
tls: complete() -> onPrivateKeyMethodComplete()
ipuustin Jun 4, 2019
9146b40
tests: FIPS check, private key read from disk only once.
ipuustin Jun 4, 2019
8a688f6
tls: fix typos.
ipuustin Jun 4, 2019
455be73
tests: checkFips() implementation.
ipuustin Jun 5, 2019
14b6d33
tls: remove one of three TlsCertificateConfig constructors.
ipuustin Jun 5, 2019
cc54803
tests: remove use of old TlsCertificateConfigImpl constructor.
ipuustin Jun 5, 2019
12680f6
tls: remove PrivateKeyMethodConnection class.
ipuustin Jun 5, 2019
16ca2a1
tests: use higher-level RSA signing functions.
ipuustin Jun 5, 2019
6d5cf6f
tests: do not use locking in test providers.
ipuustin Jun 5, 2019
e3a66fe
docs: add an overview and some API documentation.
ipuustin Jun 6, 2019
6ac9b30
dictionary: add "TPM".
ipuustin Jun 6, 2019
caee5ad
Merge remote-tracking branch 'origin/master' into private-key-pr-1.3-…
ipuustin Jun 6, 2019
1f1c997
Merge remote-tracking branch 'origin/master' into private-key-pr-1.3-…
ipuustin Jun 10, 2019
2ef535f
tls: style cleanups.
ipuustin Jun 10, 2019
c4a5836
Merge remote-tracking branch 'origin/master' into private-key-pr-1.3-…
ipuustin Jun 19, 2019
2498b0f
tls: reduce TlsCertificateConfigImpl constructors to one.
ipuustin Jun 18, 2019
cd28e08
tls: simplify private key method callback. Add some assumptions to th…
ipuustin Jun 18, 2019
d7e9e5a
Add transport_sockets/tls/private_key to unowned directories.
ipuustin Jun 19, 2019
9b31b1a
CODEOWNERS: add @PiotrSikora and @lizan to transport_sockets/tls.
ipuustin Jun 20, 2019
c9ec2bc
tests: merged RSA and ECDSA providers.
ipuustin Jun 24, 2019
9a321eb
tls: change from PrivateKeyMethod to PrivateKeyProvider.
ipuustin Jun 25, 2019
7ec959e
tests: change how the crypto error is done in private key method test.
ipuustin Jun 25, 2019
f35cca5
test: fix typo in comment.
ipuustin Jun 27, 2019
0a51246
tests: improved an error message.
ipuustin Jun 27, 2019
808d046
Merge remote-tracking branch 'origin/master' into private-key-pr-1.3-…
ipuustin Jul 15, 2019
3f57152
tls: change "typedef" to "using".
ipuustin Jul 15, 2019
ed99c59
test: remove redundant return.
ipuustin Jul 15, 2019
4d1ceca
Merge remote-tracking branch 'origin/master' into private-key-pr-1.3-…
ipuustin Jul 23, 2019
e4cab89
tests: annotated destructors with override.
ipuustin Jul 23, 2019
5db871c
tls: gather bools into an enum.
ipuustin Aug 12, 2019
e0e4ed7
Merge remote-tracking branch 'origin/master' into private-key-pr-1.3-…
ipuustin Aug 12, 2019
d28d44b
tests: update one new TlsCertificateConfigImpl constructor.
ipuustin Aug 12, 2019
b79e532
tls: modernize a for loop.
ipuustin Aug 12, 2019
abf8758
tls: various variable name and comment cleanups.
ipuustin Aug 22, 2019
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
2 changes: 2 additions & 0 deletions CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ extensions/filters/common/original_src @snowp @klarose
/*/extensions/filters/http/header_to_metadata @rgs1 @zuercher
# alts transport socket extension
/*/extensions/transport_sockets/alts @htuch @yangminzhu
# tls transport socket extension
/*/extensions/transport_sockets/tls @PiotrSikora @lizan
# sni_cluster extension
/*/extensions/filters/network/sni_cluster @rshriram @lizan
# tracers.datadog extension
Expand Down
27 changes: 27 additions & 0 deletions api/envoy/api/v2/auth/cert.proto
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ option go_package = "auth";
import "envoy/api/v2/core/base.proto";
import "envoy/api/v2/core/config_source.proto";

import "google/protobuf/any.proto";
import "google/protobuf/struct.proto";
import "google/protobuf/wrappers.proto";

import "validate/validate.proto";
Expand Down Expand Up @@ -102,13 +104,38 @@ message TlsParameters {
repeated string ecdh_curves = 4;
}

// BoringSSL private key method configuration. The private key methods are used for external
// (potentially asynchronous) signing and decryption operations. Some use cases for private key
// methods would be TPM support and TLS acceleration.
message PrivateKeyProvider {
// Private key method provider name. The name must match a
// supported private key method provider type.
string provider_name = 1 [(validate.rules).string.min_bytes = 1];

// Private key method provider specific configuration.
oneof config_type {
google.protobuf.Struct config = 2;

google.protobuf.Any typed_config = 3;
}
}

message TlsCertificate {
// The TLS certificate chain.
core.DataSource certificate_chain = 1;

// The TLS private key.
core.DataSource private_key = 2;

// BoringSSL private key method provider. This is an alternative to :ref:`private_key
// <envoy_api_field_auth.TlsCertificate.private_key>` field. This can't be
// marked as ``oneof`` due to API compatibility reasons. Setting both :ref:`private_key
// <envoy_api_field_auth.TlsCertificate.private_key>` and
// :ref:`private_key_provider
// <envoy_api_field_auth.TlsCertificate.private_key_provider>` fields will result in an
// error.
PrivateKeyProvider private_key_provider = 6;

// The password to decrypt the TLS private key. If this field is not set, it is assumed that the
// TLS private key is not password encrypted.
core.DataSource password = 3;
Expand Down
1 change: 1 addition & 0 deletions docs/root/extending/extending.rst
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ types including:
* :ref:`Stat sinks <arch_overview_statistics>`
* :ref:`Tracers <arch_overview_tracing>`
* Transport sockets
* BoringSSL private key methods

As of this writing there is no high level extension developer documentation. The
:repo:`existing extensions <source/extensions>` are a good way to learn what is possible.
Expand Down
4 changes: 4 additions & 0 deletions docs/root/intro/arch_overview/security/ssl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ requirements (TLS1.2, SNI, etc.). Envoy supports the following TLS features:
tickets (see `RFC 5077 <https://www.ietf.org/rfc/rfc5077.txt>`_). Resumption can be performed
across hot restarts and between parallel Envoy instances (typically useful in a front proxy
configuration).
* **BoringSSL private key methods**: TLS private key operations (signing and decrypting) can be
performed asynchronously from an extension. This allows extending Envoy to support various key
management schemes (such as TPM) and TLS acceleration. This mechanism uses
`BoringSSL private key method interface <https://github.com/google/boringssl/blob/c0b4c72b6d4c6f4828a373ec454bd646390017d4/include/openssl/ssl.h#L1169>`_.

Underlying implementation
-------------------------
Expand Down
3 changes: 3 additions & 0 deletions include/envoy/ssl/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ envoy_cc_library(
envoy_cc_library(
name = "tls_certificate_config_interface",
hdrs = ["tls_certificate_config.h"],
deps = [
"//include/envoy/ssl/private_key:private_key_interface",
],
)

envoy_cc_library(
Expand Down
7 changes: 7 additions & 0 deletions include/envoy/ssl/context_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "envoy/common/time.h"
#include "envoy/ssl/context.h"
#include "envoy/ssl/context_config.h"
#include "envoy/ssl/private_key/private_key.h"
#include "envoy/stats/scope.h"

namespace Envoy {
Expand Down Expand Up @@ -39,6 +40,12 @@ class ContextManager {
* Iterate through all currently allocated contexts.
*/
virtual void iterateContexts(std::function<void(const Context&)> callback) PURE;

/**
* Access the private key operations manager, which is part of SSL
htuch marked this conversation as resolved.
Show resolved Hide resolved
* context manager.
*/
virtual PrivateKeyMethodManager& privateKeyMethodManager() PURE;
};

using ContextManagerPtr = std::unique_ptr<ContextManager>;
Expand Down
35 changes: 35 additions & 0 deletions include/envoy/ssl/private_key/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 = "private_key_interface",
hdrs = ["private_key.h"],
external_deps = ["ssl"],
deps = [
":private_key_callbacks_interface",
"//include/envoy/event:dispatcher_interface",
"@envoy_api//envoy/api/v2/auth:cert_cc",
],
)

envoy_cc_library(
name = "private_key_config_interface",
hdrs = ["private_key_config.h"],
deps = [
":private_key_interface",
"//include/envoy/registry",
],
)

envoy_cc_library(
name = "private_key_callbacks_interface",
hdrs = ["private_key_callbacks.h"],
external_deps = ["ssl"],
)
85 changes: 85 additions & 0 deletions include/envoy/ssl/private_key/private_key.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
#pragma once

#include <functional>
#include <string>

#include "envoy/api/v2/auth/cert.pb.h"
#include "envoy/common/pure.h"
#include "envoy/event/dispatcher.h"
#include "envoy/ssl/private_key/private_key_callbacks.h"

#include "openssl/ssl.h"

namespace Envoy {
namespace Server {
namespace Configuration {
// Prevent a dependency loop with the forward declaration.
class TransportSocketFactoryContext;
Copy link
Member

Choose a reason for hiding this comment

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

Is this to break an include dependency loop?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, that's why this is needed -- Bazel will complain if this is done with a real dependency.

Copy link
Member

Choose a reason for hiding this comment

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

Arguably we could do some header refactoring to fix this, but I think this is OK if this is a one off.

Copy link
Member Author

Choose a reason for hiding this comment

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

Ok. Just for the record, the dep loop would be this:

    //include/envoy/ssl:context_manager_interface
    //include/envoy/ssl:context_config_interface
.-> //include/envoy/ssl:tls_certificate_config_interface
|   //include/envoy/ssl/private_key:private_key_interface
|   //include/envoy/server:transport_socket_config_interface
|   //include/envoy/secret:secret_manager_interface
|   //include/envoy/secret:secret_provider_interface
`-- //include/envoy/ssl:tls_certificate_config_interface

} // namespace Configuration
} // namespace Server

namespace Ssl {

using BoringSslPrivateKeyMethodSharedPtr = std::shared_ptr<SSL_PRIVATE_KEY_METHOD>;

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

/**
* Register an SSL connection to private key operations by the provider.
* @param ssl a SSL connection object.
* @param cb a callbacks object, whose "complete" method will be invoked
* when the asynchronous processing is complete.
* @param dispatcher supplies the owning thread's dispatcher.
*/
virtual void registerPrivateKeyMethod(SSL* ssl, PrivateKeyConnectionCallbacks& cb,
Event::Dispatcher& dispatcher) PURE;

/**
* Unregister an SSL connection from private key operations by the provider.
* @param ssl a SSL connection object.
* @throw EnvoyException if registration fails.
*/
virtual void unregisterPrivateKeyMethod(SSL* ssl) PURE;

/**
* Check whether the private key method satisfies FIPS requirements.
* @return true if FIPS key requirements are satisfied, false if not.
*/
virtual bool checkFips() PURE;

/**
* Get the private key methods from the provider.
* @return the private key methods associated with this provider and
* configuration.
*/
virtual BoringSslPrivateKeyMethodSharedPtr getBoringSslPrivateKeyMethod() PURE;
};

using PrivateKeyMethodProviderSharedPtr = std::shared_ptr<PrivateKeyMethodProvider>;

/**
* A manager for finding correct user-provided functions for handling BoringSSL private key
* operations.
*/
class PrivateKeyMethodManager {
public:
virtual ~PrivateKeyMethodManager() = default;

/**
* Finds and returns a private key operations provider for BoringSSL.
*
* @param config a protobuf message object containing a PrivateKeyProvider message.
* @param factory_context context that provides components for creating and
* initializing connections using asynchronous private key operations.
* @return PrivateKeyMethodProvider the private key operations provider, or nullptr if
* no provider can be used with the context configuration.
*/
virtual PrivateKeyMethodProviderSharedPtr createPrivateKeyMethodProvider(
const envoy::api::v2::auth::PrivateKeyProvider& config,
Envoy::Server::Configuration::TransportSocketFactoryContext& factory_context) PURE;
};

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

#include <functional>
#include <string>

#include "envoy/common/pure.h"

namespace Envoy {
namespace Ssl {

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

/**
* Callback function which is called when the asynchronous private key
* operation has been completed (with either success or failure). The
* provider will communicate the success status when SSL_do_handshake()
* is called the next time.
*/
virtual void onPrivateKeyMethodComplete() PURE;
};

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

#include "envoy/api/v2/auth/cert.pb.h"
#include "envoy/registry/registry.h"
#include "envoy/ssl/private_key/private_key.h"

namespace Envoy {
namespace Ssl {

// Base class which the private key operation provider implementations can register.

class PrivateKeyMethodProviderInstanceFactory {
public:
virtual ~PrivateKeyMethodProviderInstanceFactory() = default;
virtual PrivateKeyMethodProviderSharedPtr createPrivateKeyMethodProviderInstance(
const envoy::api::v2::auth::PrivateKeyProvider& config,
Server::Configuration::TransportSocketFactoryContext& factory_context) PURE;
virtual std::string name() const PURE;
};

} // namespace Ssl
} // namespace Envoy
6 changes: 6 additions & 0 deletions include/envoy/ssl/tls_certificate_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <string>

#include "envoy/common/pure.h"
#include "envoy/ssl/private_key/private_key.h"

namespace Envoy {
namespace Ssl {
Expand Down Expand Up @@ -34,6 +35,11 @@ class TlsCertificateConfig {
*/
virtual const std::string& privateKeyPath() const PURE;

/**
* @return private key method provider.
*/
virtual Envoy::Ssl::PrivateKeyMethodProviderSharedPtr privateKeyMethod() const PURE;

/**
* @return a string of password.
*/
Expand Down
2 changes: 2 additions & 0 deletions source/common/ssl/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ envoy_cc_library(
srcs = ["tls_certificate_config_impl.cc"],
hdrs = ["tls_certificate_config_impl.h"],
deps = [
"//include/envoy/server:transport_socket_config_interface",
"//include/envoy/ssl:tls_certificate_config_interface",
"//include/envoy/ssl/private_key:private_key_interface",
"//source/common/common:empty_string",
"//source/common/config:datasource_lib",
"@envoy_api//envoy/api/v2/auth:cert_cc",
Expand Down
19 changes: 15 additions & 4 deletions source/common/ssl/tls_certificate_config_impl.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "common/ssl/tls_certificate_config_impl.h"

#include "envoy/common/exception.h"
#include "envoy/server/transport_socket_config.h"

#include "common/common/empty_string.h"
#include "common/common/fmt.h"
Expand All @@ -12,7 +13,8 @@ namespace Ssl {
static const std::string INLINE_STRING = "<inline>";

TlsCertificateConfigImpl::TlsCertificateConfigImpl(
const envoy::api::v2::auth::TlsCertificate& config, Api::Api& api)
const envoy::api::v2::auth::TlsCertificate& config,
Server::Configuration::TransportSocketFactoryContext* factory_context, Api::Api& api)
: certificate_chain_(Config::DataSource::read(config.certificate_chain(), true, api)),
certificate_chain_path_(
Config::DataSource::getPath(config.certificate_chain())
Expand All @@ -22,9 +24,18 @@ TlsCertificateConfigImpl::TlsCertificateConfigImpl(
.value_or(private_key_.empty() ? EMPTY_STRING : INLINE_STRING)),
password_(Config::DataSource::read(config.password(), true, api)),
password_path_(Config::DataSource::getPath(config.password())
.value_or(password_.empty() ? EMPTY_STRING : INLINE_STRING)) {

if (certificate_chain_.empty() || private_key_.empty()) {
.value_or(password_.empty() ? EMPTY_STRING : INLINE_STRING)),
private_key_method_(
factory_context != nullptr && config.has_private_key_provider()
? factory_context->sslContextManager()
.privateKeyMethodManager()
.createPrivateKeyMethodProvider(config.private_key_provider(), *factory_context)
: nullptr) {
if (config.has_private_key_provider() && config.has_private_key()) {
throw EnvoyException(fmt::format(
"Certificate configuration can't have both private_key and private_key_provider"));
}
if (certificate_chain_.empty() || (private_key_.empty() && private_key_method_ == nullptr)) {
throw EnvoyException(fmt::format("Failed to load incomplete certificate from {}, {}",
certificate_chain_path_, private_key_path_));
}
Expand Down
9 changes: 8 additions & 1 deletion source/common/ssl/tls_certificate_config_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,27 @@

#include "envoy/api/api.h"
#include "envoy/api/v2/auth/cert.pb.h"
#include "envoy/ssl/private_key/private_key.h"
#include "envoy/ssl/tls_certificate_config.h"

namespace Envoy {
namespace Ssl {

class TlsCertificateConfigImpl : public TlsCertificateConfig {
public:
TlsCertificateConfigImpl(const envoy::api::v2::auth::TlsCertificate& config, Api::Api& api);
TlsCertificateConfigImpl(const envoy::api::v2::auth::TlsCertificate& config,
Server::Configuration::TransportSocketFactoryContext* factory_context,
Api::Api& api);

const std::string& certificateChain() const override { return certificate_chain_; }
const std::string& certificateChainPath() const override { return certificate_chain_path_; }
const std::string& privateKey() const override { return private_key_; }
const std::string& privateKeyPath() const override { return private_key_path_; }
const std::string& password() const override { return password_; }
const std::string& passwordPath() const override { return password_path_; }
Envoy::Ssl::PrivateKeyMethodProviderSharedPtr privateKeyMethod() const override {
return private_key_method_;
}

private:
const std::string certificate_chain_;
Expand All @@ -27,6 +33,7 @@ class TlsCertificateConfigImpl : public TlsCertificateConfig {
const std::string private_key_path_;
const std::string password_;
const std::string password_path_;
Envoy::Ssl::PrivateKeyMethodProviderSharedPtr private_key_method_{};
};

} // namespace Ssl
Expand Down
Loading