diff --git a/include/envoy/secret/BUILD b/include/envoy/secret/BUILD index c4dcf8404fd6f..bb1eb8f1847a6 100644 --- a/include/envoy/secret/BUILD +++ b/include/envoy/secret/BUILD @@ -8,11 +8,18 @@ load( envoy_package() +envoy_cc_library( + name = "secret_callbacks_interface", + hdrs = ["secret_callbacks.h"], +) + envoy_cc_library( name = "secret_manager_interface", hdrs = ["secret_manager.h"], deps = [ + ":secret_callbacks_interface", "//include/envoy/ssl:tls_certificate_config_interface", "@envoy_api//envoy/api/v2/auth:cert_cc", + "@envoy_api//envoy/api/v2/core:config_source_cc", ], ) diff --git a/include/envoy/secret/secret_callbacks.h b/include/envoy/secret/secret_callbacks.h new file mode 100644 index 0000000000000..671bdc202ad7e --- /dev/null +++ b/include/envoy/secret/secret_callbacks.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include + +#include "envoy/common/pure.h" + +namespace Envoy { +namespace Secret { + +/** + * Callbacks invoked by a secret manager. + */ +class SecretCallbacks { +public: + virtual ~SecretCallbacks() {} + + virtual void onAddOrUpdateSecret() PURE; +}; + +} // namespace Secret +} // namespace Envoy \ No newline at end of file diff --git a/include/envoy/secret/secret_manager.h b/include/envoy/secret/secret_manager.h index d7f9788741213..7e93a0f950e4f 100644 --- a/include/envoy/secret/secret_manager.h +++ b/include/envoy/secret/secret_manager.h @@ -3,31 +3,67 @@ #include #include "envoy/api/v2/auth/cert.pb.h" +#include "envoy/secret/secret_callbacks.h" #include "envoy/ssl/tls_certificate_config.h" namespace Envoy { namespace Secret { /** - * A manager for static secrets. - * - * TODO(jaebong) Support dynamic secrets. + * A manager for static and dynamic secrets. */ class SecretManager { public: virtual ~SecretManager() {} /** + * @param config_source_hash a hash string of normalized config source. If it is empty string, + * find secret from the static secrets. * @param secret a protobuf message of envoy::api::v2::auth::Secret. * @throw an EnvoyException if the secret is invalid or not supported. */ - virtual void addOrUpdateSecret(const envoy::api::v2::auth::Secret& secret) PURE; + virtual void addOrUpdateSecret(const std::string& config_source_hash, + const envoy::api::v2::auth::Secret& secret) PURE; /** + * @param sds_config_source_hash hash string of normalized config source. * @param name a name of the Ssl::TlsCertificateConfig. * @return the TlsCertificate secret. Returns nullptr if the secret is not found. */ - virtual const Ssl::TlsCertificateConfig* findTlsCertificate(const std::string& name) const PURE; + virtual const Ssl::TlsCertificateConfig* findTlsCertificate(const std::string& config_source_hash, + const std::string& name) const PURE; + + /** + * Add or update SDS config source. SecretManager starts downloading secrets from registered + * config source. + * + * @param sdsConfigSource a protobuf message object contains SDS config source. + * @param config_name a name that uniquely refers to the SDS config source. + * @return a hash string of normalized config source. + */ + virtual std::string addOrUpdateSdsService(const envoy::api::v2::core::ConfigSource& config_source, + std::string config_name) PURE; + + /** + * Register callback function which is to be invoked on secret update. + * + * @param config_source_hash Hash code of ConfigSource. + * @param secret_name name of the secret. + * @param callback SecretCallbacks class. + */ + virtual void registerTlsCertificateConfigCallbacks(const std::string& config_source_hash, + const std::string& secret_name, + SecretCallbacks* callback) PURE; + + /** + * Unregister callback function. + * @param config_source_hash Hash code of ConfigSource. + * @param secret_name name of the secret. + * @param callback SecretCallbacks class. + */ + virtual void unRegisterTlsCertificateConfigCallbacks(const std::string& config_source_hash, + const std::string& secret_name, + SecretCallbacks& callback) PURE; }; } // namespace Secret diff --git a/include/envoy/ssl/context.h b/include/envoy/ssl/context.h index 5af2fa804fb8a..a01b0fd47c05b 100644 --- a/include/envoy/ssl/context.h +++ b/include/envoy/ssl/context.h @@ -33,11 +33,15 @@ class Context { virtual std::string getCertChainInformation() const PURE; }; +typedef std::shared_ptr ContextSharedPtr; + class ClientContext : public virtual Context {}; typedef std::unique_ptr ClientContextPtr; +typedef std::shared_ptr ClientContextSharedPtr; class ServerContext : public virtual Context {}; typedef std::unique_ptr ServerContextPtr; +typedef std::shared_ptr ServerContextSharedPtr; } // namespace Ssl } // namespace Envoy diff --git a/include/envoy/ssl/context_config.h b/include/envoy/ssl/context_config.h index a56a89e903e6b..54e34a68fc0a3 100644 --- a/include/envoy/ssl/context_config.h +++ b/include/envoy/ssl/context_config.h @@ -111,6 +111,23 @@ class ContextConfig { * @return The maximum TLS protocol version to negotiate. */ virtual unsigned maxProtocolVersion() const PURE; + + /** + * @return The hash code of SdsSecretConfig in std::string. If the SdsSecretConfig is empty, then + * returns empty string. + */ + virtual const std::string& sdsConfigSourceHash() const PURE; + + /** + * @return The secret name in SdsSecretConfig. If SdsSecretConfig is empty, returns empty string. + */ + virtual const std::string& sdsSecretName() const PURE; + + /** + * @return true if dynamic secret is required per config, and is not + * downloaded from SDS server yet; and false otherwise. + */ + virtual bool waitForSdsSecret() const PURE; }; class ClientContextConfig : public virtual ContextConfig { @@ -127,6 +144,8 @@ class ClientContextConfig : public virtual ContextConfig { virtual bool allowRenegotiation() const PURE; }; +typedef std::unique_ptr ClientContextConfigPtr; + class ServerContextConfig : public virtual ContextConfig { public: struct SessionTicketKey { @@ -148,5 +167,7 @@ class ServerContextConfig : public virtual ContextConfig { virtual const std::vector& sessionTicketKeys() const PURE; }; +typedef std::unique_ptr ServerContextConfigPtr; + } // namespace Ssl } // namespace Envoy diff --git a/include/envoy/ssl/context_manager.h b/include/envoy/ssl/context_manager.h index 7489800c99caf..185040694bab5 100644 --- a/include/envoy/ssl/context_manager.h +++ b/include/envoy/ssl/context_manager.h @@ -2,6 +2,7 @@ #include +#include "envoy/secret/secret_manager.h" #include "envoy/ssl/context.h" #include "envoy/ssl/context_config.h" #include "envoy/stats/stats.h" @@ -19,16 +20,42 @@ class ContextManager { /** * Builds a ClientContext from a ClientContextConfig. */ - virtual ClientContextPtr createSslClientContext(Stats::Scope& scope, - const ClientContextConfig& config) PURE; + virtual ClientContextSharedPtr createSslClientContext(Stats::Scope& scope, + const ClientContextConfig& config) PURE; + + /** + * Updates ClientContext and returns updated ClientContext. + * + * @param context ClientContext to be updated. + * @param scope stats scope. + * @param config supplies the configuration for ClientContext. + * @return an updated ClientContext. + */ + virtual ClientContextSharedPtr updateSslClientContext(const ClientContextSharedPtr context, + Stats::Scope& scope, + const ClientContextConfig& config) PURE; /** * Builds a ServerContext from a ServerContextConfig. */ - virtual ServerContextPtr + virtual ServerContextSharedPtr createSslServerContext(Stats::Scope& scope, const ServerContextConfig& config, const std::vector& server_names) PURE; + /** + * Updates ServerContext and returns updated ServerContext. + * + * @param context ServerContext to be updated. + * @param scope stats scope + * @param config supplies the configuration for ServerContext. + * @param server_names server names. + * @return an updated ServerContext. + */ + virtual ServerContextSharedPtr + updateSslServerContext(const ServerContextSharedPtr context, Stats::Scope& scope, + const ServerContextConfig& config, + const std::vector& server_names) PURE; + /** * @return the number of days until the next certificate being managed will expire. */ @@ -38,6 +65,16 @@ class ContextManager { * Iterate through all currently allocated contexts. */ virtual void iterateContexts(std::function callback) PURE; + + /** + * release context from this manager. + */ + virtual void releaseContext(Context* context) PURE; + + /** + * @return a SecretManager. + */ + virtual Secret::SecretManager& secretManager() PURE; }; } // namespace Ssl diff --git a/include/envoy/ssl/tls_certificate_config.h b/include/envoy/ssl/tls_certificate_config.h index 6a5c8c842a060..069d29f459a09 100644 --- a/include/envoy/ssl/tls_certificate_config.h +++ b/include/envoy/ssl/tls_certificate_config.h @@ -21,9 +21,15 @@ class TlsCertificateConfig { * @return a string of private key */ virtual const std::string& privateKey() const PURE; + + /** + * @return true if secret contains same certificate chain and private key. + * Otherwise returns false. + */ + virtual bool equalTo(const TlsCertificateConfig& secret) const PURE; }; -typedef std::unique_ptr TlsCertificateConfigPtr; +typedef std::unique_ptr TlsCertificateConfigPtr; } // namespace Ssl } // namespace Envoy diff --git a/source/common/config/BUILD b/source/common/config/BUILD index 83067aa8806eb..ba821c967bd73 100644 --- a/source/common/config/BUILD +++ b/source/common/config/BUILD @@ -237,6 +237,7 @@ envoy_cc_library( hdrs = ["protobuf_link_hacks.h"], deps = [ "@envoy_api//envoy/service/discovery/v2:ads_cc", + "@envoy_api//envoy/service/discovery/v2:sds_cc", "@envoy_api//envoy/service/ratelimit/v2:rls_cc", ], ) diff --git a/source/common/config/protobuf_link_hacks.h b/source/common/config/protobuf_link_hacks.h index 6792f3e797c1e..243d9ecf69a20 100644 --- a/source/common/config/protobuf_link_hacks.h +++ b/source/common/config/protobuf_link_hacks.h @@ -1,6 +1,7 @@ #pragma once #include "envoy/service/discovery/v2/ads.pb.h" +#include "envoy/service/discovery/v2/sds.pb.h" #include "envoy/service/ratelimit/v2/rls.pb.h" namespace Envoy { @@ -8,5 +9,6 @@ namespace Envoy { // Hack to force linking of the service: https://github.com/google/protobuf/issues/4221. // This file should be included ONLY if this hack is required. const envoy::service::discovery::v2::AdsDummy _ads_dummy; +const envoy::service::discovery::v2::SdsDummy _sds_dummy; const envoy::service::ratelimit::v2::RateLimitRequest _rls_dummy; } // namespace Envoy diff --git a/source/common/config/resources.h b/source/common/config/resources.h index 03f0c9a2efd8b..69ed2d91a46dc 100644 --- a/source/common/config/resources.h +++ b/source/common/config/resources.h @@ -15,6 +15,7 @@ class TypeUrlValues { const std::string Listener{"type.googleapis.com/envoy.api.v2.Listener"}; const std::string Cluster{"type.googleapis.com/envoy.api.v2.Cluster"}; const std::string ClusterLoadAssignment{"type.googleapis.com/envoy.api.v2.ClusterLoadAssignment"}; + const std::string Secret{"type.googleapis.com/envoy.api.v2.auth.Secret"}; const std::string RouteConfiguration{"type.googleapis.com/envoy.api.v2.RouteConfiguration"}; }; diff --git a/source/common/secret/BUILD b/source/common/secret/BUILD index 4f1eff746d6d9..e6faac70c5e4b 100644 --- a/source/common/secret/BUILD +++ b/source/common/secret/BUILD @@ -13,9 +13,34 @@ envoy_cc_library( srcs = ["secret_manager_impl.cc"], hdrs = ["secret_manager_impl.h"], deps = [ + ":sds_api_lib", + ":secret_manager_util", "//include/envoy/secret:secret_manager_interface", + "//include/envoy/server:instance_interface", "//source/common/common:minimal_logger_lib", "//source/common/ssl:tls_certificate_config_impl_lib", "@envoy_api//envoy/api/v2/auth:cert_cc", ], ) + +envoy_cc_library( + name = "secret_manager_util", + hdrs = ["secret_manager_util.h"], + deps = [ + "//source/common/json:json_loader_lib", + "@envoy_api//envoy/api/v2/core:config_source_cc", + ], +) + +envoy_cc_library( + name = "sds_api_lib", + srcs = ["sds_api.cc"], + hdrs = ["sds_api.h"], + deps = [ + ":secret_manager_util", + "//include/envoy/config:subscription_interface", + "//include/envoy/server:instance_interface", + "//source/common/config:resources_lib", + "//source/common/config:subscription_factory_lib", + ], +) diff --git a/source/common/secret/sds_api.cc b/source/common/secret/sds_api.cc new file mode 100644 index 0000000000000..6bb0771cf842b --- /dev/null +++ b/source/common/secret/sds_api.cc @@ -0,0 +1,62 @@ +#include "common/secret/sds_api.h" + +#include + +#include "common/config/resources.h" +#include "common/config/subscription_factory.h" +#include "common/secret/secret_manager_util.h" + +namespace Envoy { +namespace Secret { + +SdsApi::SdsApi(Server::Instance& server, const envoy::api::v2::core::ConfigSource& sds_config, + std::string sds_config_hash, std::string sds_config_name) + : server_(server), sds_config_(sds_config), sds_config_source_hash_(sds_config_hash), + sds_config_name_(sds_config_name) { + server_.initManager().registerTarget(*this); +} + +void SdsApi::initialize(std::function callback) { + initialize_callback_ = callback; + subscription_ = Envoy::Config::SubscriptionFactory::subscriptionFromConfigSource< + envoy::api::v2::auth::Secret>( + sds_config_, server_.localInfo().node(), server_.dispatcher(), server_.clusterManager(), + server_.random(), server_.stats(), /* rest_legacy_constructor */ nullptr, + "envoy.service.discovery.v2.SecretDiscoveryService.FetchSecrets", + "envoy.service.discovery.v2.SecretDiscoveryService.StreamSecrets"); + + Config::Utility::checkLocalInfo("sds", server_.localInfo()); + + subscription_->start({sds_config_name_}, *this); +} + +void SdsApi::onConfigUpdate(const ResourceVector& resources, const std::string&) { + for (const auto& resource : resources) { + switch (resource.type_case()) { + case envoy::api::v2::auth::Secret::kTlsCertificate: + server_.secretManager().addOrUpdateSecret(sds_config_source_hash_, resource); + break; + case envoy::api::v2::auth::Secret::kSessionTicketKeys: + NOT_IMPLEMENTED; + default: + throw EnvoyException("sds: invalid configuration"); + } + } + + runInitializeCallbackIfAny(); +} + +void SdsApi::onConfigUpdateFailed(const EnvoyException*) { + // We need to allow server startup to continue, even if we have a bad config. + runInitializeCallbackIfAny(); +} + +void SdsApi::runInitializeCallbackIfAny() { + if (initialize_callback_) { + initialize_callback_(); + initialize_callback_ = nullptr; + } +} + +} // namespace Secret +} // namespace Envoy \ No newline at end of file diff --git a/source/common/secret/sds_api.h b/source/common/secret/sds_api.h new file mode 100644 index 0000000000000..faf95bbfb2e21 --- /dev/null +++ b/source/common/secret/sds_api.h @@ -0,0 +1,45 @@ +#pragma once + +#include + +#include "envoy/api/v2/auth/cert.pb.h" +#include "envoy/api/v2/core/config_source.pb.h" +#include "envoy/config/subscription.h" +#include "envoy/server/instance.h" + +namespace Envoy { +namespace Secret { + +/** + * SDS API implementation that fetches secrets from SDS server via Subscription. + */ +class SdsApi : public Init::Target, Config::SubscriptionCallbacks { +public: + SdsApi(Server::Instance& server, const envoy::api::v2::core::ConfigSource& sds_config, + std::string sds_config_hash, std::string sds_config_name); + + // Init::Target + void initialize(std::function callback) override; + + // Config::SubscriptionCallbacks + void onConfigUpdate(const ResourceVector& resources, const std::string& version_info) override; + void onConfigUpdateFailed(const EnvoyException* e) override; + std::string resourceName(const ProtobufWkt::Any& resource) override { + return MessageUtil::anyConvert(resource).name(); + } + +private: + void runInitializeCallbackIfAny(); + + Server::Instance& server_; + const envoy::api::v2::core::ConfigSource sds_config_; + const std::string sds_config_source_hash_; + std::unique_ptr> subscription_; + std::function initialize_callback_; + std::string sds_config_name_; +}; + +typedef std::unique_ptr SdsApiPtr; + +} // namespace Secret +} // namespace Envoy \ No newline at end of file diff --git a/source/common/secret/secret_manager_impl.cc b/source/common/secret/secret_manager_impl.cc index 3e6689a369da4..0b3f28e9e86e5 100644 --- a/source/common/secret/secret_manager_impl.cc +++ b/source/common/secret/secret_manager_impl.cc @@ -1,27 +1,121 @@ #include "common/secret/secret_manager_impl.h" +#include + #include "envoy/common/exception.h" +#include "common/secret/secret_manager_util.h" #include "common/ssl/tls_certificate_config_impl.h" namespace Envoy { namespace Secret { -void SecretManagerImpl::addOrUpdateSecret(const envoy::api::v2::auth::Secret& secret) { +void SecretManagerImpl::addOrUpdateSecret(const std::string& config_source_hash, + const envoy::api::v2::auth::Secret& secret) { switch (secret.type_case()) { - case envoy::api::v2::auth::Secret::TypeCase::kTlsCertificate: - tls_certificate_secrets_[secret.name()] = + case envoy::api::v2::auth::Secret::TypeCase::kTlsCertificate: { + std::unique_lock lhs(tls_certificate_secrets_mutex_); + tls_certificate_secrets_[config_source_hash][secret.name()] = std::make_unique(secret.tls_certificate()); - break; + ; + + if (config_source_hash.empty()) { + return; + } + + const auto tls_certificate_secret = + tls_certificate_secrets_[config_source_hash][secret.name()].get(); + std::string secret_name = secret.name(); + server_.dispatcher().post([this, config_source_hash, secret_name, tls_certificate_secret]() { + std::unique_lock lhs(tls_certificate_secret_update_callbacks_mutex_); + auto config_source_it = tls_certificate_secret_update_callbacks_.find(config_source_hash); + if (config_source_it != tls_certificate_secret_update_callbacks_.end()) { + auto callback_it = config_source_it->second.find(secret_name); + if (callback_it != config_source_it->second.end()) { + if (callback_it->second.first == nullptr || + !callback_it->second.first->equalTo(*tls_certificate_secret)) { + for (auto& callback : callback_it->second.second) { + callback->onAddOrUpdateSecret(); + } + callback_it->second.first = tls_certificate_secret; + } + } + } + }); + } break; default: throw EnvoyException("Secret type not implemented"); } } const Ssl::TlsCertificateConfig* -SecretManagerImpl::findTlsCertificate(const std::string& name) const { - auto secret = tls_certificate_secrets_.find(name); - return (secret != tls_certificate_secrets_.end()) ? secret->second.get() : nullptr; +SecretManagerImpl::findTlsCertificate(const std::string& config_source_hash, + const std::string& name) const { + std::shared_lock lhs(tls_certificate_secrets_mutex_); + + auto config_source_it = tls_certificate_secrets_.find(config_source_hash); + if (config_source_it == tls_certificate_secrets_.end()) { + return nullptr; + } + + auto secret = config_source_it->second.find(name); + return (secret != config_source_it->second.end()) ? secret->second.get() : nullptr; +} + +std::string SecretManagerImpl::addOrUpdateSdsService( + const envoy::api::v2::core::ConfigSource& sds_config_source, std::string config_name) { + std::unique_lock lhs(sds_api_mutex_); + + auto hash = SecretManagerUtil::configSourceHash(sds_config_source); + std::string sds_apis_key = hash + config_name; + if (sds_apis_.find(sds_apis_key) != sds_apis_.end()) { + return hash; + } + + sds_apis_[sds_apis_key] = std::make_unique(server_, sds_config_source, hash, config_name); + + return hash; +} + +void SecretManagerImpl::registerTlsCertificateConfigCallbacks(const std::string& config_source_hash, + const std::string& secret_name, + SecretCallbacks* callback) { + auto secret = findTlsCertificate(config_source_hash, secret_name); + + std::unique_lock lhs(tls_certificate_secret_update_callbacks_mutex_); + + auto config_source_it = tls_certificate_secret_update_callbacks_.find(config_source_hash); + if (config_source_it == tls_certificate_secret_update_callbacks_.end()) { + tls_certificate_secret_update_callbacks_[config_source_hash][secret_name] = {secret, + {callback}}; + return; + } + + auto name_it = config_source_it->second.find(secret_name); + if (name_it == config_source_it->second.end()) { + config_source_it->second[secret_name] = {secret, {callback}}; + return; + } + + name_it->second.second.push_back(callback); +} + +void SecretManagerImpl::unRegisterTlsCertificateConfigCallbacks( + const std::string& config_source_hash, const std::string& secret_name, + SecretCallbacks& callback) { + std::unique_lock lhs(tls_certificate_secret_update_callbacks_mutex_); + + auto config_source_it = tls_certificate_secret_update_callbacks_.find(config_source_hash); + if (config_source_it != tls_certificate_secret_update_callbacks_.end()) { + auto name_it = config_source_it->second.find(secret_name); + if (name_it != config_source_it->second.end()) { + auto callback_it = + std::find(name_it->second.second.begin(), name_it->second.second.end(), &callback); + if (callback_it != name_it->second.second.end()) { + name_it->second.second.erase(callback_it); + } + } + } } } // namespace Secret diff --git a/source/common/secret/secret_manager_impl.h b/source/common/secret/secret_manager_impl.h index b9406754a8c45..3bd6bca291515 100644 --- a/source/common/secret/secret_manager_impl.h +++ b/source/common/secret/secret_manager_impl.h @@ -1,22 +1,63 @@ #pragma once +#include #include #include "envoy/secret/secret_manager.h" +#include "envoy/server/instance.h" #include "envoy/ssl/tls_certificate_config.h" #include "common/common/logger.h" +#include "common/secret/sds_api.h" namespace Envoy { namespace Secret { class SecretManagerImpl : public SecretManager, Logger::Loggable { public: - void addOrUpdateSecret(const envoy::api::v2::auth::Secret& secret) override; - const Ssl::TlsCertificateConfig* findTlsCertificate(const std::string& name) const override; + SecretManagerImpl(Server::Instance& server) : server_(server) {} + + void addOrUpdateSecret(const std::string& config_source_hash, + const envoy::api::v2::auth::Secret& secret) override; + const Ssl::TlsCertificateConfig* findTlsCertificate(const std::string& config_source_hash, + const std::string& name) const override; + + std::string addOrUpdateSdsService(const envoy::api::v2::core::ConfigSource& config_source, + std::string config_name) override; + + void registerTlsCertificateConfigCallbacks(const std::string& config_source_hash, + const std::string& secret_name, + SecretCallbacks* callback) override; + + void unRegisterTlsCertificateConfigCallbacks(const std::string& config_source_hash, + const std::string& secret_name, + SecretCallbacks& callback) override; private: - std::unordered_map tls_certificate_secrets_; + Server::Instance& server_; + + // map hash code of SDS config source and SdsApi object. + std::unordered_map sds_apis_; + mutable std::shared_timed_mutex sds_api_mutex_; + + // Manages pairs of name and Ssl::TlsCertificateConfig grouped by SDS config source hash. + // If SDS config source hash is empty, it is a static secret. + std::unordered_map> + tls_certificate_secrets_; + mutable std::shared_timed_mutex tls_certificate_secrets_mutex_; + + // callback functions for secret update + // "config source hash": { + // "secret name": + // secret, + // [{callback}] + // ] + // } + std::unordered_map>>> + tls_certificate_secret_update_callbacks_; + mutable std::shared_timed_mutex tls_certificate_secret_update_callbacks_mutex_; }; } // namespace Secret diff --git a/source/common/secret/secret_manager_util.h b/source/common/secret/secret_manager_util.h new file mode 100644 index 0000000000000..5571e28943867 --- /dev/null +++ b/source/common/secret/secret_manager_util.h @@ -0,0 +1,37 @@ +#pragma once + +#include "envoy/api/v2/core/config_source.pb.h" + +#include "common/common/fmt.h" +#include "common/json/json_loader.h" +#include "common/protobuf/protobuf.h" + +namespace Envoy { +namespace Secret { + +class SecretManagerUtil { +public: + virtual ~SecretManagerUtil() {} + + /** + * Calculate hash code of ConfigSource. To identify the same ConfigSource, calculate the hash + * code from the ConfigSource. + * + * @param config_source envoy::api::v2::core::ConfigSource. + * @return hash code. + */ + static std::string configSourceHash(const envoy::api::v2::core::ConfigSource& config_source) { + std::string jsonstr; + if (Protobuf::util::MessageToJsonString(config_source, &jsonstr).ok()) { + auto obj = Json::Factory::loadFromString(jsonstr); + if (obj.get() != nullptr) { + return std::to_string(obj->hash()); + } + } + throw EnvoyException( + fmt::format("Invalid ConfigSource message: {}", config_source.DebugString())); + } +}; + +} // namespace Secret +} // namespace Envoy \ No newline at end of file diff --git a/source/common/ssl/BUILD b/source/common/ssl/BUILD index 486d0da347dca..b40b86820e8ef 100644 --- a/source/common/ssl/BUILD +++ b/source/common/ssl/BUILD @@ -18,6 +18,8 @@ envoy_cc_library( ":context_lib", "//include/envoy/network:connection_interface", "//include/envoy/network:transport_socket_interface", + "//include/envoy/secret:secret_callbacks_interface", + "//include/envoy/secret:secret_manager_interface", "//source/common/common:assert_lib", "//source/common/common:empty_string", "//source/common/common:minimal_logger_lib", @@ -58,6 +60,7 @@ envoy_cc_library( external_deps = ["ssl"], deps = [ "//include/envoy/runtime:runtime_interface", + "//include/envoy/secret:secret_manager_interface", "//include/envoy/ssl:context_config_interface", "//include/envoy/ssl:context_interface", "//include/envoy/ssl:context_manager_interface", diff --git a/source/common/ssl/context_config_impl.cc b/source/common/ssl/context_config_impl.cc index 374cd2945b50a..5311ae7a46580 100644 --- a/source/common/ssl/context_config_impl.cc +++ b/source/common/ssl/context_config_impl.cc @@ -3,6 +3,8 @@ #include #include +#include "envoy/ssl/tls_certificate_config.h" + #include "common/common/assert.h" #include "common/common/empty_string.h" #include "common/config/datasource.h" @@ -16,20 +18,42 @@ namespace Ssl { namespace { +std::string readSdsSecretName(const envoy::api::v2::auth::CommonTlsContext& config) { + return (!config.tls_certificate_sds_secret_configs().empty()) + ? config.tls_certificate_sds_secret_configs()[0].name() + : EMPTY_STRING; +} + +std::string readConfigSourceHash(const envoy::api::v2::auth::CommonTlsContext& config, + Secret::SecretManager& secret_manager) { + return (!config.tls_certificate_sds_secret_configs().empty() && + config.tls_certificate_sds_secret_configs()[0].has_sds_config()) + ? secret_manager.addOrUpdateSdsService( + config.tls_certificate_sds_secret_configs()[0].sds_config(), + config.tls_certificate_sds_secret_configs()[0].name()) + : EMPTY_STRING; +} + std::string readConfig( const envoy::api::v2::auth::CommonTlsContext& config, Secret::SecretManager& secret_manager, + const std::string& config_source_hash, const std::string& secret_name, const std::function& read_inline_config, - const std::function& read_secret) { + const std::function& + read_managed_secret) { if (!config.tls_certificates().empty()) { return read_inline_config(config.tls_certificates()[0]); } else if (!config.tls_certificate_sds_secret_configs().empty()) { - auto name = config.tls_certificate_sds_secret_configs()[0].name(); - const Ssl::TlsCertificateConfig* secret = secret_manager.findTlsCertificate(name); + const auto secret = secret_manager.findTlsCertificate(config_source_hash, secret_name); if (!secret) { - throw EnvoyException(fmt::format("Static secret is not defined: {}", name)); + if (config_source_hash.empty()) { + throw EnvoyException( + fmt::format("Unknown static secret: {} : {}", config_source_hash, secret_name)); + } else { + return EMPTY_STRING; + } } - return read_secret(*secret); + return read_managed_secret(*secret); } else { return EMPTY_STRING; } @@ -55,7 +79,9 @@ const std::string ContextConfigImpl::DEFAULT_ECDH_CURVES = "X25519:P-256"; ContextConfigImpl::ContextConfigImpl(const envoy::api::v2::auth::CommonTlsContext& config, Secret::SecretManager& secret_manager) - : alpn_protocols_(RepeatedPtrUtil::join(config.alpn_protocols(), ",")), + : secret_manager_(secret_manager), sds_secret_name_(readSdsSecretName(config)), + sds_config_source_hash_(readConfigSourceHash(config, secret_manager)), + alpn_protocols_(RepeatedPtrUtil::join(config.alpn_protocols(), ",")), alt_alpn_protocols_(config.deprecated_v1().alt_alpn_protocols()), cipher_suites_(StringUtil::nonEmptyStringOrDefault( RepeatedPtrUtil::join(config.tls_params().cipher_suites(), ":"), DEFAULT_CIPHER_SUITES)), @@ -68,7 +94,7 @@ ContextConfigImpl::ContextConfigImpl(const envoy::api::v2::auth::CommonTlsContex certificate_revocation_list_path_( Config::DataSource::getPath(config.validation_context().crl())), cert_chain_(readConfig( - config, secret_manager, + config, secret_manager, sds_config_source_hash_, sds_secret_name_, [](const envoy::api::v2::auth::TlsCertificate& tls_certificate) -> std::string { return Config::DataSource::read(tls_certificate.certificate_chain(), true); }, @@ -80,7 +106,7 @@ ContextConfigImpl::ContextConfigImpl(const envoy::api::v2::auth::CommonTlsContex ? "" : Config::DataSource::getPath(config.tls_certificates()[0].certificate_chain())), private_key_(readConfig( - config, secret_manager, + config, secret_manager, sds_config_source_hash_, sds_secret_name_, [](const envoy::api::v2::auth::TlsCertificate& tls_certificate) -> std::string { return Config::DataSource::read(tls_certificate.private_key(), true); }, @@ -138,6 +164,37 @@ unsigned ContextConfigImpl::tlsVersionFromProto( NOT_REACHED; } +const std::string& ContextConfigImpl::certChain() const { + if (!cert_chain_.empty()) { + return cert_chain_; + } + + auto secret = secret_manager_.findTlsCertificate(sds_config_source_hash_, sds_secret_name_); + if (!secret) { + return cert_chain_; + } + + return secret->certificateChain(); +} + +const std::string& ContextConfigImpl::privateKey() const { + if (!private_key_.empty()) { + return private_key_; + } + + auto secret = secret_manager_.findTlsCertificate(sds_config_source_hash_, sds_secret_name_); + if (!secret) { + return private_key_; + } + + return secret->privateKey(); +} + +bool ContextConfigImpl::waitForSdsSecret() const { + return !sds_config_source_hash_.empty() && !sds_secret_name_.empty() && certChain().empty() && + privateKey().empty(); +} + ClientContextConfigImpl::ClientContextConfigImpl( const envoy::api::v2::auth::UpstreamTlsContext& config, Secret::SecretManager& secret_manager) : ContextConfigImpl(config.common_tls_context(), secret_manager), diff --git a/source/common/ssl/context_config_impl.h b/source/common/ssl/context_config_impl.h index 2628f39b2e00c..0ad991450e8f2 100644 --- a/source/common/ssl/context_config_impl.h +++ b/source/common/ssl/context_config_impl.h @@ -33,11 +33,11 @@ class ContextConfigImpl : public virtual Ssl::ContextConfig { ? INLINE_STRING : certificate_revocation_list_path_; } - const std::string& certChain() const override { return cert_chain_; } + const std::string& certChain() const override; const std::string& certChainPath() const override { return (cert_chain_path_.empty() && !cert_chain_.empty()) ? INLINE_STRING : cert_chain_path_; } - const std::string& privateKey() const override { return private_key_; } + const std::string& privateKey() const override; const std::string& privateKeyPath() const override { return (private_key_path_.empty() && !private_key_.empty()) ? INLINE_STRING : private_key_path_; } @@ -54,6 +54,11 @@ class ContextConfigImpl : public virtual Ssl::ContextConfig { unsigned minProtocolVersion() const override { return min_protocol_version_; }; unsigned maxProtocolVersion() const override { return max_protocol_version_; }; + const std::string& sdsConfigSourceHash() const override { return sds_config_source_hash_; } + const std::string& sdsSecretName() const override { return sds_secret_name_; } + + bool waitForSdsSecret() const override; + protected: ContextConfigImpl(const envoy::api::v2::auth::CommonTlsContext& config, Secret::SecretManager& secret_manager); @@ -66,6 +71,9 @@ class ContextConfigImpl : public virtual Ssl::ContextConfig { static const std::string DEFAULT_CIPHER_SUITES; static const std::string DEFAULT_ECDH_CURVES; + Secret::SecretManager& secret_manager_; + const std::string sds_secret_name_; + const std::string sds_config_source_hash_; const std::string alpn_protocols_; const std::string alt_alpn_protocols_; const std::string cipher_suites_; diff --git a/source/common/ssl/context_impl.h b/source/common/ssl/context_impl.h index 4001ff23d9719..0034e1860d9c2 100644 --- a/source/common/ssl/context_impl.h +++ b/source/common/ssl/context_impl.h @@ -136,6 +136,8 @@ class ContextImpl : public virtual Context { std::string cert_chain_file_path_; }; +typedef std::shared_ptr ContextImplSharedPtr; + class ClientContextImpl : public ContextImpl, public ClientContext { public: ClientContextImpl(ContextManagerImpl& parent, Stats::Scope& scope, @@ -148,6 +150,8 @@ class ClientContextImpl : public ContextImpl, public ClientContext { const bool allow_renegotiation_; }; +typedef std::shared_ptr ClientContextImplSharedPtr; + class ServerContextImpl : public ContextImpl, public ServerContext { public: ServerContextImpl(ContextManagerImpl& parent, Stats::Scope& scope, @@ -165,5 +169,7 @@ class ServerContextImpl : public ContextImpl, public ServerContext { const std::vector session_ticket_keys_; }; +typedef std::shared_ptr ServerContextImplSharedPtr; + } // namespace Ssl } // namespace Envoy diff --git a/source/common/ssl/context_manager_impl.cc b/source/common/ssl/context_manager_impl.cc index 1c54c2f018656..ba7d5fe8182d4 100644 --- a/source/common/ssl/context_manager_impl.cc +++ b/source/common/ssl/context_manager_impl.cc @@ -17,23 +17,65 @@ void ContextManagerImpl::releaseContext(Context* context) { // context may not be found, in the case that a subclass of Context throws // in it's constructor. In that case the context did not get added, but // the destructor of Context will run and call releaseContext(). - contexts_.remove(context); + contexts_.erase(context); } -ClientContextPtr ContextManagerImpl::createSslClientContext(Stats::Scope& scope, - const ClientContextConfig& config) { +Ssl::ClientContextSharedPtr +ContextManagerImpl::createSslClientContext(Stats::Scope& scope, const ClientContextConfig& config) { + if (config.waitForSdsSecret()) { + return nullptr; + } + ClientContextPtr context(new ClientContextImpl(*this, scope, config)); std::unique_lock lock(contexts_lock_); - contexts_.emplace_back(context.get()); + contexts_.emplace(context.get()); return context; } -ServerContextPtr +Ssl::ClientContextSharedPtr +ContextManagerImpl::updateSslClientContext(const Ssl::ClientContextSharedPtr client_context, + Stats::Scope& scope, const ClientContextConfig& config) { + std::unique_lock lock(contexts_lock_); + + if (contexts_.erase(client_context.get()) == 0) { + return nullptr; + } + + ClientContextSharedPtr context(new ClientContextImpl(*this, scope, config)); + contexts_.emplace(context.get()); + + return context; +} + +Ssl::ServerContextSharedPtr ContextManagerImpl::createSslServerContext(Stats::Scope& scope, const ServerContextConfig& config, const std::vector& server_names) { - ServerContextPtr context(new ServerContextImpl(*this, scope, config, server_names, runtime_)); + if (!config.sdsConfigSourceHash().empty() && !config.sdsSecretName().empty() && + config.certChain().empty() && config.privateKey().empty()) { + return nullptr; + } + + ServerContextSharedPtr context( + new ServerContextImpl(*this, scope, config, server_names, runtime_)); + std::unique_lock lock(contexts_lock_); + contexts_.emplace(context.get()); + return context; +} + +Ssl::ServerContextSharedPtr +ContextManagerImpl::updateSslServerContext(const ServerContextSharedPtr server_context, + Stats::Scope& scope, const ServerContextConfig& config, + const std::vector& server_names) { std::unique_lock lock(contexts_lock_); - contexts_.emplace_back(context.get()); + + if (contexts_.erase(server_context.get()) == 0) { + return nullptr; + } + + ServerContextSharedPtr context( + new ServerContextImpl(*this, scope, config, server_names, runtime_)); + contexts_.emplace(context.get()); + return context; } diff --git a/source/common/ssl/context_manager_impl.h b/source/common/ssl/context_manager_impl.h index bd31db4f22008..8fc6bee820ca6 100644 --- a/source/common/ssl/context_manager_impl.h +++ b/source/common/ssl/context_manager_impl.h @@ -1,10 +1,11 @@ #pragma once #include -#include +#include #include #include "envoy/runtime/runtime.h" +#include "envoy/secret/secret_manager.h" #include "envoy/ssl/context_manager.h" namespace Envoy { @@ -19,7 +20,8 @@ namespace Ssl { */ class ContextManagerImpl final : public ContextManager { public: - ContextManagerImpl(Runtime::Loader& runtime) : runtime_(runtime) {} + ContextManagerImpl(Runtime::Loader& runtime, Secret::SecretManager& secret_manager) + : runtime_(runtime), secret_manager_(secret_manager) {} ~ContextManagerImpl(); /** @@ -27,21 +29,33 @@ class ContextManagerImpl final : public ContextManager { * admin purposes. When a caller frees a context it will tell us to release it also from the list * of contexts. */ - void releaseContext(Context* context); + void releaseContext(Context* context) override; // Ssl::ContextManager - Ssl::ClientContextPtr createSslClientContext(Stats::Scope& scope, - const ClientContextConfig& config) override; - Ssl::ServerContextPtr + Ssl::ClientContextSharedPtr createSslClientContext(Stats::Scope& scope, + const ClientContextConfig& config) override; + Ssl::ClientContextSharedPtr updateSslClientContext(const Ssl::ClientContextSharedPtr context, + Stats::Scope& scope, + const ClientContextConfig& config) override; + + Ssl::ServerContextSharedPtr createSslServerContext(Stats::Scope& scope, const ServerContextConfig& config, const std::vector& server_names) override; + virtual Ssl::ServerContextSharedPtr + updateSslServerContext(const Ssl::ServerContextSharedPtr context, Stats::Scope& scope, + const ServerContextConfig& config, + const std::vector& server_names) override; + size_t daysUntilFirstCertExpires() const override; void iterateContexts(std::function callback) override; + Secret::SecretManager& secretManager() override { return secret_manager_; } + private: Runtime::Loader& runtime_; - std::list contexts_; + std::set contexts_; mutable std::shared_timed_mutex contexts_lock_; + Secret::SecretManager& secret_manager_; }; } // namespace Ssl diff --git a/source/common/ssl/ssl_socket.cc b/source/common/ssl/ssl_socket.cc index b433358f16532..3bbc037214211 100644 --- a/source/common/ssl/ssl_socket.cc +++ b/source/common/ssl/ssl_socket.cc @@ -1,5 +1,7 @@ #include "common/ssl/ssl_socket.h" +#include + #include "common/common/assert.h" #include "common/common/empty_string.h" #include "common/common/hex.h" @@ -14,8 +16,8 @@ using Envoy::Network::PostIoAction; namespace Envoy { namespace Ssl { -SslSocket::SslSocket(Context& ctx, InitialState state) - : ctx_(dynamic_cast(ctx)), ssl_(ctx_.newSsl()) { +SslSocket::SslSocket(ContextImplSharedPtr ctx, InitialState state) + : ctx_(ctx), ssl_(ctx_->newSsl()) { if (state == InitialState::Client) { SSL_set_connect_state(ssl_.get()); } else { @@ -98,7 +100,7 @@ PostIoAction SslSocket::doHandshake() { if (rc == 1) { ENVOY_CONN_LOG(debug, "handshake complete", callbacks_->connection()); handshake_complete_ = true; - ctx_.logHandshake(ssl_.get()); + ctx_->logHandshake(ssl_.get()); callbacks_->raiseEvent(Network::ConnectionEvent::Connected); // It's possible that we closed during the handshake callback. @@ -125,7 +127,7 @@ void SslSocket::drainErrorQueue() { while (uint64_t err = ERR_get_error()) { if (ERR_GET_LIB(err) == ERR_LIB_SSL) { if (ERR_GET_REASON(err) == SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE) { - ctx_.stats().fail_verify_no_cert_.inc(); + ctx_->stats().fail_verify_no_cert_.inc(); saw_counted_error = true; } else if (ERR_GET_REASON(err) == SSL_R_CERTIFICATE_VERIFY_FAILED) { saw_counted_error = true; @@ -138,7 +140,7 @@ void SslSocket::drainErrorQueue() { ERR_reason_error_string(err)); } if (saw_error && !saw_counted_error) { - ctx_.stats().connection_error_.inc(); + ctx_->stats().connection_error_.inc(); } } @@ -373,28 +375,72 @@ std::string SslSocket::subjectLocalCertificate() const { return getSubjectFromCertificate(cert); } -ClientSslSocketFactory::ClientSslSocketFactory(const ClientContextConfig& config, +ClientSslSocketFactory::ClientSslSocketFactory(std::unique_ptr config, Ssl::ContextManager& manager, Stats::Scope& stats_scope) - : ssl_ctx_(manager.createSslClientContext(stats_scope, config)) {} + : ssl_ctx_(manager.createSslClientContext(stats_scope, *config.get())), + config_(std::move(config)), manager_(manager), stats_scope_(stats_scope) {} Network::TransportSocketPtr ClientSslSocketFactory::createTransportSocket() const { - return std::make_unique(*ssl_ctx_, Ssl::InitialState::Client); + return ssl_ctx_ ? std::make_unique( + std::dynamic_pointer_cast(ssl_ctx_), Ssl::InitialState::Client) + : nullptr; +} + +ClientSslSocketFactory::~ClientSslSocketFactory() { + manager_.releaseContext(ssl_ctx_.get()); + if (config_) { + manager_.secretManager().unRegisterTlsCertificateConfigCallbacks( + config_->sdsConfigSourceHash(), config_->sdsSecretName(), *this); + } +} + +void ClientSslSocketFactory::onAddOrUpdateSecret() { + if (ssl_ctx_) { + ENVOY_LOG(debug, "cluster socket updated"); + ssl_ctx_ = manager_.updateSslClientContext(ssl_ctx_, stats_scope_, *config_.get()); + } else { + ENVOY_LOG(debug, "cluster socket initialized"); + ssl_ctx_ = manager_.createSslClientContext(stats_scope_, *config_.get()); + } } bool ClientSslSocketFactory::implementsSecureTransport() const { return true; } -ServerSslSocketFactory::ServerSslSocketFactory(const ServerContextConfig& config, +ServerSslSocketFactory::ServerSslSocketFactory(std::unique_ptr config, Ssl::ContextManager& manager, Stats::Scope& stats_scope, const std::vector& server_names) - : ssl_ctx_(manager.createSslServerContext(stats_scope, config, server_names)) {} + : ssl_ctx_(manager.createSslServerContext(stats_scope, *config.get(), server_names)), + config_(std::move(config)), manager_(manager), stats_scope_(stats_scope), + server_names_(server_names) {} + +ServerSslSocketFactory::~ServerSslSocketFactory() { + manager_.releaseContext(ssl_ctx_.get()); + if (config_) { + manager_.secretManager().unRegisterTlsCertificateConfigCallbacks( + config_->sdsConfigSourceHash(), config_->sdsSecretName(), *this); + } +} Network::TransportSocketPtr ServerSslSocketFactory::createTransportSocket() const { - return std::make_unique(*ssl_ctx_, Ssl::InitialState::Server); + return ssl_ctx_ ? std::make_unique( + std::dynamic_pointer_cast(ssl_ctx_), Ssl::InitialState::Server) + : nullptr; } bool ServerSslSocketFactory::implementsSecureTransport() const { return true; } +void ServerSslSocketFactory::onAddOrUpdateSecret() { + if (ssl_ctx_) { + ENVOY_LOG(debug, "listener socket updated"); + ssl_ctx_ = + manager_.updateSslServerContext(ssl_ctx_, stats_scope_, *config_.get(), server_names_); + } else { + ENVOY_LOG(debug, "listener socket initialized"); + ssl_ctx_ = manager_.createSslServerContext(stats_scope_, *config_.get(), server_names_); + } +} + } // namespace Ssl } // namespace Envoy diff --git a/source/common/ssl/ssl_socket.h b/source/common/ssl/ssl_socket.h index 6bb040edcd7ef..3d3bc3f3f54e3 100644 --- a/source/common/ssl/ssl_socket.h +++ b/source/common/ssl/ssl_socket.h @@ -5,6 +5,7 @@ #include "envoy/network/connection.h" #include "envoy/network/transport_socket.h" +#include "envoy/secret/secret_callbacks.h" #include "common/common/logger.h" #include "common/ssl/context_impl.h" @@ -20,7 +21,7 @@ class SslSocket : public Network::TransportSocket, public Connection, protected Logger::Loggable { public: - SslSocket(Context& ctx, InitialState state); + SslSocket(ContextImplSharedPtr ctx, InitialState state); // Ssl::Connection bool peerCertificatePresented() const override; @@ -55,7 +56,7 @@ class SslSocket : public Network::TransportSocket, std::vector getDnsSansFromCertificate(X509* cert); Network::TransportSocketCallbacks* callbacks_{}; - ContextImpl& ctx_; + ContextImplSharedPtr ctx_; bssl::UniquePtr ssl_; bool handshake_complete_{}; bool shutdown_sent_{}; @@ -64,27 +65,52 @@ class SslSocket : public Network::TransportSocket, mutable std::string cached_url_encoded_pem_encoded_peer_certificate_; }; -class ClientSslSocketFactory : public Network::TransportSocketFactory { +class ClientSslSocketFactory : public Network::TransportSocketFactory, + public Secret::SecretCallbacks, + Logger::Loggable { public: - ClientSslSocketFactory(const ClientContextConfig& config, Ssl::ContextManager& manager, + ClientSslSocketFactory(std::unique_ptr config, Ssl::ContextManager& manager, Stats::Scope& stats_scope); + virtual ~ClientSslSocketFactory(); + Network::TransportSocketPtr createTransportSocket() const override; bool implementsSecureTransport() const override; + // Secret::SecretCallbacks + void onAddOrUpdateSecret() override; + private: - const ClientContextPtr ssl_ctx_; + ClientContextSharedPtr ssl_ctx_; + ClientContextConfigPtr config_; + Ssl::ContextManager& manager_; + Stats::Scope& stats_scope_; }; -class ServerSslSocketFactory : public Network::TransportSocketFactory { +typedef std::unique_ptr ClientContextConfigPtr; + +class ServerSslSocketFactory : public Network::TransportSocketFactory, + public Secret::SecretCallbacks, + Logger::Loggable { public: - ServerSslSocketFactory(const ServerContextConfig& config, Ssl::ContextManager& manager, + ServerSslSocketFactory(std::unique_ptr config, Ssl::ContextManager& manager, Stats::Scope& stats_scope, const std::vector& server_names); + virtual ~ServerSslSocketFactory(); + Network::TransportSocketPtr createTransportSocket() const override; bool implementsSecureTransport() const override; + // Secret::SecretCallbacks + void onAddOrUpdateSecret() override; + private: - const ServerContextPtr ssl_ctx_; + ServerContextSharedPtr ssl_ctx_; + ServerContextConfigPtr config_; + Ssl::ContextManager& manager_; + Stats::Scope& stats_scope_; + const std::vector server_names_; }; +typedef std::unique_ptr ServerContextConfigPtr; + } // namespace Ssl } // namespace Envoy diff --git a/source/common/ssl/tls_certificate_config_impl.cc b/source/common/ssl/tls_certificate_config_impl.cc index 4f0afeb49733f..16cf2e99683c4 100644 --- a/source/common/ssl/tls_certificate_config_impl.cc +++ b/source/common/ssl/tls_certificate_config_impl.cc @@ -1,5 +1,7 @@ #include "common/ssl/tls_certificate_config_impl.h" +#include + #include "envoy/common/exception.h" #include "common/config/datasource.h" @@ -12,5 +14,9 @@ TlsCertificateConfigImpl::TlsCertificateConfigImpl( : certificate_chain_(Config::DataSource::read(config.certificate_chain(), true)), private_key_(Config::DataSource::read(config.private_key(), true)) {} +bool TlsCertificateConfigImpl::equalTo(const TlsCertificateConfig& secret) const { + return certificate_chain_ == secret.certificateChain() && private_key_ == secret.privateKey(); +} + } // namespace Ssl } // namespace Envoy diff --git a/source/common/ssl/tls_certificate_config_impl.h b/source/common/ssl/tls_certificate_config_impl.h index b8875ffd6998e..2ba50c4e09f39 100644 --- a/source/common/ssl/tls_certificate_config_impl.h +++ b/source/common/ssl/tls_certificate_config_impl.h @@ -14,6 +14,7 @@ class TlsCertificateConfigImpl : public TlsCertificateConfig { const std::string& certificateChain() const override { return certificate_chain_; } const std::string& privateKey() const override { return private_key_; } + bool equalTo(const TlsCertificateConfig& secret) const override; private: const std::string certificate_chain_; diff --git a/source/common/upstream/upstream_impl.cc b/source/common/upstream/upstream_impl.cc index ea36ac333fe28..412e3e224f8a5 100644 --- a/source/common/upstream/upstream_impl.cc +++ b/source/common/upstream/upstream_impl.cc @@ -132,9 +132,13 @@ HostImpl::createConnection(Event::Dispatcher& dispatcher, const ClusterInfo& clu connection_options = options; } + auto transport_socket = cluster.transportSocketFactory().createTransportSocket(); + if (!transport_socket) { + return nullptr; + } + Network::ClientConnectionPtr connection = dispatcher.createClientConnection( - address, cluster.sourceAddress(), cluster.transportSocketFactory().createTransportSocket(), - connection_options); + address, cluster.sourceAddress(), std::move(transport_socket), connection_options); connection->setBufferLimits(cluster.perConnectionBufferLimitBytes()); return connection; } diff --git a/source/extensions/transport_sockets/ssl/config.cc b/source/extensions/transport_sockets/ssl/config.cc index c0c0feec1970a..c2dfd151e8d23 100644 --- a/source/extensions/transport_sockets/ssl/config.cc +++ b/source/extensions/transport_sockets/ssl/config.cc @@ -16,12 +16,22 @@ namespace SslTransport { Network::TransportSocketFactoryPtr UpstreamSslSocketFactory::createTransportSocketFactory( const Protobuf::Message& message, Server::Configuration::TransportSocketFactoryContext& context) { - return std::make_unique( - Ssl::ClientContextConfigImpl( + std::unique_ptr upstream_config = + std::make_unique( MessageUtil::downcastAndValidate( message), - context.secretManager()), - context.sslContextManager(), context.statsScope()); + context.secretManager()); + + auto hash = upstream_config->sdsConfigSourceHash(); + auto name = upstream_config->sdsSecretName(); + + std::unique_ptr tsf = std::make_unique( + std::move(upstream_config), context.sslContextManager(), context.statsScope()); + if (!hash.empty()) { + context.sslContextManager().secretManager().registerTlsCertificateConfigCallbacks(hash, name, + tsf.get()); + } + return std::move(tsf); } ProtobufTypes::MessagePtr UpstreamSslSocketFactory::createEmptyConfigProto() { @@ -35,12 +45,25 @@ static Registry::RegisterFactory& server_names) { - return std::make_unique( - Ssl::ServerContextConfigImpl( + std::unique_ptr downstream_config = + std::make_unique( MessageUtil::downcastAndValidate( message), - context.secretManager()), - context.sslContextManager(), context.statsScope(), server_names); + context.sslContextManager().secretManager()); + + auto hash = downstream_config->sdsConfigSourceHash(); + auto name = downstream_config->sdsSecretName(); + + std::unique_ptr tsf = std::make_unique( + std::move(downstream_config), context.sslContextManager(), context.statsScope(), + server_names); + + if (!hash.empty()) { + context.sslContextManager().secretManager().registerTlsCertificateConfigCallbacks(hash, name, + tsf.get()); + } + + return std::move(tsf); } ProtobufTypes::MessagePtr DownstreamSslSocketFactory::createEmptyConfigProto() { diff --git a/source/server/config_validation/server.cc b/source/server/config_validation/server.cc index 885c0b997a39a..5ebae7a00b0a8 100644 --- a/source/server/config_validation/server.cc +++ b/source/server/config_validation/server.cc @@ -78,8 +78,8 @@ void ValidationInstance::initialize(Options& options, Configuration::InitialImpl initial_config(bootstrap); thread_local_.registerThread(*dispatcher_, true); runtime_loader_ = component_factory.createRuntime(*this, initial_config); - secret_manager_.reset(new Secret::SecretManagerImpl()); - ssl_context_manager_.reset(new Ssl::ContextManagerImpl(*runtime_loader_)); + secret_manager_.reset(new Secret::SecretManagerImpl(*this)); + ssl_context_manager_.reset(new Ssl::ContextManagerImpl(*runtime_loader_, *secret_manager_)); cluster_manager_factory_.reset(new Upstream::ValidationClusterManagerFactory( runtime(), stats(), threadLocal(), random(), dnsResolver(), sslContextManager(), dispatcher(), localInfo(), *secret_manager_)); diff --git a/source/server/configuration_impl.cc b/source/server/configuration_impl.cc index 0746d48d9d6d0..dd9b8b3165197 100644 --- a/source/server/configuration_impl.cc +++ b/source/server/configuration_impl.cc @@ -13,6 +13,7 @@ #include "envoy/ssl/context_manager.h" #include "common/common/assert.h" +#include "common/common/empty_string.h" #include "common/common/utility.h" #include "common/config/lds_json.h" #include "common/config/utility.h" @@ -50,7 +51,7 @@ void MainImpl::initialize(const envoy::config::bootstrap::v2::Bootstrap& bootstr ENVOY_LOG(info, "loading {} static secret(s)", secrets.size()); for (ssize_t i = 0; i < secrets.size(); i++) { ENVOY_LOG(debug, "static secret #{}: {}", i, secrets[i].name()); - server.secretManager().addOrUpdateSecret(secrets[i]); + server.secretManager().addOrUpdateSecret(EMPTY_STRING, secrets[i]); } cluster_manager_ = cluster_manager_factory.clusterManagerFromProto( diff --git a/source/server/connection_handler_impl.cc b/source/server/connection_handler_impl.cc index 854180cd4914d..e2dd241c35fcc 100644 --- a/source/server/connection_handler_impl.cc +++ b/source/server/connection_handler_impl.cc @@ -196,6 +196,14 @@ void ConnectionHandlerImpl::ActiveListener::newConnection(Network::ConnectionSoc } auto transport_socket = filter_chain->transportSocketFactory().createTransportSocket(); + if (!transport_socket) { + ENVOY_LOG_TO_LOGGER(parent_.logger_, debug, + "closing connection: transport socket was not created yet"); + // TODO(jaebong) update stats + socket->close(); + return; + } + Network::ConnectionPtr new_connection = parent_.dispatcher_.createServerConnection(std::move(socket), std::move(transport_socket)); new_connection->setBufferLimits(config_.perConnectionBufferLimitBytes()); diff --git a/source/server/server.cc b/source/server/server.cc index 058463a028afd..8db26c286f4e6 100644 --- a/source/server/server.cc +++ b/source/server/server.cc @@ -55,7 +55,7 @@ InstanceImpl::InstanceImpl(Options& options, Network::Address::InstanceConstShar handler_(new ConnectionHandlerImpl(ENVOY_LOGGER(), *dispatcher_)), random_generator_(std::move(random_generator)), listener_component_factory_(*this), worker_factory_(thread_local_, *api_, hooks), - secret_manager_(new Secret::SecretManagerImpl()), + secret_manager_(new Secret::SecretManagerImpl(*this)), dns_resolver_(dispatcher_->createDnsResolver({})), access_log_manager_(*api_, *dispatcher_, access_log_lock, store), terminated_(false) { @@ -246,6 +246,9 @@ void InstanceImpl::initialize(Options& options, loadServerFlags(initial_config.flagsPath()); + // Shared storage of secrets from SDS + secret_manager_.reset(new Secret::SecretManagerImpl(*this)); + // Workers get created first so they register for thread local updates. listener_manager_.reset( new ListenerManagerImpl(*this, listener_component_factory_, worker_factory_)); @@ -262,7 +265,7 @@ void InstanceImpl::initialize(Options& options, runtime_loader_ = component_factory.createRuntime(*this, initial_config); // Once we have runtime we can initialize the SSL context manager. - ssl_context_manager_.reset(new Ssl::ContextManagerImpl(*runtime_loader_)); + ssl_context_manager_.reset(new Ssl::ContextManagerImpl(*runtime_loader_, *secret_manager_)); cluster_manager_factory_.reset(new Upstream::ProdClusterManagerFactory( runtime(), stats(), threadLocal(), random(), dnsResolver(), sslContextManager(), dispatcher(), diff --git a/source/server/server.h b/source/server/server.h index 16f47450e08e9..8984b07364dcd 100644 --- a/source/server/server.h +++ b/source/server/server.h @@ -202,8 +202,8 @@ class InstanceImpl : Logger::Loggable, public Instance { std::unique_ptr ssl_context_manager_; ProdListenerComponentFactory listener_component_factory_; ProdWorkerFactory worker_factory_; - std::unique_ptr listener_manager_; std::unique_ptr secret_manager_; + std::unique_ptr listener_manager_; std::unique_ptr config_; Network::DnsResolverSharedPtr dns_resolver_; Event::TimerPtr stat_flush_timer_; diff --git a/test/common/grpc/grpc_client_integration_test_harness.h b/test/common/grpc/grpc_client_integration_test_harness.h index f6c9c3fa152c6..3af6363db20ec 100644 --- a/test/common/grpc/grpc_client_integration_test_harness.h +++ b/test/common/grpc/grpc_client_integration_test_harness.h @@ -11,7 +11,7 @@ #include "test/integration/fake_upstream.h" #include "test/mocks/grpc/mocks.h" #include "test/mocks/local_info/mocks.h" -#include "test/mocks/secret/mocks.h" +#include "test/mocks/server/mocks.h" #include "test/mocks/tracing/mocks.h" #include "test/mocks/upstream/mocks.h" #include "test/proto/helloworld.pb.h" @@ -444,10 +444,9 @@ class GrpcSslClientIntegrationTest : public GrpcClientIntegrationTest { tls_cert->mutable_private_key()->set_filename( TestEnvironment::runfilesPath("test/config/integration/certs/clientkey.pem")); } - Ssl::ClientContextConfigImpl cfg(tls_context, secret_manager_); - - mock_cluster_info_->transport_socket_factory_ = - std::make_unique(cfg, context_manager_, *stats_store_); + mock_cluster_info_->transport_socket_factory_ = std::make_unique( + std::make_unique(tls_context, server_.secretManager()), + context_manager_, *stats_store_); ON_CALL(*mock_cluster_info_, transportSocketFactory()) .WillByDefault(ReturnRef(*mock_cluster_info_->transport_socket_factory_)); async_client_transport_socket_ = @@ -474,16 +473,15 @@ class GrpcSslClientIntegrationTest : public GrpcClientIntegrationTest { TestEnvironment::runfilesPath("test/config/integration/certs/cacert.pem")); } - Ssl::ServerContextConfigImpl cfg(tls_context, secret_manager_); - static Stats::Scope* upstream_stats_store = new Stats::IsolatedStoreImpl(); return std::make_unique( - cfg, context_manager_, *upstream_stats_store, std::vector{}); + std::make_unique(tls_context, server_.secretManager()), + context_manager_, *upstream_stats_store, std::vector{}); } bool use_client_cert_{}; - Secret::MockSecretManager secret_manager_; - Ssl::ContextManagerImpl context_manager_{runtime_}; + Server::MockInstance server_; + Ssl::ContextManagerImpl context_manager_{runtime_, server_.secretManager()}; }; } // namespace diff --git a/test/common/secret/BUILD b/test/common/secret/BUILD index c65489952a84e..98da0e9670554 100644 --- a/test/common/secret/BUILD +++ b/test/common/secret/BUILD @@ -15,7 +15,10 @@ envoy_cc_test( "//test/common/ssl/test_data:certs", ], deps = [ + "//source/common/event:dispatcher_includes", + "//source/common/event:dispatcher_lib", "//source/common/secret:secret_manager_impl_lib", + "//test/mocks/server:server_mocks", "//test/test_common:environment_lib", "//test/test_common:registry_lib", "//test/test_common:utility_lib", diff --git a/test/common/secret/secret_manager_impl_test.cc b/test/common/secret/secret_manager_impl_test.cc index 4c82c3deb8170..009e345276f28 100644 --- a/test/common/secret/secret_manager_impl_test.cc +++ b/test/common/secret/secret_manager_impl_test.cc @@ -3,8 +3,10 @@ #include "envoy/api/v2/auth/cert.pb.h" #include "envoy/common/exception.h" +#include "common/event/dispatcher_impl.h" #include "common/secret/secret_manager_impl.h" +#include "test/mocks/server/mocks.h" #include "test/test_common/environment.h" #include "test/test_common/utility.h" @@ -15,6 +17,27 @@ namespace Envoy { namespace Secret { namespace { +class MockServer : public Server::MockInstance { +public: + Event::Dispatcher& dispatcher() override { return dispatcher_; } + Init::Manager& initManager() { return initmanager_; } + +private: + class InitManager : public Init::Manager { + public: + void initialize(std::function callback); + void registerTarget(Init::Target&) override {} + }; + + Event::DispatcherImpl dispatcher_; + InitManager initmanager_; +}; + +class MockSecretCallback : public SecretCallbacks { +public: + MOCK_METHOD0(onAddOrUpdateSecret, void()); +}; + class SecretManagerImplTest : public testing::Test {}; TEST_F(SecretManagerImplTest, SecretLoadSuccess) { @@ -25,31 +48,74 @@ TEST_F(SecretManagerImplTest, SecretLoadSuccess) { name: "abc.com" tls_certificate: certificate_chain: - filename: "test/common/ssl/test_data/selfsigned_cert.pem" + filename: "{{ test_rundir }}/test/common/ssl/test_data/selfsigned_cert.pem" private_key: - filename: "test/common/ssl/test_data/selfsigned_key.pem" + filename: "{{ test_rundir }}/test/common/ssl/test_data/selfsigned_key.pem" )EOF"; - MessageUtil::loadFromYaml(yaml, secret_config); + MessageUtil::loadFromYaml(TestEnvironment::substitute(yaml), secret_config); - std::unique_ptr secret_manager(new SecretManagerImpl()); + Server::MockInstance server; - secret_manager->addOrUpdateSecret(secret_config); + server.secretManager().addOrUpdateSecret("", secret_config); - ASSERT_EQ(secret_manager->findTlsCertificate("undefined"), nullptr); + ASSERT_EQ(server.secretManager().findTlsCertificate("", "undefined"), nullptr); - ASSERT_NE(secret_manager->findTlsCertificate("abc.com"), nullptr); + ASSERT_NE(server.secretManager().findTlsCertificate("", "abc.com"), nullptr); - EXPECT_EQ( - TestEnvironment::readFileToStringForTest("test/common/ssl/test_data/selfsigned_cert.pem"), - secret_manager->findTlsCertificate("abc.com")->certificateChain()); + const std::string cert_pem = "{{ test_rundir }}/test/common/ssl/test_data/selfsigned_cert.pem"; + EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(cert_pem)), + server.secretManager().findTlsCertificate("", "abc.com")->certificateChain()); + + const std::string key_pem = "{{ test_rundir }}/test/common/ssl/test_data/selfsigned_key.pem"; + EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(key_pem)), + server.secretManager().findTlsCertificate("", "abc.com")->privateKey()); +} + +TEST_F(SecretManagerImplTest, SdsDynamicSecretUpdateSuccess) { + envoy::api::v2::core::ConfigSource config_source; + envoy::api::v2::auth::Secret secret_config; + std::string yaml = + R"EOF( + name: "abc.com" + tls_certificate: + certificate_chain: + filename: "{{ test_rundir }}/test/common/ssl/test_data/selfsigned_cert.pem" + private_key: + filename: "{{ test_rundir }}/test/common/ssl/test_data/selfsigned_key.pem" + )EOF"; + + MessageUtil::loadFromYaml(TestEnvironment::substitute(yaml), secret_config); + + MockServer server; + + std::unique_ptr secret_callback( + new ::testing::NiceMock()); + EXPECT_CALL(*secret_callback.get(), onAddOrUpdateSecret()); + std::string config_source_hash = + server.secretManager().addOrUpdateSdsService(config_source, "abc_config"); + + server.secretManager().registerTlsCertificateConfigCallbacks(config_source_hash, "abc.com", + secret_callback.get()); + server.secretManager().addOrUpdateSecret(config_source_hash, secret_config); + + server.dispatcher().run(Event::Dispatcher::RunType::Block); + + ASSERT_EQ(server.secretManager().findTlsCertificate(config_source_hash, "undefined"), nullptr); + + const std::string cert_pem = "{{ test_rundir }}/test/common/ssl/test_data/selfsigned_cert.pem"; EXPECT_EQ( - TestEnvironment::readFileToStringForTest("test/common/ssl/test_data/selfsigned_key.pem"), - secret_manager->findTlsCertificate("abc.com")->privateKey()); + TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(cert_pem)), + server.secretManager().findTlsCertificate(config_source_hash, "abc.com")->certificateChain()); + + const std::string key_pem = "{{ test_rundir }}/test/common/ssl/test_data/selfsigned_key.pem"; + EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(key_pem)), + server.secretManager().findTlsCertificate(config_source_hash, "abc.com")->privateKey()); } TEST_F(SecretManagerImplTest, NotImplementedException) { + envoy::api::v2::core::ConfigSource config_source; envoy::api::v2::auth::Secret secret_config; std::string yaml = @@ -62,10 +128,14 @@ name: "abc.com" MessageUtil::loadFromYaml(yaml, secret_config); - std::unique_ptr secret_manager(new SecretManagerImpl()); + MockServer server; + std::unique_ptr secret_manager(new SecretManagerImpl(server)); - EXPECT_THROW_WITH_MESSAGE(secret_manager->addOrUpdateSecret(secret_config), EnvoyException, - "Secret type not implemented"); + std::string config_source_hash = + server.secretManager().addOrUpdateSdsService(config_source, "abc_config"); + EXPECT_THROW_WITH_MESSAGE( + server.secretManager().addOrUpdateSecret(config_source_hash, secret_config), EnvoyException, + "Secret type not implemented"); } } // namespace diff --git a/test/common/ssl/BUILD b/test/common/ssl/BUILD index 8a9265f691da3..746c33eacc62e 100644 --- a/test/common/ssl/BUILD +++ b/test/common/ssl/BUILD @@ -62,6 +62,7 @@ envoy_cc_test( "//source/common/stats:stats_lib", "//test/mocks/runtime:runtime_mocks", "//test/mocks/secret:secret_mocks", + "//test/mocks/server:server_mocks", "//test/test_common:environment_lib", ], ) diff --git a/test/common/ssl/context_impl_test.cc b/test/common/ssl/context_impl_test.cc index 5f6002a409871..54740da43e015 100644 --- a/test/common/ssl/context_impl_test.cc +++ b/test/common/ssl/context_impl_test.cc @@ -10,6 +10,7 @@ #include "test/common/ssl/ssl_certs_test.h" #include "test/mocks/runtime/mocks.h" #include "test/mocks/secret/mocks.h" +#include "test/mocks/server/mocks.h" #include "test/test_common/environment.h" #include "test/test_common/utility.h" @@ -79,9 +80,9 @@ TEST_F(SslContextImplTest, TestCipherSuites) { )EOF"; Json::ObjectSharedPtr loader = TestEnvironment::jsonLoadFromString(json); - ClientContextConfigImpl cfg(*loader, secret_manager_); + ClientContextConfigImpl cfg(*loader, server_.secretManager()); Runtime::MockLoader runtime; - ContextManagerImpl manager(runtime); + ContextManagerImpl manager(runtime, server_.secretManager()); Stats::IsolatedStoreImpl store; EXPECT_THROW(manager.createSslClientContext(store, cfg), EnvoyException); } @@ -95,11 +96,11 @@ TEST_F(SslContextImplTest, TestExpiringCert) { )EOF"; Json::ObjectSharedPtr loader = TestEnvironment::jsonLoadFromString(json); - ClientContextConfigImpl cfg(*loader, secret_manager_); + ClientContextConfigImpl cfg(*loader, server_.secretManager()); Runtime::MockLoader runtime; - ContextManagerImpl manager(runtime); + ContextManagerImpl manager(runtime, server_.secretManager()); Stats::IsolatedStoreImpl store; - ClientContextPtr context(manager.createSslClientContext(store, cfg)); + ClientContextSharedPtr context(manager.createSslClientContext(store, cfg)); // This is a total hack, but right now we generate the cert and it expires in 15 days only in the // first second that it's valid. This can become invalid and then cause slower tests to fail. @@ -118,11 +119,11 @@ TEST_F(SslContextImplTest, TestExpiredCert) { )EOF"; Json::ObjectSharedPtr loader = TestEnvironment::jsonLoadFromString(json); - ClientContextConfigImpl cfg(*loader, secret_manager_); + ClientContextConfigImpl cfg(*loader, server_.secretManager()); Runtime::MockLoader runtime; - ContextManagerImpl manager(runtime); + ContextManagerImpl manager(runtime, server_.secretManager()); Stats::IsolatedStoreImpl store; - ClientContextPtr context(manager.createSslClientContext(store, cfg)); + ClientContextSharedPtr context(manager.createSslClientContext(store, cfg)); EXPECT_EQ(0U, context->daysUntilFirstCertExpires()); } @@ -136,12 +137,12 @@ TEST_F(SslContextImplTest, TestGetCertInformation) { )EOF"; Json::ObjectSharedPtr loader = TestEnvironment::jsonLoadFromString(json); - ClientContextConfigImpl cfg(*loader, secret_manager_); + ClientContextConfigImpl cfg(*loader, server_.secretManager()); Runtime::MockLoader runtime; - ContextManagerImpl manager(runtime); + ContextManagerImpl manager(runtime, server_.secretManager()); Stats::IsolatedStoreImpl store; - ClientContextPtr context(manager.createSslClientContext(store, cfg)); + ClientContextSharedPtr context(manager.createSslClientContext(store, cfg)); // This is similar to the hack above, but right now we generate the ca_cert and it expires in 15 // days only in the first second that it's valid. We will partially match for up until Days until // Expiration: 1. @@ -162,11 +163,11 @@ TEST_F(SslContextImplTest, TestGetCertInformation) { TEST_F(SslContextImplTest, TestNoCert) { Json::ObjectSharedPtr loader = TestEnvironment::jsonLoadFromString("{}"); - ClientContextConfigImpl cfg(*loader, secret_manager_); + ClientContextConfigImpl cfg(*loader, server_.secretManager()); Runtime::MockLoader runtime; - ContextManagerImpl manager(runtime); + ContextManagerImpl manager(runtime, server_.secretManager()); Stats::IsolatedStoreImpl store; - ClientContextPtr context(manager.createSslClientContext(store, cfg)); + ClientContextSharedPtr context(manager.createSslClientContext(store, cfg)); EXPECT_EQ("", context->getCaCertInformation()); EXPECT_EQ("", context->getCertChainInformation()); } @@ -175,14 +176,15 @@ class SslServerContextImplTicketTest : public SslContextImplTest { public: static void loadConfig(ServerContextConfigImpl& cfg) { Runtime::MockLoader runtime; - Secret::MockSecretManager secret_manager; - ContextManagerImpl manager(runtime); + Server::MockInstance server; + ContextManagerImpl manager(runtime, server.secretManager()); Stats::IsolatedStoreImpl store; - ServerContextPtr server_ctx( + ServerContextSharedPtr server_ctx( manager.createSslServerContext(store, cfg, std::vector{})); } static void loadConfigV2(envoy::api::v2::auth::DownstreamTlsContext& cfg) { + Server::MockInstance server; // Must add a certificate for the config to be considered valid. envoy::api::v2::auth::TlsCertificate* server_cert = cfg.mutable_common_tls_context()->add_tls_certificates(); @@ -191,15 +193,14 @@ class SslServerContextImplTicketTest : public SslContextImplTest { server_cert->mutable_private_key()->set_filename( TestEnvironment::substitute("{{ test_tmpdir }}/unittestkey.pem")); - Secret::MockSecretManager secret_manager; - ServerContextConfigImpl server_context_config(cfg, secret_manager); + ServerContextConfigImpl server_context_config(cfg, server.secretManager()); loadConfig(server_context_config); } static void loadConfigJson(const std::string& json) { + Server::MockInstance server; Json::ObjectSharedPtr loader = TestEnvironment::jsonLoadFromString(json); - Secret::MockSecretManager secret_manager; - ServerContextConfigImpl cfg(*loader, secret_manager); + ServerContextConfigImpl cfg(*loader, server.secretManager()); loadConfig(cfg); } }; @@ -356,30 +357,30 @@ class ClientContextConfigImplTest : public SslCertsTest {}; // Validate that empty SNI (according to C string rules) fails config validation. TEST(ClientContextConfigImplTest, EmptyServerNameIndication) { envoy::api::v2::auth::UpstreamTlsContext tls_context; - Secret::MockSecretManager secret_manager; + Server::MockInstance server; tls_context.set_sni(std::string("\000", 1)); EXPECT_THROW_WITH_MESSAGE( - ClientContextConfigImpl client_context_config(tls_context, secret_manager), EnvoyException, - "SNI names containing NULL-byte are not allowed"); + ClientContextConfigImpl client_context_config(tls_context, server.secretManager()), + EnvoyException, "SNI names containing NULL-byte are not allowed"); tls_context.set_sni(std::string("a\000b", 3)); EXPECT_THROW_WITH_MESSAGE( - ClientContextConfigImpl client_context_config(tls_context, secret_manager), EnvoyException, - "SNI names containing NULL-byte are not allowed"); + ClientContextConfigImpl client_context_config(tls_context, server.secretManager()), + EnvoyException, "SNI names containing NULL-byte are not allowed"); } // Validate that values other than a hex-encoded SHA-256 fail config validation. TEST(ClientContextConfigImplTest, InvalidCertificateHash) { envoy::api::v2::auth::UpstreamTlsContext tls_context; - Secret::MockSecretManager secret_manager; + Server::MockInstance server; tls_context.mutable_common_tls_context() ->mutable_validation_context() // This is valid hex-encoded string, but it doesn't represent SHA-256 (80 vs 64 chars). ->add_verify_certificate_hash("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - ClientContextConfigImpl client_context_config(tls_context, secret_manager); + ClientContextConfigImpl client_context_config(tls_context, server.secretManager()); Runtime::MockLoader runtime; - ContextManagerImpl manager(runtime); + ContextManagerImpl manager(runtime, server.secretManager()); Stats::IsolatedStoreImpl store; EXPECT_THROW_WITH_REGEX(manager.createSslClientContext(store, client_context_config), EnvoyException, "Invalid hex-encoded SHA-256 .*"); @@ -388,14 +389,14 @@ TEST(ClientContextConfigImplTest, InvalidCertificateHash) { // Validate that values other than a base64-encoded SHA-256 fail config validation. TEST(ClientContextConfigImplTest, InvalidCertificateSpki) { envoy::api::v2::auth::UpstreamTlsContext tls_context; - Secret::MockSecretManager secret_manager; + Server::MockInstance server; tls_context.mutable_common_tls_context() ->mutable_validation_context() // Not a base64-encoded string. ->add_verify_certificate_spki("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); - ClientContextConfigImpl client_context_config(tls_context, secret_manager); + ClientContextConfigImpl client_context_config(tls_context, server.secretManager()); Runtime::MockLoader runtime; - ContextManagerImpl manager(runtime); + ContextManagerImpl manager(runtime, server.secretManager()); Stats::IsolatedStoreImpl store; EXPECT_THROW_WITH_REGEX(manager.createSslClientContext(store, client_context_config), EnvoyException, "Invalid base64-encoded SHA-256 .*"); @@ -405,12 +406,22 @@ TEST(ClientContextConfigImplTest, InvalidCertificateSpki) { // TODO(PiotrSikora): Support multiple TLS certificates. TEST(ClientContextConfigImplTest, MultipleTlsCertificates) { envoy::api::v2::auth::UpstreamTlsContext tls_context; - Secret::MockSecretManager secret_manager; + Server::MockInstance server; tls_context.mutable_common_tls_context()->add_tls_certificates(); tls_context.mutable_common_tls_context()->add_tls_certificates(); EXPECT_THROW_WITH_MESSAGE( - ClientContextConfigImpl client_context_config(tls_context, secret_manager), EnvoyException, - "Multiple TLS certificates are not supported for client contexts"); + ClientContextConfigImpl client_context_config(tls_context, server.secretManager()), + EnvoyException, "Multiple TLS certificates are not supported for client contexts"); +} + +TEST(ClientContextConfigImplTest, TlsCertificatesAndSdsConfig) { + envoy::api::v2::auth::UpstreamTlsContext tls_context; + Server::MockInstance server; + tls_context.mutable_common_tls_context()->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificate_sds_secret_configs(); + EXPECT_THROW_WITH_MESSAGE( + ClientContextConfigImpl client_context_config(tls_context, server.secretManager()), + EnvoyException, "Multiple TLS certificates are not supported for client contexts"); } TEST(ClientContextConfigImplTest, StaticTlsCertificates) { @@ -427,8 +438,8 @@ name: "abc.com" MessageUtil::loadFromYaml(yaml, secret_config); - std::unique_ptr secret_manager(new Secret::SecretManagerImpl()); - secret_manager->addOrUpdateSecret(secret_config); + Server::MockInstance server; + server.secretManager().addOrUpdateSecret("", secret_config); envoy::api::v2::auth::UpstreamTlsContext tls_context; tls_context.mutable_common_tls_context() @@ -436,7 +447,7 @@ name: "abc.com" ->Add() ->set_name("abc.com"); - ClientContextConfigImpl client_context_config(tls_context, *secret_manager.get()); + ClientContextConfigImpl client_context_config(tls_context, server.secretManager()); EXPECT_EQ( TestEnvironment::readFileToStringForTest("test/common/ssl/test_data/selfsigned_cert.pem"), @@ -460,9 +471,8 @@ name: "abc.com" MessageUtil::loadFromYaml(yaml, secret_config); - std::unique_ptr secret_manager(new Secret::SecretManagerImpl()); - - secret_manager->addOrUpdateSecret(secret_config); + Server::MockInstance server; + server.secretManager().addOrUpdateSecret("", secret_config); envoy::api::v2::auth::UpstreamTlsContext tls_context; tls_context.mutable_common_tls_context() @@ -471,8 +481,8 @@ name: "abc.com" ->set_name("missing"); EXPECT_THROW_WITH_MESSAGE( - ClientContextConfigImpl client_context_config(tls_context, *secret_manager.get()), - EnvoyException, "Static secret is not defined: missing"); + ClientContextConfigImpl client_context_config(tls_context, server.secretManager()), + EnvoyException, "Unknown static secret: : missing"); } // Multiple TLS certificates are not yet supported, but one is expected for @@ -480,27 +490,41 @@ name: "abc.com" // TODO(PiotrSikora): Support multiple TLS certificates. TEST(ServerContextConfigImplTest, MultipleTlsCertificates) { envoy::api::v2::auth::DownstreamTlsContext tls_context; - Secret::MockSecretManager secret_manager; + Server::MockInstance server; EXPECT_THROW_WITH_MESSAGE( - ServerContextConfigImpl client_context_config(tls_context, secret_manager), EnvoyException, - "A single TLS certificate is required for server contexts"); + ServerContextConfigImpl client_context_config(tls_context, server.secretManager()), + EnvoyException, "A single TLS certificate is required for server contexts"); + tls_context.mutable_common_tls_context()->add_tls_certificates(); tls_context.mutable_common_tls_context()->add_tls_certificates(); + EXPECT_THROW_WITH_MESSAGE( + ServerContextConfigImpl client_context_config(tls_context, server.secretManager()), + EnvoyException, "A single TLS certificate is required for server contexts"); +} + +TEST(ServerContextConfigImplTest, TlsCertificatesAndSdsConfig) { + Server::MockInstance server; + envoy::api::v2::auth::DownstreamTlsContext tls_context; + + EXPECT_THROW_WITH_MESSAGE( + ServerContextConfigImpl client_context_config(tls_context, server.secretManager()), + EnvoyException, "A single TLS certificate is required for server contexts"); tls_context.mutable_common_tls_context()->add_tls_certificates(); + tls_context.mutable_common_tls_context()->add_tls_certificate_sds_secret_configs(); EXPECT_THROW_WITH_MESSAGE( - ServerContextConfigImpl client_context_config(tls_context, secret_manager), EnvoyException, - "A single TLS certificate is required for server contexts"); + ServerContextConfigImpl client_context_config(tls_context, server.secretManager()), + EnvoyException, "A single TLS certificate is required for server contexts"); } // TlsCertificate messages must have a cert for servers. TEST(ServerContextImplTest, TlsCertificateNonEmpty) { envoy::api::v2::auth::DownstreamTlsContext tls_context; - Secret::MockSecretManager secret_manager; + Server::MockInstance server; tls_context.mutable_common_tls_context()->add_tls_certificates(); - ServerContextConfigImpl client_context_config(tls_context, secret_manager); + ServerContextConfigImpl client_context_config(tls_context, server.secretManager()); Runtime::MockLoader runtime; - ContextManagerImpl manager(runtime); + ContextManagerImpl manager(runtime, server.secretManager()); Stats::IsolatedStoreImpl store; - EXPECT_THROW_WITH_MESSAGE(ServerContextPtr server_ctx(manager.createSslServerContext( + EXPECT_THROW_WITH_MESSAGE(ServerContextSharedPtr server_ctx(manager.createSslServerContext( store, client_context_config, std::vector{})), EnvoyException, "Server TlsCertificates must have a certificate specified"); @@ -509,7 +533,7 @@ TEST(ServerContextImplTest, TlsCertificateNonEmpty) { // Cannot ignore certificate expiration without a trusted CA. TEST(ServerContextConfigImplTest, InvalidIgnoreCertsNoCA) { envoy::api::v2::auth::DownstreamTlsContext tls_context; - Secret::MockSecretManager secret_manager; + Server::MockInstance server; envoy::api::v2::auth::CertificateValidationContext* server_validation_ctx = tls_context.mutable_common_tls_context()->mutable_validation_context(); @@ -517,8 +541,8 @@ TEST(ServerContextConfigImplTest, InvalidIgnoreCertsNoCA) { server_validation_ctx->set_allow_expired_certificate(true); EXPECT_THROW_WITH_MESSAGE( - ServerContextConfigImpl server_context_config(tls_context, secret_manager), EnvoyException, - "Certificate validity period is always ignored without trusted CA"); + ServerContextConfigImpl server_context_config(tls_context, server.secretManager()), + EnvoyException, "Certificate validity period is always ignored without trusted CA"); envoy::api::v2::auth::TlsCertificate* server_cert = tls_context.mutable_common_tls_context()->add_tls_certificates(); @@ -529,19 +553,21 @@ TEST(ServerContextConfigImplTest, InvalidIgnoreCertsNoCA) { server_validation_ctx->set_allow_expired_certificate(false); - EXPECT_NO_THROW(ServerContextConfigImpl server_context_config(tls_context, secret_manager)); + EXPECT_NO_THROW( + ServerContextConfigImpl server_context_config(tls_context, server.secretManager())); server_validation_ctx->set_allow_expired_certificate(true); EXPECT_THROW_WITH_MESSAGE( - ServerContextConfigImpl server_context_config(tls_context, secret_manager), EnvoyException, - "Certificate validity period is always ignored without trusted CA"); + ServerContextConfigImpl server_context_config(tls_context, server.secretManager()), + EnvoyException, "Certificate validity period is always ignored without trusted CA"); // But once you add a trusted CA, you should be able to create the context. server_validation_ctx->mutable_trusted_ca()->set_filename( TestEnvironment::substitute("{{ test_rundir }}/test/common/ssl/test_data/ca_cert.pem")); - EXPECT_NO_THROW(ServerContextConfigImpl server_context_config(tls_context, secret_manager)); + EXPECT_NO_THROW( + ServerContextConfigImpl server_context_config(tls_context, server.secretManager())); } } // namespace Ssl diff --git a/test/common/ssl/ssl_certs_test.h b/test/common/ssl/ssl_certs_test.h index 2f09e019944a4..4cdbef6c791b5 100644 --- a/test/common/ssl/ssl_certs_test.h +++ b/test/common/ssl/ssl_certs_test.h @@ -1,6 +1,6 @@ #pragma once -#include "test/mocks/secret/mocks.h" +#include "test/mocks/server/mocks.h" #include "test/test_common/environment.h" #include "gtest/gtest.h" @@ -12,6 +12,6 @@ class SslCertsTest : public testing::Test { TestEnvironment::exec({TestEnvironment::runfilesPath("test/common/ssl/gen_unittest_certs.sh")}); } - Secret::MockSecretManager secret_manager_; + Server::MockInstance server_; }; } // namespace Envoy diff --git a/test/common/ssl/ssl_socket_test.cc b/test/common/ssl/ssl_socket_test.cc index 095600f02cfe2..ed70061472056 100644 --- a/test/common/ssl/ssl_socket_test.cc +++ b/test/common/ssl/ssl_socket_test.cc @@ -52,13 +52,13 @@ void testUtil(const std::string& client_ctx_json, const std::string& server_ctx_ const Network::Address::IpVersion version) { Stats::IsolatedStoreImpl stats_store; Runtime::MockLoader runtime; - Secret::MockSecretManager secret_manager; + Server::MockInstance server; Json::ObjectSharedPtr server_ctx_loader = TestEnvironment::jsonLoadFromString(server_ctx_json); - ServerContextConfigImpl server_ctx_config(*server_ctx_loader, secret_manager); - ContextManagerImpl manager(runtime); - Ssl::ServerSslSocketFactory server_ssl_socket_factory(server_ctx_config, manager, stats_store, - std::vector{}); + ContextManagerImpl manager(runtime, server.secretManager()); + Ssl::ServerSslSocketFactory server_ssl_socket_factory( + std::make_unique(*server_ctx_loader, server.secretManager()), + manager, stats_store, std::vector{}); Event::DispatcherImpl dispatcher; Network::TcpListenSocket socket(Network::Test::getCanonicalLoopbackAddress(version), nullptr, @@ -68,8 +68,9 @@ void testUtil(const std::string& client_ctx_json, const std::string& server_ctx_ Network::ListenerPtr listener = dispatcher.createListener(socket, callbacks, true, false); Json::ObjectSharedPtr client_ctx_loader = TestEnvironment::jsonLoadFromString(client_ctx_json); - ClientContextConfigImpl client_ctx_config(*client_ctx_loader, secret_manager); - Ssl::ClientSslSocketFactory client_ssl_socket_factory(client_ctx_config, manager, stats_store); + Ssl::ClientSslSocketFactory client_ssl_socket_factory( + std::make_unique(*client_ctx_loader, server.secretManager()), + manager, stats_store); Network::ClientConnectionPtr client_connection = dispatcher.createClientConnection( socket.localAddress(), Network::Address::InstanceConstSharedPtr(), client_ssl_socket_factory.createTransportSocket(), nullptr); @@ -145,8 +146,8 @@ const std::string testUtilV2(const envoy::api::v2::Listener& server_proto, const Network::Address::IpVersion version) { Stats::IsolatedStoreImpl stats_store; Runtime::MockLoader runtime; - Secret::MockSecretManager secret_manager; - ContextManagerImpl manager(runtime); + Server::MockInstance server; + ContextManagerImpl manager(runtime, server.secretManager()); std::string new_session = EMPTY_STRING; // SNI-based selection logic isn't happening in Ssl::SslSocket anymore. @@ -154,9 +155,10 @@ const std::string testUtilV2(const envoy::api::v2::Listener& server_proto, const auto& filter_chain = server_proto.filter_chains(0); std::vector server_names(filter_chain.filter_chain_match().server_names().begin(), filter_chain.filter_chain_match().server_names().end()); - Ssl::ServerContextConfigImpl server_ctx_config(filter_chain.tls_context(), secret_manager); - Ssl::ServerSslSocketFactory server_ssl_socket_factory(server_ctx_config, manager, stats_store, - server_names); + Ssl::ServerSslSocketFactory server_ssl_socket_factory( + std::make_unique(filter_chain.tls_context(), + server.secretManager()), + manager, stats_store, server_names); Event::DispatcherImpl dispatcher; Network::TcpListenSocket socket(Network::Test::getCanonicalLoopbackAddress(version), nullptr, @@ -165,9 +167,12 @@ const std::string testUtilV2(const envoy::api::v2::Listener& server_proto, Network::MockConnectionHandler connection_handler; Network::ListenerPtr listener = dispatcher.createListener(socket, callbacks, true, false); - ClientContextConfigImpl client_ctx_config(client_ctx_proto, secret_manager); - ClientSslSocketFactory client_ssl_socket_factory(client_ctx_config, manager, stats_store); - ClientContextPtr client_ctx(manager.createSslClientContext(stats_store, client_ctx_config)); + ClientSslSocketFactory client_ssl_socket_factory( + std::make_unique(client_ctx_proto, server.secretManager()), manager, + stats_store); + ClientContextSharedPtr client_ctx(manager.createSslClientContext( + stats_store, + *std::make_unique(client_ctx_proto, server.secretManager()).get())); Network::ClientConnectionPtr client_connection = dispatcher.createClientConnection( socket.localAddress(), Network::Address::InstanceConstSharedPtr(), client_ssl_socket_factory.createTransportSocket(), nullptr); @@ -1516,10 +1521,10 @@ TEST_P(SslSocketTest, FlushCloseDuringHandshake) { )EOF"; Json::ObjectSharedPtr server_ctx_loader = TestEnvironment::jsonLoadFromString(server_ctx_json); - ServerContextConfigImpl server_ctx_config(*server_ctx_loader, secret_manager_); - ContextManagerImpl manager(runtime); - Ssl::ServerSslSocketFactory server_ssl_socket_factory(server_ctx_config, manager, stats_store, - std::vector{}); + ContextManagerImpl manager(runtime, server_.secretManager()); + Ssl::ServerSslSocketFactory server_ssl_socket_factory( + std::make_unique(*server_ctx_loader, server_.secretManager()), + manager, stats_store, std::vector{}); Event::DispatcherImpl dispatcher; Network::TcpListenSocket socket(Network::Test::getCanonicalLoopbackAddress(GetParam()), nullptr, @@ -1574,10 +1579,11 @@ TEST_P(SslSocketTest, HalfClose) { )EOF"; Json::ObjectSharedPtr server_ctx_loader = TestEnvironment::jsonLoadFromString(server_ctx_json); - ServerContextConfigImpl server_ctx_config(*server_ctx_loader, secret_manager_); - ContextManagerImpl manager(runtime); - Ssl::ServerSslSocketFactory server_ssl_socket_factory(server_ctx_config, manager, stats_store, - std::vector{}); + ContextManagerImpl manager(runtime, server_.secretManager()); + + Ssl::ServerSslSocketFactory server_ssl_socket_factory( + std::make_unique(*server_ctx_loader, server_.secretManager()), + manager, stats_store, std::vector{}); Event::DispatcherImpl dispatcher; Network::TcpListenSocket socket(Network::Test::getCanonicalLoopbackAddress(GetParam()), nullptr, @@ -1595,8 +1601,9 @@ TEST_P(SslSocketTest, HalfClose) { )EOF"; Json::ObjectSharedPtr client_ctx_loader = TestEnvironment::jsonLoadFromString(client_ctx_json); - ClientContextConfigImpl client_ctx_config(*client_ctx_loader, secret_manager_); - ClientSslSocketFactory client_ssl_socket_factory(client_ctx_config, manager, stats_store); + ClientSslSocketFactory client_ssl_socket_factory( + std::make_unique(*client_ctx_loader, server_.secretManager()), + manager, stats_store); Network::ClientConnectionPtr client_connection = dispatcher.createClientConnection( socket.localAddress(), Network::Address::InstanceConstSharedPtr(), client_ssl_socket_factory.createTransportSocket(), nullptr); @@ -1647,7 +1654,7 @@ TEST_P(SslSocketTest, HalfClose) { TEST_P(SslSocketTest, ClientAuthMultipleCAs) { Stats::IsolatedStoreImpl stats_store; Runtime::MockLoader runtime; - Secret::MockSecretManager secret_manager; + Server::MockInstance server; std::string server_ctx_json = R"EOF( { @@ -1658,10 +1665,10 @@ TEST_P(SslSocketTest, ClientAuthMultipleCAs) { )EOF"; Json::ObjectSharedPtr server_ctx_loader = TestEnvironment::jsonLoadFromString(server_ctx_json); - ServerContextConfigImpl server_ctx_config(*server_ctx_loader, secret_manager); - ContextManagerImpl manager(runtime); - Ssl::ServerSslSocketFactory server_ssl_socket_factory(server_ctx_config, manager, stats_store, - std::vector{}); + ContextManagerImpl manager(runtime, server_.secretManager()); + Ssl::ServerSslSocketFactory server_ssl_socket_factory( + std::make_unique(*server_ctx_loader, server_.secretManager()), + manager, stats_store, std::vector{}); Event::DispatcherImpl dispatcher; Network::TcpListenSocket socket(Network::Test::getCanonicalLoopbackAddress(GetParam()), nullptr, @@ -1678,8 +1685,9 @@ TEST_P(SslSocketTest, ClientAuthMultipleCAs) { )EOF"; Json::ObjectSharedPtr client_ctx_loader = TestEnvironment::jsonLoadFromString(client_ctx_json); - ClientContextConfigImpl client_ctx_config(*client_ctx_loader, secret_manager); - ClientSslSocketFactory ssl_socket_factory(client_ctx_config, manager, stats_store); + ClientSslSocketFactory ssl_socket_factory( + std::make_unique(*client_ctx_loader, server_.secretManager()), + manager, stats_store); Network::ClientConnectionPtr client_connection = dispatcher.createClientConnection( socket.localAddress(), Network::Address::InstanceConstSharedPtr(), ssl_socket_factory.createTransportSocket(), nullptr); @@ -1735,17 +1743,17 @@ void testTicketSessionResumption(const std::string& server_ctx_json1, const Network::Address::IpVersion ip_version) { Stats::IsolatedStoreImpl stats_store; Runtime::MockLoader runtime; - Secret::MockSecretManager secret_manager; - ContextManagerImpl manager(runtime); + Server::MockInstance server; + ContextManagerImpl manager(runtime, server.secretManager()); Json::ObjectSharedPtr server_ctx_loader1 = TestEnvironment::jsonLoadFromString(server_ctx_json1); Json::ObjectSharedPtr server_ctx_loader2 = TestEnvironment::jsonLoadFromString(server_ctx_json2); - ServerContextConfigImpl server_ctx_config1(*server_ctx_loader1, secret_manager); - ServerContextConfigImpl server_ctx_config2(*server_ctx_loader2, secret_manager); - Ssl::ServerSslSocketFactory server_ssl_socket_factory1(server_ctx_config1, manager, stats_store, - server_names1); - Ssl::ServerSslSocketFactory server_ssl_socket_factory2(server_ctx_config2, manager, stats_store, - server_names2); + Ssl::ServerSslSocketFactory server_ssl_socket_factory1( + std::make_unique(*server_ctx_loader1, server.secretManager()), + manager, stats_store, server_names1); + Ssl::ServerSslSocketFactory server_ssl_socket_factory2( + std::make_unique(*server_ctx_loader2, server.secretManager()), + manager, stats_store, server_names2); Event::DispatcherImpl dispatcher; Network::TcpListenSocket socket1(Network::Test::getCanonicalLoopbackAddress(ip_version), nullptr, @@ -1758,8 +1766,9 @@ void testTicketSessionResumption(const std::string& server_ctx_json1, Network::ListenerPtr listener2 = dispatcher.createListener(socket2, callbacks, true, false); Json::ObjectSharedPtr client_ctx_loader = TestEnvironment::jsonLoadFromString(client_ctx_json); - ClientContextConfigImpl client_ctx_config(*client_ctx_loader, secret_manager); - ClientSslSocketFactory ssl_socket_factory(client_ctx_config, manager, stats_store); + ClientSslSocketFactory ssl_socket_factory( + std::make_unique(*client_ctx_loader, server.secretManager()), + manager, stats_store); Network::ClientConnectionPtr client_connection = dispatcher.createClientConnection( socket1.localAddress(), Network::Address::InstanceConstSharedPtr(), ssl_socket_factory.createTransportSocket(), nullptr); @@ -2098,14 +2107,14 @@ TEST_P(SslSocketTest, ClientAuthCrossListenerSessionResumption) { )EOF"; Json::ObjectSharedPtr server_ctx_loader = TestEnvironment::jsonLoadFromString(server_ctx_json); - ServerContextConfigImpl server_ctx_config(*server_ctx_loader, secret_manager_); Json::ObjectSharedPtr server2_ctx_loader = TestEnvironment::jsonLoadFromString(server2_ctx_json); - ServerContextConfigImpl server2_ctx_config(*server2_ctx_loader, secret_manager_); - ContextManagerImpl manager(runtime); - Ssl::ServerSslSocketFactory server_ssl_socket_factory(server_ctx_config, manager, stats_store, - std::vector{}); - Ssl::ServerSslSocketFactory server2_ssl_socket_factory(server2_ctx_config, manager, stats_store, - std::vector{}); + ContextManagerImpl manager(runtime, server_.secretManager()); + Ssl::ServerSslSocketFactory server_ssl_socket_factory( + std::make_unique(*server_ctx_loader, server_.secretManager()), + manager, stats_store, std::vector{}); + Ssl::ServerSslSocketFactory server2_ssl_socket_factory( + std::make_unique(*server2_ctx_loader, server_.secretManager()), + manager, stats_store, std::vector{}); Event::DispatcherImpl dispatcher; Network::TcpListenSocket socket(Network::Test::getCanonicalLoopbackAddress(GetParam()), nullptr, @@ -2125,8 +2134,9 @@ TEST_P(SslSocketTest, ClientAuthCrossListenerSessionResumption) { )EOF"; Json::ObjectSharedPtr client_ctx_loader = TestEnvironment::jsonLoadFromString(client_ctx_json); - ClientContextConfigImpl client_ctx_config(*client_ctx_loader, secret_manager_); - ClientSslSocketFactory ssl_socket_factory(client_ctx_config, manager, stats_store); + ClientSslSocketFactory ssl_socket_factory( + std::make_unique(*client_ctx_loader, server_.secretManager()), + manager, stats_store); Network::ClientConnectionPtr client_connection = dispatcher.createClientConnection( socket.localAddress(), Network::Address::InstanceConstSharedPtr(), ssl_socket_factory.createTransportSocket(), nullptr); @@ -2211,10 +2221,10 @@ TEST_P(SslSocketTest, SslError) { )EOF"; Json::ObjectSharedPtr server_ctx_loader = TestEnvironment::jsonLoadFromString(server_ctx_json); - ServerContextConfigImpl server_ctx_config(*server_ctx_loader, secret_manager_); - ContextManagerImpl manager(runtime); - Ssl::ServerSslSocketFactory server_ssl_socket_factory(server_ctx_config, manager, stats_store, - std::vector{}); + ContextManagerImpl manager(runtime, server_.secretManager()); + Ssl::ServerSslSocketFactory server_ssl_socket_factory( + std::make_unique(*server_ctx_loader, server_.secretManager()), + manager, stats_store, std::vector{}); Event::DispatcherImpl dispatcher; Network::TcpListenSocket socket(Network::Test::getCanonicalLoopbackAddress(GetParam()), nullptr, @@ -2533,18 +2543,18 @@ class SslReadBufferLimitTest : public SslCertsTest, public: void initialize() { server_ctx_loader_ = TestEnvironment::jsonLoadFromString(server_ctx_json_); - server_ctx_config_.reset(new ServerContextConfigImpl(*server_ctx_loader_, secret_manager_)); - manager_.reset(new ContextManagerImpl(runtime_)); + manager_.reset(new ContextManagerImpl(runtime_, server_.secretManager())); server_ssl_socket_factory_.reset(new ServerSslSocketFactory( - *server_ctx_config_, *manager_, stats_store_, std::vector{})); + std::make_unique(*server_ctx_loader_, server_.secretManager()), + *manager_, stats_store_, std::vector{})); listener_ = dispatcher_->createListener(socket_, listener_callbacks_, true, false); client_ctx_loader_ = TestEnvironment::jsonLoadFromString(client_ctx_json_); - client_ctx_config_.reset(new ClientContextConfigImpl(*client_ctx_loader_, secret_manager_)); - client_ssl_socket_factory_.reset( - new ClientSslSocketFactory(*client_ctx_config_, *manager_, stats_store_)); + client_ssl_socket_factory_.reset(new ClientSslSocketFactory( + std::make_unique(*client_ctx_loader_, server_.secretManager()), + *manager_, stats_store_)); client_connection_ = dispatcher_->createClientConnection( socket_.localAddress(), source_address_, client_ssl_socket_factory_->createTransportSocket(), nullptr); @@ -2708,13 +2718,11 @@ class SslReadBufferLimitTest : public SslCertsTest, )EOF"; Runtime::MockLoader runtime_; Json::ObjectSharedPtr server_ctx_loader_; - std::unique_ptr server_ctx_config_; std::unique_ptr manager_; Network::TransportSocketFactoryPtr server_ssl_socket_factory_; Network::ListenerPtr listener_; Json::ObjectSharedPtr client_ctx_loader_; - std::unique_ptr client_ctx_config_; - ClientContextPtr client_ctx_; + ClientContextSharedPtr client_ctx_; Network::TransportSocketFactoryPtr client_ssl_socket_factory_; Network::ClientConnectionPtr client_connection_; Network::ConnectionPtr server_connection_; diff --git a/test/common/upstream/cluster_manager_impl_test.cc b/test/common/upstream/cluster_manager_impl_test.cc index 28f7094b0ab08..2c989ac82ba05 100644 --- a/test/common/upstream/cluster_manager_impl_test.cc +++ b/test/common/upstream/cluster_manager_impl_test.cc @@ -109,7 +109,8 @@ class TestClusterManagerFactory : public ClusterManagerFactory { new NiceMock}; NiceMock runtime_; NiceMock random_; - Ssl::ContextManagerImpl ssl_context_manager_{runtime_}; + Server::MockInstance server_; + Ssl::ContextManagerImpl ssl_context_manager_{runtime_, server_.secretManager()}; NiceMock dispatcher_; LocalInfo::MockLocalInfo local_info_; Secret::MockSecretManager secret_manager_; diff --git a/test/integration/BUILD b/test/integration/BUILD index 2c4b907bdebd4..a32e8cea0b09e 100644 --- a/test/integration/BUILD +++ b/test/integration/BUILD @@ -283,6 +283,7 @@ envoy_cc_test_library( "//test/common/upstream:utility_lib", "//test/config:utility_lib", "//test/mocks/buffer:buffer_mocks", + "//test/mocks/server:server_mocks", "//test/mocks/upstream:upstream_mocks", "//test/test_common:environment_lib", "//test/test_common:network_utility_lib", diff --git a/test/integration/ads_integration_test.cc b/test/integration/ads_integration_test.cc index cd84b757900ef..2152642adbd8e 100644 --- a/test/integration/ads_integration_test.cc +++ b/test/integration/ads_integration_test.cc @@ -19,7 +19,7 @@ #include "test/integration/http_integration.h" #include "test/integration/utility.h" #include "test/mocks/runtime/mocks.h" -#include "test/mocks/secret/mocks.h" +#include "test/mocks/server/mocks.h" #include "test/test_common/network_utility.h" #include "test/test_common/utility.h" @@ -85,11 +85,11 @@ class AdsIntegrationTest : public HttpIntegrationTest, public Grpc::GrpcClientIn TestEnvironment::runfilesPath("test/config/integration/certs/upstreamcert.pem")); tls_cert->mutable_private_key()->set_filename( TestEnvironment::runfilesPath("test/config/integration/certs/upstreamkey.pem")); - Ssl::ServerContextConfigImpl cfg(tls_context, secret_manager_); static Stats::Scope* upstream_stats_store = new Stats::TestIsolatedStoreImpl(); return std::make_unique( - cfg, context_manager_, *upstream_stats_store, std::vector{}); + std::make_unique(tls_context, server_.secretManager()), + context_manager_, *upstream_stats_store, std::vector{}); } AssertionResult @@ -243,9 +243,9 @@ class AdsIntegrationTest : public HttpIntegrationTest, public Grpc::GrpcClientIn } } - Secret::MockSecretManager secret_manager_; + Server::MockInstance server_; Runtime::MockLoader runtime_; - Ssl::ContextManagerImpl context_manager_{runtime_}; + Ssl::ContextManagerImpl context_manager_{runtime_, server_.secretManager()}; FakeHttpConnectionPtr ads_connection_; FakeStreamPtr ads_stream_; }; diff --git a/test/integration/ssl_integration_test.cc b/test/integration/ssl_integration_test.cc index 2cbd941cfa9a4..99051051d7282 100644 --- a/test/integration/ssl_integration_test.cc +++ b/test/integration/ssl_integration_test.cc @@ -32,17 +32,13 @@ void SslIntegrationTest::initialize() { HttpIntegrationTest::initialize(); runtime_.reset(new NiceMock()); - context_manager_.reset(new ContextManagerImpl(*runtime_)); + context_manager_.reset(new ContextManagerImpl(*runtime_, server_.secretManager())); registerTestServerPorts({"http"}); - client_ssl_ctx_plain_ = - createClientSslTransportSocketFactory(false, false, *context_manager_, secret_manager_); - client_ssl_ctx_alpn_ = - createClientSslTransportSocketFactory(true, false, *context_manager_, secret_manager_); - client_ssl_ctx_san_ = - createClientSslTransportSocketFactory(false, true, *context_manager_, secret_manager_); - client_ssl_ctx_alpn_san_ = - createClientSslTransportSocketFactory(true, true, *context_manager_, secret_manager_); + client_ssl_ctx_plain_ = createClientSslTransportSocketFactory(false, false, *context_manager_); + client_ssl_ctx_alpn_ = createClientSslTransportSocketFactory(true, false, *context_manager_); + client_ssl_ctx_san_ = createClientSslTransportSocketFactory(false, true, *context_manager_); + client_ssl_ctx_alpn_san_ = createClientSslTransportSocketFactory(true, true, *context_manager_); } void SslIntegrationTest::TearDown() { @@ -50,10 +46,15 @@ void SslIntegrationTest::TearDown() { client_ssl_ctx_alpn_.reset(); client_ssl_ctx_san_.reset(); client_ssl_ctx_alpn_san_.reset(); - context_manager_.reset(); runtime_.reset(); } +SslIntegrationTest::~SslIntegrationTest() { + HttpIntegrationTest::cleanupUpstreamAndDownstream(); + codec_client_.reset(); + context_manager_.reset(); +} + Network::ClientConnectionPtr SslIntegrationTest::makeSslClientConnection(bool alpn, bool san) { Network::Address::InstanceConstSharedPtr address = getSslAddress(version_, lookupPort("http")); if (alpn) { diff --git a/test/integration/ssl_integration_test.h b/test/integration/ssl_integration_test.h index 26d21bab70533..6f04dc26c1a41 100644 --- a/test/integration/ssl_integration_test.h +++ b/test/integration/ssl_integration_test.h @@ -6,7 +6,7 @@ #include "test/integration/http_integration.h" #include "test/integration/server.h" #include "test/mocks/runtime/mocks.h" -#include "test/mocks/secret/mocks.h" +#include "test/mocks/server/mocks.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -20,6 +20,7 @@ class SslIntegrationTest : public HttpIntegrationTest, public testing::TestWithParam { public: SslIntegrationTest() : HttpIntegrationTest(Http::CodecClient::Type::HTTP1, GetParam()) {} + virtual ~SslIntegrationTest(); void initialize() override; @@ -32,7 +33,7 @@ class SslIntegrationTest : public HttpIntegrationTest, private: std::unique_ptr runtime_; std::unique_ptr context_manager_; - Secret::MockSecretManager secret_manager_; + Server::MockInstance server_; Network::TransportSocketFactoryPtr client_ssl_ctx_plain_; Network::TransportSocketFactoryPtr client_ssl_ctx_alpn_; diff --git a/test/integration/ssl_utility.cc b/test/integration/ssl_utility.cc index 9c3d1e773b06d..49f60b47b5594 100644 --- a/test/integration/ssl_utility.cc +++ b/test/integration/ssl_utility.cc @@ -7,6 +7,7 @@ #include "common/ssl/ssl_socket.h" #include "test/integration/server.h" +#include "test/mocks/server/mocks.h" #include "test/test_common/environment.h" #include "test/test_common/network_utility.h" @@ -14,8 +15,7 @@ namespace Envoy { namespace Ssl { Network::TransportSocketFactoryPtr -createClientSslTransportSocketFactory(bool alpn, bool san, ContextManager& context_manager, - Secret::SecretManager& secret_manager) { +createClientSslTransportSocketFactory(bool alpn, bool san, ContextManager& context_manager) { const std::string json_plain = R"EOF( { "ca_cert_file": "{{ test_rundir }}/test/config/integration/certs/cacert.pem", @@ -58,11 +58,13 @@ createClientSslTransportSocketFactory(bool alpn, bool san, ContextManager& conte } else { target = san ? json_san : json_plain; } + Server::MockInstance server; Json::ObjectSharedPtr loader = TestEnvironment::jsonLoadFromString(target); - ClientContextConfigImpl cfg(*loader, secret_manager); + ClientContextConfigImpl cfg(*loader, server.secretManager()); static auto* client_stats_store = new Stats::TestIsolatedStoreImpl(); - return Network::TransportSocketFactoryPtr{ - new Ssl::ClientSslSocketFactory(cfg, context_manager, *client_stats_store)}; + return Network::TransportSocketFactoryPtr{new Ssl::ClientSslSocketFactory( + std::make_unique(*loader, context_manager.secretManager()), + context_manager, *client_stats_store)}; } Network::Address::InstanceConstSharedPtr getSslAddress(const Network::Address::IpVersion& version, diff --git a/test/integration/ssl_utility.h b/test/integration/ssl_utility.h index d2ff42561bd44..c55e081a1dd46 100644 --- a/test/integration/ssl_utility.h +++ b/test/integration/ssl_utility.h @@ -9,8 +9,7 @@ namespace Envoy { namespace Ssl { Network::TransportSocketFactoryPtr -createClientSslTransportSocketFactory(bool alpn, bool san, ContextManager& context_manager, - Secret::SecretManager& secret_manager); +createClientSslTransportSocketFactory(bool alpn, bool san, ContextManager& context_manager); Network::Address::InstanceConstSharedPtr getSslAddress(const Network::Address::IpVersion& version, int port); diff --git a/test/integration/tcp_proxy_integration_test.cc b/test/integration/tcp_proxy_integration_test.cc index a7e7fecb9e3b1..fde955445b2b1 100644 --- a/test/integration/tcp_proxy_integration_test.cc +++ b/test/integration/tcp_proxy_integration_test.cc @@ -347,7 +347,7 @@ void TcpProxySslIntegrationTest::initialize() { config_helper_.addSslConfig(); TcpProxyIntegrationTest::initialize(); - context_manager_.reset(new Ssl::ContextManagerImpl(runtime_)); + context_manager_.reset(new Ssl::ContextManagerImpl(runtime_, server_.secretManager())); payload_reader_.reset(new WaitForPayloadReader(*dispatcher_)); } @@ -371,8 +371,7 @@ void TcpProxySslIntegrationTest::setupConnections() { // Set up the SSl client. Network::Address::InstanceConstSharedPtr address = Ssl::getSslAddress(version_, lookupPort("tcp_proxy")); - context_ = - Ssl::createClientSslTransportSocketFactory(false, false, *context_manager_, secret_manager_); + context_ = Ssl::createClientSslTransportSocketFactory(false, false, *context_manager_); ssl_client_ = dispatcher_->createClientConnection(address, Network::Address::InstanceConstSharedPtr(), context_->createTransportSocket(), nullptr); diff --git a/test/integration/tcp_proxy_integration_test.h b/test/integration/tcp_proxy_integration_test.h index 0532890980ba4..742e3c1428040 100644 --- a/test/integration/tcp_proxy_integration_test.h +++ b/test/integration/tcp_proxy_integration_test.h @@ -5,7 +5,7 @@ #include "test/integration/integration.h" #include "test/mocks/runtime/mocks.h" -#include "test/mocks/secret/mocks.h" +#include "test/mocks/server/mocks.h" #include "gtest/gtest.h" @@ -41,7 +41,7 @@ class TcpProxySslIntegrationTest : public TcpProxyIntegrationTest { ConnectionStatusCallbacks connect_callbacks_; MockWatermarkBuffer* client_write_buffer_; std::shared_ptr payload_reader_; - Secret::MockSecretManager secret_manager_; + Server::MockInstance server_; }; } // namespace diff --git a/test/integration/xfcc_integration_test.cc b/test/integration/xfcc_integration_test.cc index f0d4f70eda79d..0221db823d69b 100644 --- a/test/integration/xfcc_integration_test.cc +++ b/test/integration/xfcc_integration_test.cc @@ -12,6 +12,7 @@ #include "common/ssl/context_manager_impl.h" #include "common/ssl/ssl_socket.h" +#include "test/mocks/server/mocks.h" #include "test/test_common/network_utility.h" #include "test/test_common/printers.h" #include "test/test_common/utility.h" @@ -31,10 +32,15 @@ void XfccIntegrationTest::TearDown() { client_tls_ssl_ctx_.reset(); fake_upstream_connection_.reset(); fake_upstreams_.clear(); - context_manager_.reset(); runtime_.reset(); } +XfccIntegrationTest::~XfccIntegrationTest() { + HttpIntegrationTest::cleanupUpstreamAndDownstream(); + codec_client_.reset(); + context_manager_.reset(); +} + Network::TransportSocketFactoryPtr XfccIntegrationTest::createClientSslContext(bool mtls) { std::string json_tls = R"EOF( { @@ -58,10 +64,10 @@ Network::TransportSocketFactoryPtr XfccIntegrationTest::createClientSslContext(b target = json_tls; } Json::ObjectSharedPtr loader = TestEnvironment::jsonLoadFromString(target); - Ssl::ClientContextConfigImpl cfg(*loader, secret_manager_); static auto* client_stats_store = new Stats::TestIsolatedStoreImpl(); - return Network::TransportSocketFactoryPtr{ - new Ssl::ClientSslSocketFactory(cfg, *context_manager_, *client_stats_store)}; + return Network::TransportSocketFactoryPtr{new Ssl::ClientSslSocketFactory( + std::make_unique(*loader, server_.secretManager()), + *context_manager_, *client_stats_store)}; } Network::TransportSocketFactoryPtr XfccIntegrationTest::createUpstreamSslContext() { @@ -73,10 +79,10 @@ Network::TransportSocketFactoryPtr XfccIntegrationTest::createUpstreamSslContext )EOF"; Json::ObjectSharedPtr loader = TestEnvironment::jsonLoadFromString(json); - Ssl::ServerContextConfigImpl cfg(*loader, secret_manager_); static Stats::Scope* upstream_stats_store = new Stats::TestIsolatedStoreImpl(); return std::make_unique( - cfg, *context_manager_, *upstream_stats_store, std::vector{}); + std::make_unique(*loader, server_.secretManager()), + *context_manager_, *upstream_stats_store, std::vector{}); } Network::ClientConnectionPtr XfccIntegrationTest::makeClientConnection() { @@ -122,7 +128,7 @@ void XfccIntegrationTest::initialize() { } runtime_.reset(new NiceMock()); - context_manager_.reset(new Ssl::ContextManagerImpl(*runtime_)); + context_manager_.reset(new Ssl::ContextManagerImpl(*runtime_, server_.secretManager())); client_tls_ssl_ctx_ = createClientSslContext(false); client_mtls_ssl_ctx_ = createClientSslContext(true); HttpIntegrationTest::initialize(); diff --git a/test/integration/xfcc_integration_test.h b/test/integration/xfcc_integration_test.h index 3432313af7153..f3760dbe97ed1 100644 --- a/test/integration/xfcc_integration_test.h +++ b/test/integration/xfcc_integration_test.h @@ -6,7 +6,7 @@ #include "test/integration/http_integration.h" #include "test/integration/server.h" #include "test/mocks/runtime/mocks.h" -#include "test/mocks/secret/mocks.h" +#include "test/mocks/server/mocks.h" #include "gmock/gmock.h" #include "gtest/gtest.h" @@ -32,6 +32,7 @@ class XfccIntegrationTest : public HttpIntegrationTest, const std::string client_dns_san_ = "DNS=lyft.com;DNS=www.lyft.com"; XfccIntegrationTest() : HttpIntegrationTest(Http::CodecClient::Type::HTTP1, GetParam()) {} + virtual ~XfccIntegrationTest(); void initialize() override; void createUpstreams() override; @@ -56,7 +57,7 @@ class XfccIntegrationTest : public HttpIntegrationTest, Network::TransportSocketFactoryPtr client_tls_ssl_ctx_; Network::TransportSocketFactoryPtr client_mtls_ssl_ctx_; Network::TransportSocketFactoryPtr upstream_ssl_ctx_; - Secret::MockSecretManager secret_manager_; + Server::MockInstance server_; }; } // namespace Xfcc } // namespace Envoy diff --git a/test/mocks/secret/mocks.h b/test/mocks/secret/mocks.h index 1d111df74993c..0a523ae993ea1 100644 --- a/test/mocks/secret/mocks.h +++ b/test/mocks/secret/mocks.h @@ -14,8 +14,22 @@ class MockSecretManager : public SecretManager { MockSecretManager(); ~MockSecretManager(); - MOCK_METHOD1(addOrUpdateSecret, void(const envoy::api::v2::auth::Secret& secret)); - MOCK_CONST_METHOD1(findTlsCertificate, const Ssl::TlsCertificateConfig*(const std::string& name)); + MOCK_METHOD2(addOrUpdateSecret, void(const std::string& config_source_hash, + const envoy::api::v2::auth::Secret& secret)); + MOCK_CONST_METHOD2(findTlsCertificate, + Ssl::TlsCertificateConfig*(const std::string& config_source_hash, + const std::string& name)); + MOCK_METHOD2(addOrUpdateSdsService, + std::string(const envoy::api::v2::core::ConfigSource& config_source, + std::string config_name)); + + MOCK_METHOD3(registerTlsCertificateConfigCallbacks, + void(const std::string& config_source_hash, const std::string& secret_name, + SecretCallbacks* callback)); + + MOCK_METHOD3(unRegisterTlsCertificateConfigCallbacks, + void(const std::string& config_source_hash, const std::string& secret_name, + SecretCallbacks& callback)); }; } // namespace Secret diff --git a/test/mocks/server/mocks.cc b/test/mocks/server/mocks.cc index 72724db9b04d0..469e60539c749 100644 --- a/test/mocks/server/mocks.cc +++ b/test/mocks/server/mocks.cc @@ -107,7 +107,8 @@ MockWorker::MockWorker() { MockWorker::~MockWorker() {} MockInstance::MockInstance() - : secret_manager_(new Secret::SecretManagerImpl()), ssl_context_manager_(runtime_loader_), + : secret_manager_(new Secret::SecretManagerImpl(*this)), + ssl_context_manager_(runtime_loader_, *secret_manager_), singleton_manager_(new Singleton::ManagerImpl()) { ON_CALL(*this, threadLocal()).WillByDefault(ReturnRef(thread_local_)); ON_CALL(*this, stats()).WillByDefault(ReturnRef(stats_store_)); diff --git a/test/mocks/ssl/BUILD b/test/mocks/ssl/BUILD index 39330f6db3538..fd044e7726de2 100644 --- a/test/mocks/ssl/BUILD +++ b/test/mocks/ssl/BUILD @@ -13,6 +13,7 @@ envoy_cc_mock( srcs = ["mocks.cc"], hdrs = ["mocks.h"], deps = [ + "//include/envoy/secret:secret_manager_interface", "//include/envoy/ssl:connection_interface", "//include/envoy/ssl:context_config_interface", "//include/envoy/ssl:context_interface", diff --git a/test/mocks/ssl/mocks.h b/test/mocks/ssl/mocks.h index 60691fa397973..2e478c016bd0a 100644 --- a/test/mocks/ssl/mocks.h +++ b/test/mocks/ssl/mocks.h @@ -21,16 +21,31 @@ class MockContextManager : public ContextManager { MockContextManager(); ~MockContextManager(); - ClientContextPtr createSslClientContext(Stats::Scope& scope, - const ClientContextConfig& config) override { - return ClientContextPtr{createSslClientContext_(scope, config)}; + ClientContextSharedPtr createSslClientContext(Stats::Scope& scope, + const ClientContextConfig& config) override { + return ClientContextSharedPtr{createSslClientContext_(scope, config)}; } - ServerContextPtr createSslServerContext(Stats::Scope& scope, const ServerContextConfig& config, - const std::vector& server_names) override { - return ServerContextPtr{createSslServerContext_(scope, config, server_names)}; + ServerContextSharedPtr + createSslServerContext(Stats::Scope& scope, const ServerContextConfig& config, + const std::vector& server_names) override { + return ServerContextSharedPtr{createSslServerContext_(scope, config, server_names)}; } + ClientContextSharedPtr updateSslClientContext(const ClientContextSharedPtr, Stats::Scope& scope, + const ClientContextConfig& config) override { + return ClientContextSharedPtr{createSslClientContext_(scope, config)}; + } + + ServerContextSharedPtr + updateSslServerContext(const ServerContextSharedPtr, Stats::Scope& scope, + const ServerContextConfig& config, + const std::vector& server_names) override { + return ServerContextSharedPtr{createSslServerContext_(scope, config, server_names)}; + } + + Secret::SecretManager& secretManager() override { return secret_manager_; } + MOCK_METHOD2(createSslClientContext_, ClientContext*(Stats::Scope& scope, const ClientContextConfig& config)); MOCK_METHOD3(createSslServerContext_, @@ -38,6 +53,9 @@ class MockContextManager : public ContextManager { const std::vector& server_names)); MOCK_CONST_METHOD0(daysUntilFirstCertExpires, size_t()); MOCK_METHOD1(iterateContexts, void(std::function callback)); + MOCK_METHOD1(releaseContext, void(Context* context)); + + testing::NiceMock secret_manager_; }; class MockConnection : public Connection { diff --git a/test/server/config_validation/cluster_manager_test.cc b/test/server/config_validation/cluster_manager_test.cc index cafd2666a4de0..a24a890374594 100644 --- a/test/server/config_validation/cluster_manager_test.cc +++ b/test/server/config_validation/cluster_manager_test.cc @@ -28,7 +28,7 @@ TEST(ValidationClusterManagerTest, MockedMethods) { NiceMock random; Secret::MockSecretManager secret_manager; auto dns_resolver = std::make_shared>(); - Ssl::ContextManagerImpl ssl_context_manager{runtime}; + Ssl::ContextManagerImpl ssl_context_manager{runtime, secret_manager}; NiceMock dispatcher; LocalInfo::MockLocalInfo local_info; NiceMock admin; diff --git a/test/server/configuration_impl_test.cc b/test/server/configuration_impl_test.cc index bec9bb6d05670..8fad6ba6ade13 100644 --- a/test/server/configuration_impl_test.cc +++ b/test/server/configuration_impl_test.cc @@ -303,6 +303,54 @@ TEST_F(ConfigurationImplTest, StatsSinkWithNoName) { "Provided name for static registration lookup was empty."); } +TEST_F(ConfigurationImplTest, StaticSecretRead) { + std::string json = + R"EOF( + { + "listeners" : [ + { + "address": "tcp://127.0.0.1:1234", + "filters": [] + } + ], + "cluster_manager": { + "clusters": [] + }, + "admin": {"access_log_path": "/dev/null", "address": "tcp://1.2.3.4:5678"} + } + )EOF"; + + envoy::config::bootstrap::v2::Bootstrap bootstrap = TestUtility::parseBootstrapFromJson(json); + + auto secret_config = bootstrap.mutable_static_resources()->mutable_secrets()->Add(); + + std::string yaml = + R"EOF( + name: "abc.com" + tls_certificate: + certificate_chain: + filename: "{{ test_rundir }}/test/config/integration/certs/cacert.pem" + private_key: + filename: "{{ test_rundir }}/test/config/integration/certs/cakey.pem" + )EOF"; + + MessageUtil::loadFromYaml(TestEnvironment::substitute(yaml), *secret_config); + + MainImpl config; + config.initialize(bootstrap, server_, cluster_manager_factory_); + + auto secret = server_.secretManager().findTlsCertificate("", "abc.com"); + + ASSERT_NE(secret, nullptr); + + const std::string cert_pem = "{{ test_rundir }}/test/config/integration/certs/cacert.pem"; + EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(cert_pem)), + secret->certificateChain()); + const std::string key_pem = "{{ test_rundir }}/test/config/integration/certs/cakey.pem"; + EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(key_pem)), + secret->privateKey()); +} + } // namespace Configuration } // namespace Server } // namespace Envoy