Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions include/envoy/secret/secret_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 25 additions & 0 deletions source/common/secret/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -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",
],
)
65 changes: 65 additions & 0 deletions source/common/secret/sds_api.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "common/secret/sds_api.h"

#include <unordered_map>

#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<void()> 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
45 changes: 45 additions & 0 deletions source/common/secret/sds_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#pragma once

#include <functional>

#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<envoy::api::v2::auth::Secret> {
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<void()> 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<envoy::api::v2::auth::Secret>(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<Config::Subscription<envoy::api::v2::auth::Secret>> subscription_;
std::function<void()> initialize_callback_;
std::string sds_config_name_;
};

typedef std::unique_ptr<SdsApi> SdsApiPtr;

} // namespace Secret
} // namespace Envoy
40 changes: 33 additions & 7 deletions source/common/secret/secret_manager_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::shared_timed_mutex> lhs(tls_certificate_secrets_mutex_);
tls_certificate_secrets_[config_source_hash][secret.name()] =
std::make_unique<Ssl::TlsCertificateConfigImpl>(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<std::shared_timed_mutex> 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<std::shared_timed_mutex> 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<SdsApi>(server_, sds_config_source, hash, config_name);

return hash;
}

} // namespace Secret
Expand Down
24 changes: 21 additions & 3 deletions source/common/secret/secret_manager_impl.h
Original file line number Diff line number Diff line change
@@ -1,22 +1,40 @@
#pragma once

#include <shared_mutex>
#include <unordered_map>

#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<Logger::Id::upstream> {
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<std::string, Ssl::TlsCertificateConfigPtr> tls_certificate_secrets_;
Server::Instance& server_;
// map hash code of SDS config source and SdsApi object.
std::unordered_map<std::string, SdsApiPtr> 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<std::string, std::unordered_map<std::string, Ssl::TlsCertificateConfigPtr>>
tls_certificate_secrets_;
mutable std::shared_timed_mutex tls_certificate_secrets_mutex_;
};

} // namespace Secret
Expand Down
37 changes: 37 additions & 0 deletions source/common/secret/secret_manager_util.h
Original file line number Diff line number Diff line change
@@ -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
Loading