Skip to content

Commit

Permalink
sds: add ability to reload TLS session ticket keys (#8635)
Browse files Browse the repository at this point in the history
Description: Finish migration of TLS session ticket keys to provider-based API.
Risk Level: Medium
Testing: added new tests
Docs Changes: updated
Release Notes: updated
Fixes #7397

Signed-off-by: Ruslan Nigmatullin <elessar@dropbox.com>
  • Loading branch information
Ruslan Nigmatullin authored and lizan committed Oct 18, 2019
1 parent cc8fe6d commit 55ab495
Show file tree
Hide file tree
Showing 15 changed files with 575 additions and 42 deletions.
2 changes: 1 addition & 1 deletion api/envoy/api/v2/auth/cert.proto
Expand Up @@ -377,7 +377,7 @@ message DownstreamTlsContext {
// TLS session ticket key settings.
TlsSessionTicketKeys session_ticket_keys = 4;

// [#not-implemented-hide:]
// Config for fetching TLS session ticket keys via SDS API.
SdsSecretConfig session_ticket_keys_sds_secret_config = 5;
}
}
Expand Down
2 changes: 1 addition & 1 deletion api/envoy/api/v3alpha/auth/cert.proto
Expand Up @@ -380,7 +380,7 @@ message DownstreamTlsContext {
// TLS session ticket key settings.
TlsSessionTicketKeys session_ticket_keys = 4;

// [#not-implemented-hide:]
// Config for fetching TLS session ticket keys via SDS API.
SdsSecretConfig session_ticket_keys_sds_secret_config = 5;
}
}
Expand Down
1 change: 1 addition & 0 deletions docs/root/intro/version_history.rst
Expand Up @@ -77,6 +77,7 @@ Version history
* router check tool: add support for outputting missing tests in the detailed coverage report.
* runtime: allow for the ability to parse boolean values.
* runtime: allow for the ability to parse integers as double values and vice-versa.
* sds: added :ref:`session_ticket_keys_sds_secret_config <envoy_api_field_auth.DownstreamTlsContext.session_ticket_keys_sds_secret_config>` for loading TLS Session Ticket Encryption Keys using SDS API.
* server: added a post initialization lifecycle event, in addition to the existing startup and shutdown events.
* server: added :ref:`per-handler listener stats <config_listener_stats_per_handler>` and
:ref:`per-worker watchdog stats <operations_performance_watchdog>` to help diagnosing event
Expand Down
31 changes: 31 additions & 0 deletions include/envoy/secret/secret_manager.h
Expand Up @@ -44,6 +44,14 @@ class SecretManager {
virtual CertificateValidationContextConfigProviderSharedPtr
findStaticCertificateValidationContextProvider(const std::string& name) const PURE;

/**
* @param name a name of the static TlsSessionTicketKeysConfigProviderSharedPtr.
* @return the TlsSessionTicketKeysConfigProviderSharedPtr. Returns nullptr
* if the static tls session ticket keys are not found.
*/
virtual TlsSessionTicketKeysConfigProviderSharedPtr
findStaticTlsSessionTicketKeysContextProvider(const std::string& name) const PURE;

/**
* @param tls_certificate the protobuf config of the TLS certificate.
* @return a TlsCertificateConfigProviderSharedPtr created from tls_certificate.
Expand All @@ -62,6 +70,13 @@ class SecretManager {
const envoy::api::v2::auth::CertificateValidationContext& certificate_validation_context)
PURE;

/**
* @param tls_certificate the protobuf config of the TLS session ticket keys.
* @return a TlsSessionTicketKeysConfigProviderSharedPtr created from session_ticket_keys.
*/
virtual TlsSessionTicketKeysConfigProviderSharedPtr createInlineTlsSessionTicketKeysProvider(
const envoy::api::v2::auth::TlsSessionTicketKeys& tls_certificate) PURE;

/**
* Finds and returns a dynamic secret provider associated to SDS config. Create
* a new one if such provider does not exist.
Expand Down Expand Up @@ -91,6 +106,22 @@ class SecretManager {
findOrCreateCertificateValidationContextProvider(
const envoy::api::v2::core::ConfigSource& config_source, const std::string& config_name,
Server::Configuration::TransportSocketFactoryContext& secret_provider_context) PURE;

/**
* Finds and returns a dynamic secret provider associated to SDS config. Create
* a new one if such provider does not exist.
*
* @param config_source a protobuf message object containing a SDS config source.
* @param config_name a name that uniquely refers to the SDS config source.
* @param secret_provider_context context that provides components for creating and initializing
* secret provider.
* @return TlsSessionTicketKeysConfigProviderSharedPtr the dynamic tls session ticket keys secret
* provider.
*/
virtual TlsSessionTicketKeysConfigProviderSharedPtr
findOrCreateTlsSessionTicketKeysContextProvider(
const envoy::api::v2::core::ConfigSource& config_source, const std::string& config_name,
Server::Configuration::TransportSocketFactoryContext& secret_provider_context) PURE;
};

} // namespace Secret
Expand Down
6 changes: 6 additions & 0 deletions include/envoy/secret/secret_provider.h
Expand Up @@ -46,6 +46,7 @@ template <class SecretType> class SecretProvider {
using TlsCertificatePtr = std::unique_ptr<envoy::api::v2::auth::TlsCertificate>;
using CertificateValidationContextPtr =
std::unique_ptr<envoy::api::v2::auth::CertificateValidationContext>;
using TlsSessionTicketKeysPtr = std::unique_ptr<envoy::api::v2::auth::TlsSessionTicketKeys>;

using TlsCertificateConfigProvider = SecretProvider<envoy::api::v2::auth::TlsCertificate>;
using TlsCertificateConfigProviderSharedPtr = std::shared_ptr<TlsCertificateConfigProvider>;
Expand All @@ -55,5 +56,10 @@ using CertificateValidationContextConfigProvider =
using CertificateValidationContextConfigProviderSharedPtr =
std::shared_ptr<CertificateValidationContextConfigProvider>;

using TlsSessionTicketKeysConfigProvider =
SecretProvider<envoy::api::v2::auth::TlsSessionTicketKeys>;
using TlsSessionTicketKeysConfigProviderSharedPtr =
std::shared_ptr<TlsSessionTicketKeysConfigProvider>;

} // namespace Secret
} // namespace Envoy
63 changes: 63 additions & 0 deletions source/common/secret/sds_api.h
Expand Up @@ -82,9 +82,11 @@ class SdsApi : public Config::SubscriptionCallbacks {

class TlsCertificateSdsApi;
class CertificateValidationContextSdsApi;
class TlsSessionTicketKeysSdsApi;
using TlsCertificateSdsApiSharedPtr = std::shared_ptr<TlsCertificateSdsApi>;
using CertificateValidationContextSdsApiSharedPtr =
std::shared_ptr<CertificateValidationContextSdsApi>;
using TlsSessionTicketKeysSdsApiSharedPtr = std::shared_ptr<TlsSessionTicketKeysSdsApi>;

/**
* TlsCertificateSdsApi implementation maintains and updates dynamic TLS certificate secrets.
Expand Down Expand Up @@ -198,5 +200,66 @@ class CertificateValidationContextSdsApi : public SdsApi,
validation_callback_manager_;
};

/**
* TlsSessionTicketKeysSdsApi implementation maintains and updates dynamic tls session ticket keys
* secrets.
*/
class TlsSessionTicketKeysSdsApi : public SdsApi, public TlsSessionTicketKeysConfigProvider {
public:
static TlsSessionTicketKeysSdsApiSharedPtr
create(Server::Configuration::TransportSocketFactoryContext& secret_provider_context,
const envoy::api::v2::core::ConfigSource& sds_config, const std::string& sds_config_name,
std::function<void()> destructor_cb) {
// We need to do this early as we invoke the subscription factory during initialization, which
// is too late to throw.
Config::Utility::checkLocalInfo("TlsSessionTicketKeysSdsApi",
secret_provider_context.localInfo());
return std::make_shared<TlsSessionTicketKeysSdsApi>(
sds_config, sds_config_name, secret_provider_context.clusterManager().subscriptionFactory(),
secret_provider_context.dispatcher().timeSource(),
secret_provider_context.messageValidationVisitor(), secret_provider_context.stats(),
*secret_provider_context.initManager(), destructor_cb);
}

TlsSessionTicketKeysSdsApi(const envoy::api::v2::core::ConfigSource& sds_config,
const std::string& sds_config_name,
Config::SubscriptionFactory& subscription_factory,
TimeSource& time_source,
ProtobufMessage::ValidationVisitor& validation_visitor,
Stats::Store& stats, Init::Manager& init_manager,
std::function<void()> destructor_cb)
: SdsApi(sds_config, sds_config_name, subscription_factory, time_source, validation_visitor,
stats, init_manager, std::move(destructor_cb)) {}

// SecretProvider
const envoy::api::v2::auth::TlsSessionTicketKeys* secret() const override {
return tls_session_ticket_keys_.get();
}

Common::CallbackHandle* addUpdateCallback(std::function<void()> callback) override {
return update_callback_manager_.add(callback);
}

Common::CallbackHandle* addValidationCallback(
std::function<void(const envoy::api::v2::auth::TlsSessionTicketKeys&)> callback) override {
return validation_callback_manager_.add(callback);
}

protected:
void setSecret(const envoy::api::v2::auth::Secret& secret) override {
tls_session_ticket_keys_ =
std::make_unique<envoy::api::v2::auth::TlsSessionTicketKeys>(secret.session_ticket_keys());
}

void validateConfig(const envoy::api::v2::auth::Secret& secret) override {
validation_callback_manager_.runCallbacks(secret.session_ticket_keys());
}

private:
Secret::TlsSessionTicketKeysPtr tls_session_ticket_keys_;
Common::CallbackManager<const envoy::api::v2::auth::TlsSessionTicketKeys&>
validation_callback_manager_;
};

} // namespace Secret
} // namespace Envoy
80 changes: 79 additions & 1 deletion source/common/secret/secret_manager_impl.cc
Expand Up @@ -39,6 +39,17 @@ void SecretManagerImpl::addStaticSecret(const envoy::api::v2::auth::Secret& secr
}
break;
}
case envoy::api::v2::auth::Secret::TypeCase::kSessionTicketKeys: {
auto secret_provider =
std::make_shared<TlsSessionTicketKeysConfigProviderImpl>(secret.session_ticket_keys());
if (!static_session_ticket_keys_providers_
.insert(std::make_pair(secret.name(), secret_provider))
.second) {
throw EnvoyException(
fmt::format("Duplicate static TlsSessionTicketKeys secret name {}", secret.name()));
}
break;
}
default:
throw EnvoyException("Secret type not implemented");
}
Expand All @@ -57,6 +68,12 @@ SecretManagerImpl::findStaticCertificateValidationContextProvider(const std::str
: nullptr;
}

