diff --git a/include/envoy/secret/secret_manager.h b/include/envoy/secret/secret_manager.h index d7f9788741213..bf2f472ed444d 100644 --- a/include/envoy/secret/secret_manager.h +++ b/include/envoy/secret/secret_manager.h @@ -18,16 +18,32 @@ class SecretManager { 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; }; } // namespace Secret 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..e30630e438891 --- /dev/null +++ b/source/common/secret/sds_api.cc @@ -0,0 +1,65 @@ +#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", + // TODO(jaebong): replace next line with + // "envoy.service.discovery.v2.SecretDiscoveryService.StreamSecrets" to support streaming + // service + "envoy.service.discovery.v2.SecretDiscoveryService.FetchSecrets"); + + 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..d83c7ff3915d3 100644 --- a/source/common/secret/secret_manager_impl.cc +++ b/source/common/secret/secret_manager_impl.cc @@ -2,26 +2,52 @@ #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; + } 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; } } // namespace Secret diff --git a/source/common/secret/secret_manager_impl.h b/source/common/secret/secret_manager_impl.h index b9406754a8c45..16537202b58cd 100644 --- a/source/common/secret/secret_manager_impl.h +++ b/source/common/secret/secret_manager_impl.h @@ -1,22 +1,40 @@ #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; 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_; }; } // 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/context_config_impl.cc b/source/common/ssl/context_config_impl.cc index 374cd2945b50a..f3fd055903306 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,32 @@ 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(); +} + 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..04e39788712dd 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_; } @@ -66,6 +66,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/server/config_validation/server.cc b/source/server/config_validation/server.cc index 885c0b997a39a..b149b295174e0 100644 --- a/source/server/config_validation/server.cc +++ b/source/server/config_validation/server.cc @@ -78,7 +78,7 @@ 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()); + secret_manager_.reset(new Secret::SecretManagerImpl(*this)); ssl_context_manager_.reset(new Ssl::ContextManagerImpl(*runtime_loader_)); cluster_manager_factory_.reset(new Upstream::ValidationClusterManagerFactory( runtime(), stats(), threadLocal(), random(), dnsResolver(), sslContextManager(), dispatcher(), 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/server.cc b/source/server/server.cc index 058463a028afd..bc4a445d7a9e1 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) { diff --git a/test/common/grpc/grpc_client_integration_test_harness.h b/test/common/grpc/grpc_client_integration_test_harness.h index f6c9c3fa152c6..32759e6925eac 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,7 +444,7 @@ 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_); + Ssl::ClientContextConfigImpl cfg(tls_context, server_.secretManager()); mock_cluster_info_->transport_socket_factory_ = std::make_unique(cfg, context_manager_, *stats_store_); @@ -474,7 +474,7 @@ class GrpcSslClientIntegrationTest : public GrpcClientIntegrationTest { TestEnvironment::runfilesPath("test/config/integration/certs/cacert.pem")); } - Ssl::ServerContextConfigImpl cfg(tls_context, secret_manager_); + Ssl::ServerContextConfigImpl cfg(tls_context, server_.secretManager()); static Stats::Scope* upstream_stats_store = new Stats::IsolatedStoreImpl(); return std::make_unique( @@ -482,7 +482,7 @@ class GrpcSslClientIntegrationTest : public GrpcClientIntegrationTest { } bool use_client_cert_{}; - Secret::MockSecretManager secret_manager_; + Server::MockInstance server_; Ssl::ContextManagerImpl context_manager_{runtime_}; }; diff --git a/test/common/secret/BUILD b/test/common/secret/BUILD index c65489952a84e..b7f46ff0edb71 100644 --- a/test/common/secret/BUILD +++ b/test/common/secret/BUILD @@ -16,6 +16,7 @@ envoy_cc_test( ], deps = [ "//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 a692976c4c6e4..0a7be3fc86252 100644 --- a/test/common/secret/secret_manager_impl_test.cc +++ b/test/common/secret/secret_manager_impl_test.cc @@ -5,6 +5,7 @@ #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 +16,20 @@ namespace Envoy { namespace Secret { namespace { +class MockServer : public Server::MockInstance { +public: + Init::Manager& initManager() { return initmanager_; } + +private: + class InitManager : public Init::Manager { + public: + void initialize(std::function callback); + void registerTarget(Init::Target&) override {} + }; + + InitManager initmanager_; +}; + class SecretManagerImplTest : public testing::Test {}; TEST_F(SecretManagerImplTest, SecretLoadSuccess) { @@ -32,24 +47,60 @@ name: "abc.com" 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); const std::string cert_pem = "{{ test_rundir }}/test/common/ssl/test_data/selfsigned_cert.pem"; EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(cert_pem)), - secret_manager->findTlsCertificate("abc.com")->certificateChain()); + 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/common/ssl/test_data/selfsigned_cert.pem" + private_key: + filename: "test/common/ssl/test_data/selfsigned_key.pem" + )EOF"; + + MessageUtil::loadFromYaml(yaml, secret_config); + + MockServer server; + + std::string config_source_hash = + server.secretManager().addOrUpdateSdsService(config_source, "abc_config"); + + server.secretManager().addOrUpdateSecret(config_source_hash, secret_config); + + 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(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)), - secret_manager->findTlsCertificate("abc.com")->privateKey()); + 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 +113,14 @@ name: "abc.com" MessageUtil::loadFromYaml(TestEnvironment::substitute(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 de82f328a7570..a953a94bfe2a4 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,7 +80,7 @@ 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); Stats::IsolatedStoreImpl store; @@ -95,7 +96,7 @@ 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); Stats::IsolatedStoreImpl store; @@ -118,7 +119,7 @@ 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); Stats::IsolatedStoreImpl store; @@ -136,7 +137,7 @@ 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); Stats::IsolatedStoreImpl store; @@ -162,7 +163,7 @@ 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); Stats::IsolatedStoreImpl store; @@ -183,6 +184,7 @@ class SslServerContextImplTicketTest : public SslContextImplTest { } 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(); @@ -192,14 +194,15 @@ class SslServerContextImplTicketTest : public SslContextImplTest { 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,28 +359,28 @@ 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); Stats::IsolatedStoreImpl store; @@ -388,12 +391,12 @@ 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); Stats::IsolatedStoreImpl store; @@ -405,12 +408,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 +440,8 @@ name: "abc.com" MessageUtil::loadFromYaml(TestEnvironment::substitute(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 +449,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()); const std::string cert_pem = "{{ test_rundir }}/test/common/ssl/test_data/selfsigned_cert.pem"; EXPECT_EQ(TestEnvironment::readFileToStringForTest(TestEnvironment::substitute(cert_pem)), @@ -460,9 +473,8 @@ name: "abc.com" MessageUtil::loadFromYaml(TestEnvironment::substitute(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 +483,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,23 +492,37 @@ 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); Stats::IsolatedStoreImpl store; @@ -509,7 +535,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 +543,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 +555,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..6b172a4030590 100644 --- a/test/common/ssl/ssl_socket_test.cc +++ b/test/common/ssl/ssl_socket_test.cc @@ -52,10 +52,10 @@ 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); + ServerContextConfigImpl server_ctx_config(*server_ctx_loader, server.secretManager()); ContextManagerImpl manager(runtime); Ssl::ServerSslSocketFactory server_ssl_socket_factory(server_ctx_config, manager, stats_store, std::vector{}); @@ -68,7 +68,7 @@ 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); + ClientContextConfigImpl client_ctx_config(*client_ctx_loader, server.secretManager()); Ssl::ClientSslSocketFactory client_ssl_socket_factory(client_ctx_config, manager, stats_store); Network::ClientConnectionPtr client_connection = dispatcher.createClientConnection( socket.localAddress(), Network::Address::InstanceConstSharedPtr(), @@ -145,7 +145,7 @@ 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; + Server::MockInstance server; ContextManagerImpl manager(runtime); std::string new_session = EMPTY_STRING; @@ -154,7 +154,8 @@ 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::ServerContextConfigImpl server_ctx_config(filter_chain.tls_context(), + server.secretManager()); Ssl::ServerSslSocketFactory server_ssl_socket_factory(server_ctx_config, manager, stats_store, server_names); @@ -165,7 +166,7 @@ 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); + ClientContextConfigImpl client_ctx_config(client_ctx_proto, server.secretManager()); ClientSslSocketFactory client_ssl_socket_factory(client_ctx_config, manager, stats_store); ClientContextPtr client_ctx(manager.createSslClientContext(stats_store, client_ctx_config)); Network::ClientConnectionPtr client_connection = dispatcher.createClientConnection( @@ -1516,7 +1517,7 @@ TEST_P(SslSocketTest, FlushCloseDuringHandshake) { )EOF"; Json::ObjectSharedPtr server_ctx_loader = TestEnvironment::jsonLoadFromString(server_ctx_json); - ServerContextConfigImpl server_ctx_config(*server_ctx_loader, secret_manager_); + ServerContextConfigImpl server_ctx_config(*server_ctx_loader, server_.secretManager()); ContextManagerImpl manager(runtime); Ssl::ServerSslSocketFactory server_ssl_socket_factory(server_ctx_config, manager, stats_store, std::vector{}); @@ -1574,7 +1575,7 @@ TEST_P(SslSocketTest, HalfClose) { )EOF"; Json::ObjectSharedPtr server_ctx_loader = TestEnvironment::jsonLoadFromString(server_ctx_json); - ServerContextConfigImpl server_ctx_config(*server_ctx_loader, secret_manager_); + ServerContextConfigImpl server_ctx_config(*server_ctx_loader, server_.secretManager()); ContextManagerImpl manager(runtime); Ssl::ServerSslSocketFactory server_ssl_socket_factory(server_ctx_config, manager, stats_store, std::vector{}); @@ -1595,7 +1596,7 @@ TEST_P(SslSocketTest, HalfClose) { )EOF"; Json::ObjectSharedPtr client_ctx_loader = TestEnvironment::jsonLoadFromString(client_ctx_json); - ClientContextConfigImpl client_ctx_config(*client_ctx_loader, secret_manager_); + ClientContextConfigImpl client_ctx_config(*client_ctx_loader, server_.secretManager()); ClientSslSocketFactory client_ssl_socket_factory(client_ctx_config, manager, stats_store); Network::ClientConnectionPtr client_connection = dispatcher.createClientConnection( socket.localAddress(), Network::Address::InstanceConstSharedPtr(), @@ -1647,7 +1648,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,7 +1659,7 @@ TEST_P(SslSocketTest, ClientAuthMultipleCAs) { )EOF"; Json::ObjectSharedPtr server_ctx_loader = TestEnvironment::jsonLoadFromString(server_ctx_json); - ServerContextConfigImpl server_ctx_config(*server_ctx_loader, secret_manager); + ServerContextConfigImpl server_ctx_config(*server_ctx_loader, server.secretManager()); ContextManagerImpl manager(runtime); Ssl::ServerSslSocketFactory server_ssl_socket_factory(server_ctx_config, manager, stats_store, std::vector{}); @@ -1678,7 +1679,7 @@ TEST_P(SslSocketTest, ClientAuthMultipleCAs) { )EOF"; Json::ObjectSharedPtr client_ctx_loader = TestEnvironment::jsonLoadFromString(client_ctx_json); - ClientContextConfigImpl client_ctx_config(*client_ctx_loader, secret_manager); + ClientContextConfigImpl client_ctx_config(*client_ctx_loader, server.secretManager()); ClientSslSocketFactory ssl_socket_factory(client_ctx_config, manager, stats_store); Network::ClientConnectionPtr client_connection = dispatcher.createClientConnection( socket.localAddress(), Network::Address::InstanceConstSharedPtr(), @@ -1735,13 +1736,13 @@ 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; + Server::MockInstance server; ContextManagerImpl manager(runtime); 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); + ServerContextConfigImpl server_ctx_config1(*server_ctx_loader1, server.secretManager()); + ServerContextConfigImpl server_ctx_config2(*server_ctx_loader2, server.secretManager()); 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, @@ -1758,7 +1759,7 @@ 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); + ClientContextConfigImpl client_ctx_config(*client_ctx_loader, server.secretManager()); ClientSslSocketFactory ssl_socket_factory(client_ctx_config, manager, stats_store); Network::ClientConnectionPtr client_connection = dispatcher.createClientConnection( socket1.localAddress(), Network::Address::InstanceConstSharedPtr(), @@ -2098,9 +2099,9 @@ TEST_P(SslSocketTest, ClientAuthCrossListenerSessionResumption) { )EOF"; Json::ObjectSharedPtr server_ctx_loader = TestEnvironment::jsonLoadFromString(server_ctx_json); - ServerContextConfigImpl server_ctx_config(*server_ctx_loader, secret_manager_); + ServerContextConfigImpl server_ctx_config(*server_ctx_loader, server_.secretManager()); Json::ObjectSharedPtr server2_ctx_loader = TestEnvironment::jsonLoadFromString(server2_ctx_json); - ServerContextConfigImpl server2_ctx_config(*server2_ctx_loader, secret_manager_); + ServerContextConfigImpl server2_ctx_config(*server2_ctx_loader, server_.secretManager()); ContextManagerImpl manager(runtime); Ssl::ServerSslSocketFactory server_ssl_socket_factory(server_ctx_config, manager, stats_store, std::vector{}); @@ -2125,7 +2126,7 @@ TEST_P(SslSocketTest, ClientAuthCrossListenerSessionResumption) { )EOF"; Json::ObjectSharedPtr client_ctx_loader = TestEnvironment::jsonLoadFromString(client_ctx_json); - ClientContextConfigImpl client_ctx_config(*client_ctx_loader, secret_manager_); + ClientContextConfigImpl client_ctx_config(*client_ctx_loader, server_.secretManager()); ClientSslSocketFactory ssl_socket_factory(client_ctx_config, manager, stats_store); Network::ClientConnectionPtr client_connection = dispatcher.createClientConnection( socket.localAddress(), Network::Address::InstanceConstSharedPtr(), @@ -2211,7 +2212,7 @@ TEST_P(SslSocketTest, SslError) { )EOF"; Json::ObjectSharedPtr server_ctx_loader = TestEnvironment::jsonLoadFromString(server_ctx_json); - ServerContextConfigImpl server_ctx_config(*server_ctx_loader, secret_manager_); + ServerContextConfigImpl server_ctx_config(*server_ctx_loader, server_.secretManager()); ContextManagerImpl manager(runtime); Ssl::ServerSslSocketFactory server_ssl_socket_factory(server_ctx_config, manager, stats_store, std::vector{}); @@ -2533,7 +2534,8 @@ 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_)); + server_ctx_config_.reset( + new ServerContextConfigImpl(*server_ctx_loader_, server_.secretManager())); manager_.reset(new ContextManagerImpl(runtime_)); server_ssl_socket_factory_.reset(new ServerSslSocketFactory( *server_ctx_config_, *manager_, stats_store_, std::vector{})); @@ -2541,7 +2543,8 @@ class SslReadBufferLimitTest : public SslCertsTest, 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_ctx_config_.reset( + new ClientContextConfigImpl(*client_ctx_loader_, server_.secretManager())); client_ssl_socket_factory_.reset( new ClientSslSocketFactory(*client_ctx_config_, *manager_, stats_store_)); diff --git a/test/integration/BUILD b/test/integration/BUILD index e0bad0a3ade9b..abea09b57a193 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/ssl_integration_test.cc b/test/integration/ssl_integration_test.cc index 2cbd941cfa9a4..22fcf58d46f6b 100644 --- a/test/integration/ssl_integration_test.cc +++ b/test/integration/ssl_integration_test.cc @@ -35,14 +35,10 @@ void SslIntegrationTest::initialize() { context_manager_.reset(new ContextManagerImpl(*runtime_)); 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() { diff --git a/test/integration/ssl_utility.cc b/test/integration/ssl_utility.cc index 9c3d1e773b06d..53316b1114aee 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,8 +58,9 @@ 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)}; 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..b1333372ef98a 100644 --- a/test/integration/tcp_proxy_integration_test.cc +++ b/test/integration/tcp_proxy_integration_test.cc @@ -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/xfcc_integration_test.cc b/test/integration/xfcc_integration_test.cc index f0d4f70eda79d..ed92338dd76f3 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" @@ -58,7 +59,7 @@ Network::TransportSocketFactoryPtr XfccIntegrationTest::createClientSslContext(b target = json_tls; } Json::ObjectSharedPtr loader = TestEnvironment::jsonLoadFromString(target); - Ssl::ClientContextConfigImpl cfg(*loader, secret_manager_); + Ssl::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)}; @@ -73,7 +74,7 @@ Network::TransportSocketFactoryPtr XfccIntegrationTest::createUpstreamSslContext )EOF"; Json::ObjectSharedPtr loader = TestEnvironment::jsonLoadFromString(json); - Ssl::ServerContextConfigImpl cfg(*loader, secret_manager_); + Ssl::ServerContextConfigImpl cfg(*loader, server_.secretManager()); static Stats::Scope* upstream_stats_store = new Stats::TestIsolatedStoreImpl(); return std::make_unique( cfg, *context_manager_, *upstream_stats_store, std::vector{}); diff --git a/test/integration/xfcc_integration_test.h b/test/integration/xfcc_integration_test.h index 3432313af7153..3d0c81a0ad09d 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" @@ -56,7 +56,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..f7fd1233b7bb4 100644 --- a/test/mocks/secret/mocks.h +++ b/test/mocks/secret/mocks.h @@ -14,8 +14,14 @@ 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)); }; } // namespace Secret diff --git a/test/mocks/server/mocks.cc b/test/mocks/server/mocks.cc index 72724db9b04d0..3ba3e74f7cdc8 100644 --- a/test/mocks/server/mocks.cc +++ b/test/mocks/server/mocks.cc @@ -107,7 +107,7 @@ 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_), 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/server/configuration_impl_test.cc b/test/server/configuration_impl_test.cc index bec9bb6d05670..da492c5ef5f6c 100644 --- a/test/server/configuration_impl_test.cc +++ b/test/server/configuration_impl_test.cc @@ -303,6 +303,53 @@ 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/config/integration/certs/cacert.pem" + private_key: + filename: "test/config/integration/certs/cakey.pem" + )EOF"; + + MessageUtil::loadFromYaml(yaml, *secret_config); + + MainImpl config; + config.initialize(bootstrap, server_, cluster_manager_factory_); + + auto secret = server_.secretManager().findTlsCertificate("", "abc.com"); + + ASSERT_NE(secret, nullptr); + + EXPECT_EQ(TestEnvironment::readFileToStringForTest("test/config/integration/certs/cacert.pem"), + secret->certificateChain()); + + EXPECT_EQ(TestEnvironment::readFileToStringForTest("test/config/integration/certs/cakey.pem"), + secret->privateKey()); +} + } // namespace Configuration } // namespace Server } // namespace Envoy