Skip to content
Closed
Show file tree
Hide file tree
Changes from 8 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
24 changes: 24 additions & 0 deletions source/common/secret/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,33 @@ 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",
"//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",
],
)
66 changes: 66 additions & 0 deletions source/common/secret/sds_api.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#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_name)
: server_(server), sds_config_(sds_config),
sds_config_source_hash_(SecretManagerUtil::configSourceHash(sds_config)),

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

pass in this hash_? not to calculate it

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
43 changes: 43 additions & 0 deletions source/common/secret/sds_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#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_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_;
};

} // namespace Secret
} // namespace Envoy
39 changes: 32 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,51 @@

#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);
if (sds_apis_.find(hash) != sds_apis_.end()) {

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

do we need to support multiple config_name with the same config_source ? If we do, the sds_apis map key may need
hash + config_name.

return hash;
}

sds_apis_[hash] = std::move(std::make_unique<SdsApi>(server_, sds_config_source, config_name));

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

pass in hash to SdsApi so it doesn't need to re-calculate it


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, std::unique_ptr<SdsApi>> sds_apis_;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

typedef SdsApiPtr?

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