TlsSessionTicketKeysConfigProviderSharedPtr
SecretManagerImpl::findStaticTlsSessionTicketKeysContextProvider(const std::string& name) const {
auto secret = static_session_ticket_keys_providers_.find(name);
return (secret != static_session_ticket_keys_providers_.end()) ? secret->second : nullptr;
}

TlsCertificateConfigProviderSharedPtr SecretManagerImpl::createInlineTlsCertificateProvider(
const envoy::api::v2::auth::TlsCertificate& tls_certificate) {
return std::make_shared<TlsCertificateConfigProviderImpl>(tls_certificate);
Expand All @@ -69,6 +86,12 @@ SecretManagerImpl::createInlineCertificateValidationContextProvider(
certificate_validation_context);
}

TlsSessionTicketKeysConfigProviderSharedPtr
SecretManagerImpl::createInlineTlsSessionTicketKeysProvider(
const envoy::api::v2::auth::TlsSessionTicketKeys& tls_session_ticket_keys) {
return std::make_shared<TlsSessionTicketKeysConfigProviderImpl>(tls_session_ticket_keys);
}

TlsCertificateConfigProviderSharedPtr SecretManagerImpl::findOrCreateTlsCertificateProvider(
const envoy::api::v2::core::ConfigSource& sds_config_source, const std::string& config_name,
Server::Configuration::TransportSocketFactoryContext& secret_provider_context) {
Expand All @@ -84,7 +107,15 @@ SecretManagerImpl::findOrCreateCertificateValidationContextProvider(
secret_provider_context);
}

