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

Implement Certificate Provider Framework (#19308) #19582

Closed
Closed
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d4eb3cb
Implement certificate_provider_instances in bootstrap (#19308)
Feb 15, 2022
4db7266
Merge branch 'envoyproxy:main' into certificate-provider-instances
Feb 25, 2022
18464b7
address comments
Feb 22, 2022
732fb63
subscription framework code for certificate provider
Mar 2, 2022
3bcbe0a
Merge branch 'main' into certificate-provider-instances-pr
Mar 2, 2022
45f9f19
move certificate_provider.h into envoy/certificate_provider directory
Mar 4, 2022
3c822c0
update CertificateProvider interface
Mar 7, 2022
c179df3
address comments and update StaticCertificateProvider
Mar 10, 2022
e712dbd
Merge branch 'main' into certificate-provider-instances
Mar 16, 2022
b052975
rename StaticCertificateProvider to DefaultCertificateProvider
Mar 16, 2022
d31b1d7
Merge remote-tracking branch 'upstream/main' into certificate-provide…
Apr 24, 2022
7df7915
update DefaultCertificateProvider and remove CertificateSubscriptionC…
Apr 24, 2022
d87daaa
Merge remote-tracking branch 'upstream/main' into certificate-provide…
Jun 27, 2022
932b282
Merge remote-tracking branch 'upstream/main' into certificate-provide…
Jun 27, 2022
0c4e685
Implemet certificate provider framework
Jun 27, 2022
797ba3e
Merge remote-tracking branch 'upstream/main' into certificate-provide…
Jun 30, 2022
9d0ef89
remove test code
Jun 30, 2022
bc4e2d0
add comments for certificate provider interface
Jul 18, 2022
7bf603a
Merge branch 'main' into certificate-provider-instances
Jul 18, 2022
d0acb6b
fix docs format
Jul 18, 2022
73c06f4
Merge branch 'main' into certificate-provider-instances
Nov 3, 2022
a1c21d2
update cert provider api
Nov 7, 2022
ba78629
Merge branch 'main' into certificate-provider-instances
Nov 7, 2022
6361493
format fix
Nov 7, 2022
c3467b1
add mocks for cert provider
Nov 7, 2022
7f6af44
add more mocks for cert provider
Nov 8, 2022
814d7ec
fix format and fix test failures
Nov 8, 2022
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: 1 addition & 2 deletions api/envoy/config/bootstrap/v3/bootstrap.proto
Original file line number Diff line number Diff line change
Expand Up @@ -315,9 +315,8 @@ message Bootstrap {

// Global map of CertificateProvider instances. These instances are referred to by name in the
// :ref:`CommonTlsContext.CertificateProviderInstance.instance_name
// <envoy_v3_api_field_extensions.transport_sockets.tls.v3.CommonTlsContext.CertificateProviderInstance.instance_name>`
// <envoy_v3_api_field_extensions.transport_sockets.tls.v3.CertificateProviderPluginInstance.instance_name>`
// field.
// [#not-implemented-hide:]
map<string, core.v3.TypedExtensionConfig> certificate_provider_instances = 25;

// Specifies a set of headers that need to be registered as inline header. This configuration
Expand Down
2 changes: 0 additions & 2 deletions api/envoy/extensions/transport_sockets/tls/v3/common.proto
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,6 @@ message TlsSessionTicketKeys {
// The plugin instances are defined in the client's bootstrap file.
// The plugin allows certificates to be fetched/refreshed over the network asynchronously with
// respect to the TLS handshake.
// [#not-implemented-hide:]
LuyaoZhong marked this conversation as resolved.
Show resolved Hide resolved
message CertificateProviderPluginInstance {
// Provider instance name. If not present, defaults to "default".
//
Expand Down Expand Up @@ -335,7 +334,6 @@ message CertificateValidationContext {
// Certificate provider instance for fetching TLS certificates.
//
// Only one of ``trusted_ca`` and ``ca_certificate_provider_instance`` may be specified.
// [#not-implemented-hide:]
CertificateProviderPluginInstance ca_certificate_provider_instance = 13
[(udpa.annotations.field_migrate).oneof_promotion = "ca_cert_source"];

Expand Down
1 change: 0 additions & 1 deletion api/envoy/extensions/transport_sockets/tls/v3/tls.proto
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,6 @@ message CommonTlsContext {
//
// Only one of ``tls_certificates``, ``tls_certificate_sds_secret_configs``,
// and ``tls_certificate_provider_instance`` may be used.
// [#not-implemented-hide:]
CertificateProviderPluginInstance tls_certificate_provider_instance = 14;

// Certificate provider for fetching TLS certificates.
Expand Down
46 changes: 46 additions & 0 deletions envoy/certificate_provider/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_package",
)

licenses(["notice"]) # Apache 2

envoy_package()

envoy_cc_library(
name = "certificate_provider_interface",
hdrs = ["certificate_provider.h"],
external_deps = [
"ssl",
],
deps = [
"//envoy/common:callback",
"@com_google_absl//absl/strings",
"@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto",
],
)

envoy_cc_library(
name = "certificate_provider_manager_interface",
hdrs = [
"certificate_provider_manager.h",
],
deps = [
":certificate_provider_interface",
"@envoy_api//envoy/config/core/v3:pkg_cc_proto",
],
)

envoy_cc_library(
name = "certificate_provider_factory_lib",
hdrs = [
"certificate_provider_factory.h",
],
deps = [
":certificate_provider_interface",
"//envoy/registry",
"@com_google_absl//absl/strings",
"@envoy_api//envoy/config/core/v3:pkg_cc_proto",
],
)
74 changes: 74 additions & 0 deletions envoy/certificate_provider/certificate_provider.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#pragma once

#include <list>

#include "envoy/common/callback.h"
#include "envoy/common/pure.h"
#include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h"
#include "envoy/ssl/connection.h"

#include "absl/strings/string_view.h"
#include "openssl/ssl.h"
#include "openssl/x509v3.h"

namespace Envoy {
namespace CertificateProvider {

class OnDemandUpdateMetadata {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm not sure how to define this metadata and make it can be utilized by other cases easily.
Currently I only have bumping case, in which case I need to generate certs based on a real server cert, so I need to pass the information of real server cert to the cert provider instance. How can I make this interface generic?

Anybody has ideas?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@lizan @markdroth Could you help review this?

Copy link
Member

Choose a reason for hiding this comment

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

Can you elaborate how this callback will be used? It's not very clear from the design doc or this PR.

Copy link
Contributor Author

@LuyaoZhong LuyaoZhong Aug 25, 2022

Choose a reason for hiding this comment

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

addOnDemandUpdateCallback is used to add callback passed from worker thread.

Assuming we have a network filter to request cert upon data plane request, it needs to add a callback to cert provider and pass metadata(e.g., SNI) to provider used for resource update, cert provider will update the cert resources based on the metadata (on main thread, all config updates should be on main thread due to envoy design principle ). Once the update is done, the callback is invoked, it might contains some logic needs to be run on main thread and other logic wrapped in another nested callback needs to be posted back to worker thread and resume the network filter chain.

The callback depends on feature. Above is just an example. But no matter what feature, we need pass metadata from data plane request to cert provider, this is why I was asking how to define this OnDemandUpdateMetadata

Copy link
Member

Choose a reason for hiding this comment

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

The name is still very confusing :) I need a concrete example (or TLS bumping filter) to better understand this. But IIUC we need to think the threading model more carefully. It sounds similar to what DNS cache does in terms of threading model, so you might want to take a look on that.

Copy link
Contributor Author

@LuyaoZhong LuyaoZhong Aug 25, 2022

Choose a reason for hiding this comment

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

Which name are you confused with? Is it OnDemandUpdateMetadata?

Yes, it's similar to DNS cache, the same thing is we post the task to main thread to do DNS resolve or cert update, and callback to worker thread to continue the filter chain. For DNS, it passes the host as a parameter to the function used for DNS resolve, here we need to pass OnDemandUpdateMetadata as a parameter to the function used for cert generation. Since it's not just for bumping, we need it can cover future cases as much as possible. One alternative is listing all common config items as optional fields for cert generating in OnDemandUpdateMetadata, e.g. SANs, pkey type(RSA or ECDSA) etc. What do you think about this?

I think TLS bumping filter will not be ready very soon, before it's ready, let's we think about if there is anything we can do for this PR?

public:
virtual ~OnDemandUpdateMetadata() = default;

virtual Envoy::Ssl::ConnectionInfoConstSharedPtr connectionInfo() const PURE;
};

using OnDemandUpdateMetadataPtr = std::unique_ptr<OnDemandUpdateMetadata>;

class CertificateProvider {
public:
struct Capabilites {
/* whether or not a provider supports generating identity certificates on demand */
bool provide_on_demand_identity_certs = false;
};

virtual ~CertificateProvider() = default;

virtual Capabilites capabilities() const PURE;

/**
* @return CA certificate used for validation
*/
virtual const std::string trustedCA(const std::string& cert_name) const PURE;

/**
* Certificate provider instance which used to get tls certificates
* should provide at least one tls certificate.
* @return Identity certificates used for handshake
*/
virtual std::vector<const envoy::extensions::transport_sockets::tls::v3::TlsCertificate*>
tlsCertificates(const std::string& cert_name) const PURE;

/**
* Add on-demand callback into certificate provider, this function might be invoked from worker
* thread during runtime
*
* @param metadata is passed to provider for certs fetching/refreshing
* @return CallbackHandle the handle which can remove that update callback.
*/
virtual Common::CallbackHandlePtr
addOnDemandUpdateCallback(const std::string& cert_name, OnDemandUpdateMetadataPtr metadata,
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this actually used in this PR? I was looking at how listener/cluster would delay until the callback fires.

Copy link
Contributor Author

@LuyaoZhong LuyaoZhong Aug 30, 2022

Choose a reason for hiding this comment

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

No, this is not used in this PR. We are implementing a local mimic cert provider (code not submitted yet) which will implement this API, and it will work with bumping filter to mimic certs for downstream.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, thanks. I think this particular callback is quite interesting, you can do on-demand SDS, for example. Once it's hooked into upstream/downstream, the failure mode should be more clear (what happens if too many requests are made for certs, or one request times out).

std::function<void()> thread_local_callback) PURE;

/**
* Add certificate update callback into certificate provider for asychronous usage.
*
* @param callback callback that is executed by certificate provider.
* @return CallbackHandle the handle which can remove that update callback.
*/
virtual Common::CallbackHandlePtr addUpdateCallback(const std::string& cert_name,
std::function<void()> callback) PURE;
};

using CertificateProviderSharedPtr = std::shared_ptr<CertificateProvider>;

} // namespace CertificateProvider
} // namespace Envoy
29 changes: 29 additions & 0 deletions envoy/certificate_provider/certificate_provider_factory.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once

#include "envoy/certificate_provider/certificate_provider.h"
#include "envoy/common/pure.h"
#include "envoy/config/core/v3/extension.pb.h"
#include "envoy/registry/registry.h"

#include "absl/strings/string_view.h"

namespace Envoy {
namespace Server {
namespace Configuration {
class TransportSocketFactoryContext;
} // namespace Configuration
} // namespace Server
namespace CertificateProvider {

class CertificateProviderFactory : public Config::TypedFactory {
public:
virtual Envoy::CertificateProvider::CertificateProviderSharedPtr
createCertificateProviderInstance(
const envoy::config::core::v3::TypedExtensionConfig& config,
Server::Configuration::TransportSocketFactoryContext& factory_context, Api::Api& api) PURE;

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

} // namespace CertificateProvider
} // namespace Envoy
35 changes: 35 additions & 0 deletions envoy/certificate_provider/certificate_provider_manager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once

#include <string>

#include "envoy/certificate_provider/certificate_provider.h"
#include "envoy/common/pure.h"
#include "envoy/config/core/v3/extension.pb.h"
#include "envoy/singleton/instance.h"

namespace Envoy {
namespace Server {
namespace Configuration {
class TransportSocketFactoryContext;
} // namespace Configuration
} // namespace Server
namespace CertificateProvider {

/**
* A manager for certificate provider instances.
*/
class CertificateProviderManager : public Singleton::Instance {
public:
~CertificateProviderManager() override = default;

virtual void addCertificateProvider(
absl::string_view name, const envoy::config::core::v3::TypedExtensionConfig& config,
Server::Configuration::TransportSocketFactoryContext& factory_context) PURE;

virtual CertificateProviderSharedPtr getCertificateProvider(absl::string_view name) PURE;
};

using CertificateProviderManagerPtr = std::unique_ptr<CertificateProviderManager>;

} // namespace CertificateProvider
} // namespace Envoy
1 change: 1 addition & 0 deletions envoy/server/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ envoy_cc_library(
":options_interface",
"//envoy/access_log:access_log_interface",
"//envoy/api:api_interface",
"//envoy/certificate_provider:certificate_provider_manager_interface",
"//envoy/common:mutex_tracer",
"//envoy/event:timer_interface",
"//envoy/http:context_interface",
Expand Down
6 changes: 6 additions & 0 deletions envoy/server/instance.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "envoy/access_log/access_log.h"
#include "envoy/api/api.h"
#include "envoy/certificate_provider/certificate_provider_manager.h"
#include "envoy/common/mutex_tracer.h"
#include "envoy/common/random_generator.h"
#include "envoy/config/trace/v3/http_tracer.pb.h"
Expand Down Expand Up @@ -142,6 +143,11 @@ class Instance {
*/
virtual Secret::SecretManager& secretManager() PURE;

/**
* @return the server's certificate provider manager
*/
virtual CertificateProvider::CertificateProviderManager& certificateProviderManager() PURE;

/**
* @return the server's CLI options.
*/
Expand Down
6 changes: 6 additions & 0 deletions envoy/server/transport_socket_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <string>

#include "envoy/certificate_provider/certificate_provider_manager.h"
#include "envoy/config/core/v3/health_check.pb.h"
#include "envoy/config/typed_config.h"
#include "envoy/event/dispatcher.h"
Expand Down Expand Up @@ -104,6 +105,11 @@ class TransportSocketFactoryContext {
* @return reference to the access log manager object
*/
virtual AccessLog::AccessLogManager& accessLogManager() PURE;

/**
* @return the instance of certificate provider manager.
*/
virtual CertificateProvider::CertificateProviderManager& certificateProviderManager() PURE;
};

using TransportSocketFactoryContextPtr = std::unique_ptr<TransportSocketFactoryContext>;
Expand Down
1 change: 1 addition & 0 deletions envoy/ssl/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ envoy_cc_library(
hdrs = ["certificate_validation_context_config.h"],
external_deps = ["abseil_optional"],
deps = [
"//envoy/certificate_provider:certificate_provider_interface",
"@envoy_api//envoy/extensions/transport_sockets/tls/v3:pkg_cc_proto",
"@envoy_api//envoy/type/matcher/v3:pkg_cc_proto",
],
Expand Down
13 changes: 13 additions & 0 deletions envoy/ssl/certificate_validation_context_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <vector>

#include "envoy/api/api.h"
#include "envoy/certificate_provider/certificate_provider.h"
#include "envoy/common/pure.h"
#include "envoy/extensions/transport_sockets/tls/v3/cert.pb.h"
#include "envoy/extensions/transport_sockets/tls/v3/common.pb.h"
Expand Down Expand Up @@ -94,6 +95,18 @@ class CertificateValidationContextConfig {
* @return the max depth used when verifying the certificate-chain
*/
virtual absl::optional<uint32_t> maxVerifyDepth() const PURE;

/**
* @return the CA provider which can provide CA certificate to use for peer validation
*/
virtual Envoy::CertificateProvider::CertificateProviderSharedPtr caProvider() const PURE;

/**
* Add ca callback into validation context config. When trusted ca is refreshed from
* ca provider, this callback is invoked to update config.
* @param callback callback that is executed by validation context config.
*/
virtual void setCAUpdateCallback(std::function<void()> callback) PURE;
};

using CertificateValidationContextConfigPtr = std::unique_ptr<CertificateValidationContextConfig>;
Expand Down
5 changes: 5 additions & 0 deletions envoy/ssl/context_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,11 @@ class ContextConfig {
* @return the access log manager object reference
*/
virtual AccessLog::AccessLogManager& accessLogManager() const PURE;

/**
* @return the set of capabilities for Certificate Provider instance specified in this context
*/
virtual CertificateProvider::CertificateProvider::Capabilites certProviderCaps() const PURE;
};

class ClientContextConfig : public virtual ContextConfig {
Expand Down
1 change: 1 addition & 0 deletions envoy/upstream/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ envoy_cc_library(
":thread_local_cluster_interface",
":upstream_interface",
"//envoy/access_log:access_log_interface",
"//envoy/certificate_provider:certificate_provider_manager_interface",
"//envoy/common:random_generator_interface",
"//envoy/config:grpc_mux_interface",
"//envoy/config:subscription_factory_interface",
Expand Down
6 changes: 6 additions & 0 deletions envoy/upstream/cluster_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "envoy/access_log/access_log.h"
#include "envoy/api/api.h"
#include "envoy/certificate_provider/certificate_provider_manager.h"
#include "envoy/common/random_generator.h"
#include "envoy/config/bootstrap/v3/bootstrap.pb.h"
#include "envoy/config/cluster/v3/cluster.pb.h"
Expand Down Expand Up @@ -520,6 +521,11 @@ class ClusterManagerFactory {
* Returns the singleton manager.
*/
virtual Singleton::Manager& singletonManager() PURE;

/**
* Returns the certificate provider manager
*/
virtual CertificateProvider::CertificateProviderManager& certificateProviderManager() PURE;
};

/**
Expand Down
23 changes: 23 additions & 0 deletions source/common/certificate_provider/BUILD
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
load(
"//bazel:envoy_build_system.bzl",
"envoy_cc_library",
"envoy_package",
)

licenses(["notice"]) # Apache 2

envoy_package()

envoy_cc_library(
name = "certificate_provider_manager_impl_lib",
srcs = ["certificate_provider_manager_impl.cc"],
hdrs = ["certificate_provider_manager_impl.h"],
deps = [
"//envoy/api:api_interface",
"//envoy/certificate_provider:certificate_provider_factory_lib",
"//envoy/certificate_provider:certificate_provider_manager_interface",
"//source/common/config:utility_lib",
"@com_google_absl//absl/container:flat_hash_map",
"@envoy_api//envoy/config/core/v3:pkg_cc_proto",
],
)
Loading