// We clear private key and password to avoid information leaking.
TlsSessionTicketKeysConfigProviderSharedPtr
SecretManagerImpl::findOrCreateTlsSessionTicketKeysContextProvider(
const envoy::api::v2::core::ConfigSource& sds_config_source, const std::string& config_name,
Server::Configuration::TransportSocketFactoryContext& secret_provider_context) {
return session_ticket_keys_providers_.findOrCreate(sds_config_source, config_name,
secret_provider_context);
}

// We clear private key, password, and session ticket encryption keys to avoid information leaking.
// TODO(incfly): switch to more generic scrubbing mechanism once
// https://github.com/envoyproxy/envoy/issues/4757 is resolved.
void redactSecret(::envoy::api::v2::auth::Secret* secret) {
Expand All @@ -99,6 +130,13 @@ void redactSecret(::envoy::api::v2::auth::Secret* secret) {
tls_certificate->mutable_password()->set_inline_string("[redacted]");
}
}
if (secret && secret->type_case() == envoy::api::v2::auth::Secret::TypeCase::kSessionTicketKeys) {
for (auto& data_source : *secret->mutable_session_ticket_keys()->mutable_keys()) {
if (data_source.specifier_case() != envoy::api::v2::core::DataSource::kFilename) {
data_source.set_inline_string("[redacted]");
}
}
}
}

ProtobufTypes::MessagePtr SecretManagerImpl::dumpSecretConfigs() {
Expand Down Expand Up @@ -126,6 +164,20 @@ ProtobufTypes::MessagePtr SecretManagerImpl::dumpSecretConfigs() {
dump_secret->mutable_validation_context()->MergeFrom(*validation_context->secret());
}

// Handle static session keys providers.
for (const auto& context_iter : static_session_ticket_keys_providers_) {
const auto& session_ticket_keys = context_iter.second;
auto static_secret = config_dump->mutable_static_secrets()->Add();
static_secret->set_name(context_iter.first);
ASSERT(session_ticket_keys != nullptr);
auto dump_secret = static_secret->mutable_secret();
dump_secret->set_name(context_iter.first);
for (const auto& key : session_ticket_keys->secret()->keys()) {
dump_secret->mutable_session_ticket_keys()->add_keys()->MergeFrom(key);
}
redactSecret(dump_secret);
}

// Handle dynamic tls_certificate providers.
const auto providers = certificate_providers_.allSecretProviders();
for (const auto& cert_secrets : providers) {
Expand Down Expand Up @@ -175,6 +227,32 @@ ProtobufTypes::MessagePtr SecretManagerImpl::dumpSecretConfigs() {
secret->mutable_validation_context()->MergeFrom(*validation_context);
}
}

// Handle dynamic session keys providers providers.
const auto stek_providers = session_ticket_keys_providers_.allSecretProviders();
for (const auto& stek_secrets : stek_providers) {
const auto& secret_data = stek_secrets->secretData();
const auto& tls_stek = stek_secrets->secret();
::envoy::admin::v2alpha::SecretsConfigDump_DynamicSecret* dump_secret;
const bool secret_ready = tls_stek != nullptr;
if (secret_ready) {
dump_secret = config_dump->mutable_dynamic_active_secrets()->Add();
} else {
dump_secret = config_dump->mutable_dynamic_warming_secrets()->Add();
}
dump_secret->set_name(secret_data.resource_name_);
auto secret = dump_secret->mutable_secret();
secret->set_name(secret_data.resource_name_);
ProtobufWkt::Timestamp last_updated_ts;
TimestampUtil::systemClockToTimestamp(secret_data.last_updated_, last_updated_ts);
dump_secret->set_version_info(secret_data.version_info_);
*dump_secret->mutable_last_updated() = last_updated_ts;
secret->set_name(secret_data.resource_name_);
if (secret_ready) {
secret->mutable_session_ticket_keys()->MergeFrom(*tls_stek);
}
redactSecret(secret);
}
return config_dump;
}

Expand Down
14 changes: 14 additions & 0 deletions source/common/secret/secret_manager_impl.h
Expand Up @@ -25,6 +25,9 @@ class SecretManagerImpl : public SecretManager {
CertificateValidationContextConfigProviderSharedPtr
findStaticCertificateValidationContextProvider(const std::string& name) const override;

TlsSessionTicketKeysConfigProviderSharedPtr
findStaticTlsSessionTicketKeysContextProvider(const std::string& name) const override;

TlsCertificateConfigProviderSharedPtr createInlineTlsCertificateProvider(
const envoy::api::v2::auth::TlsCertificate& tls_certificate) override;

Expand All @@ -33,6 +36,9 @@ class SecretManagerImpl : public SecretManager {
const envoy::api::v2::auth::CertificateValidationContext& certificate_validation_context)
override;

TlsSessionTicketKeysConfigProviderSharedPtr createInlineTlsSessionTicketKeysProvider(
const envoy::api::v2::auth::TlsSessionTicketKeys& tls_session_ticket_keys) override;

TlsCertificateConfigProviderSharedPtr findOrCreateTlsCertificateProvider(
const envoy::api::v2::core::ConfigSource& config_source, const std::string& config_name,
Server::Configuration::TransportSocketFactoryContext& secret_provider_context) override;
Expand All @@ -42,6 +48,10 @@ class SecretManagerImpl : public SecretManager {
const envoy::api::v2::core::ConfigSource& config_source, const std::string& config_name,
Server::Configuration::TransportSocketFactoryContext& secret_provider_context) override;

TlsSessionTicketKeysConfigProviderSharedPtr findOrCreateTlsSessionTicketKeysContextProvider(
const envoy::api::v2::core::ConfigSource& config_source, const std::string& config_name,
Server::Configuration::TransportSocketFactoryContext& secret_provider_context) override;

private:
ProtobufTypes::MessagePtr dumpSecretConfigs();

Expand Down Expand Up @@ -102,9 +112,13 @@ class SecretManagerImpl : public SecretManager {
std::unordered_map<std::string, CertificateValidationContextConfigProviderSharedPtr>
static_certificate_validation_context_providers_;

std::unordered_map<std::string, TlsSessionTicketKeysConfigProviderSharedPtr>
static_session_ticket_keys_providers_;

// map hash code of SDS config source and SdsApi object.
DynamicSecretProviders<TlsCertificateSdsApi> certificate_providers_;
DynamicSecretProviders<CertificateValidationContextSdsApi> validation_context_providers_;
DynamicSecretProviders<TlsSessionTicketKeysSdsApi> session_ticket_keys_providers_;

Server::ConfigTracker::EntryOwnerPtr config_tracker_entry_;
};
Expand Down
5 changes: 5 additions & 0 deletions source/common/secret/secret_provider_impl.cc
Expand Up @@ -17,5 +17,10 @@ CertificateValidationContextConfigProviderImpl::CertificateValidationContextConf
std::make_unique<envoy::api::v2::auth::CertificateValidationContext>(
certificate_validation_context)) {}

TlsSessionTicketKeysConfigProviderImpl::TlsSessionTicketKeysConfigProviderImpl(
const envoy::api::v2::auth::TlsSessionTicketKeys& tls_session_ticket_keys)
: tls_session_ticket_keys_(
std::make_unique<envoy::api::v2::auth::TlsSessionTicketKeys>(tls_session_ticket_keys)) {}

} // namespace Secret
} // namespace Envoy
20 changes: 20 additions & 0 deletions source/common/secret/secret_provider_impl.h
Expand Up @@ -50,5 +50,25 @@ class CertificateValidationContextConfigProviderImpl
Secret::CertificateValidationContextPtr certificate_validation_context_;
};

class TlsSessionTicketKeysConfigProviderImpl : public TlsSessionTicketKeysConfigProvider {
public:
TlsSessionTicketKeysConfigProviderImpl(
const envoy::api::v2::auth::TlsSessionTicketKeys& tls_session_ticket_keys);

const envoy::api::v2::auth::TlsSessionTicketKeys* secret() const override {
return tls_session_ticket_keys_.get();
}

Common::CallbackHandle* addValidationCallback(
std::function<void(const envoy::api::v2::auth::TlsSessionTicketKeys&)>) override {
return nullptr;
}

Common::CallbackHandle* addUpdateCallback(std::function<void()>) override { return nullptr; }

private:
Secret::TlsSessionTicketKeysPtr tls_session_ticket_keys_;
};

} // namespace Secret
} // namespace Envoy

0 comments on commit 55ab495

Please sign in to comment.