Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sds api split #4230

Closed
wants to merge 4 commits into from
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
6 changes: 6 additions & 0 deletions include/envoy/secret/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@ load(

envoy_package()

envoy_cc_library(
name = "secret_callbacks_interface",
hdrs = ["secret_callbacks.h"],
)

envoy_cc_library(
name = "secret_provider_interface",
hdrs = ["secret_provider.h"],
deps = [
":secret_callbacks_interface",
"//include/envoy/ssl:tls_certificate_config_interface",
],
)
Expand Down
18 changes: 18 additions & 0 deletions include/envoy/secret/secret_callbacks.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

#include "envoy/common/pure.h"

namespace Envoy {
namespace Secret {

/**
* Callbacks invoked by a dynamic secret provider.
*/
class SecretCallbacks {
public:
virtual ~SecretCallbacks() {}
virtual void onAddOrUpdateSecret() PURE;
};

} // namespace Secret
} // namespace Envoy
14 changes: 13 additions & 1 deletion include/envoy/secret/secret_provider.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include "envoy/common/pure.h"
#include "envoy/secret/secret_callbacks.h"
#include "envoy/ssl/tls_certificate_config.h"

namespace Envoy {
Expand All @@ -18,7 +19,18 @@ template <class SecretType> class SecretProvider {
*/
virtual const SecretType* secret() const PURE;

// TODO(lizan): Add more methods for dynamic secret provider.
/**
* Add secret callback into secret provider.
* It is safe to call this method by main thread and is safe to be invoked
* on main thread.
* @param callback callback that is executed by secret provider.
*/
virtual void addUpdateCallback(SecretCallbacks& callback) PURE;
/**
* Remove secret callback from secret provider.
* @param callback callback that is executed by secret provider.
*/
virtual void removeUpdateCallback(SecretCallbacks& callback) PURE;
};

typedef SecretProvider<Ssl::TlsCertificateConfig> TlsCertificateConfigProvider;
Expand Down
19 changes: 19 additions & 0 deletions source/common/secret/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,22 @@ envoy_cc_library(
"@envoy_api//envoy/api/v2/auth:cert_cc",
],
)

envoy_cc_library(
name = "sds_api_lib",
srcs = ["sds_api.cc"],
hdrs = ["sds_api.h"],
deps = [
"//include/envoy/config:subscription_interface",
"//include/envoy/event:dispatcher_interface",
"//include/envoy/init:init_interface",
"//include/envoy/local_info:local_info_interface",
"//include/envoy/runtime:runtime_interface",
"//include/envoy/secret:secret_provider_interface",
"//include/envoy/stats:stats_interface",
"//source/common/config:resources_lib",
"//source/common/config:subscription_factory_lib",
"//source/common/protobuf:utility_lib",
"//source/common/ssl:tls_certificate_config_impl_lib",
],
)
89 changes: 89 additions & 0 deletions source/common/secret/sds_api.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
#include "common/secret/sds_api.h"

#include <unordered_map>

#include "envoy/api/v2/auth/cert.pb.validate.h"

#include "common/config/resources.h"
#include "common/config/subscription_factory.h"
#include "common/protobuf/utility.h"
#include "common/ssl/tls_certificate_config_impl.h"

namespace Envoy {
namespace Secret {

SdsApi::SdsApi(const LocalInfo::LocalInfo& local_info, Event::Dispatcher& dispatcher,
Runtime::RandomGenerator& random, Stats::Store& stats,
Upstream::ClusterManager& cluster_manager, Init::Manager& init_manager,
const envoy::api::v2::core::ConfigSource& sds_config, std::string sds_config_name,
std::function<void()> unregister_secret_provider)
: local_info_(local_info), dispatcher_(dispatcher), random_(random), stats_(stats),
cluster_manager_(cluster_manager), sds_config_(sds_config), sds_config_name_(sds_config_name),
secret_hash_(0), unregister_secret_provider_cb_(unregister_secret_provider) {
// TODO(JimmyCYJ): Implement chained_init_manager, so that multiple init_manager
// can be chained together to behave as one init_manager. In that way, we let
// two listeners which share same SdsApi to register at separate init managers, and
// each init manager has a chance to initialize its targets.
init_manager.registerTarget(*this);
}

SdsApi::~SdsApi() { unregister_secret_provider_cb_(); }

void SdsApi::initialize(std::function<void()> callback) {
initialize_callback_ = callback;

subscription_ = Envoy::Config::SubscriptionFactory::subscriptionFromConfigSource<
envoy::api::v2::auth::Secret>(
sds_config_, local_info_, dispatcher_, cluster_manager_, random_, stats_,
/* rest_legacy_constructor */ nullptr,
"envoy.service.discovery.v2.SecretDiscoveryService.FetchSecrets",
"envoy.service.discovery.v2.SecretDiscoveryService.StreamSecrets");
Config::Utility::checkLocalInfo("sds", local_info_);

subscription_->start({sds_config_name_}, *this);
}

void SdsApi::onConfigUpdate(const ResourceVector& resources, const std::string&) {
if (resources.empty()) {
throw EnvoyException(
fmt::format("Missing SDS resources for {} in onConfigUpdate()", sds_config_name_));
}
if (resources.size() != 1) {
throw EnvoyException(fmt::format("Unexpected SDS secrets length: {}", resources.size()));
}
const auto& secret = resources[0];
MessageUtil::validate(secret);
if (secret.name() != sds_config_name_) {
throw EnvoyException(
fmt::format("Unexpected SDS secret (expecting {}): {}", sds_config_name_, secret.name()));
}

const uint64_t new_hash = MessageUtil::hash(secret);
if (new_hash != secret_hash_ &&
secret.type_case() == envoy::api::v2::auth::Secret::TypeCase::kTlsCertificate) {
secret_hash_ = new_hash;
tls_certificate_secrets_ =
std::make_unique<Ssl::TlsCertificateConfigImpl>(secret.tls_certificate());

for (auto cb : update_callbacks_) {
cb->onAddOrUpdateSecret();
}
}

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
80 changes: 80 additions & 0 deletions source/common/secret/sds_api.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#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/event/dispatcher.h"
#include "envoy/init/init.h"
#include "envoy/local_info/local_info.h"
#include "envoy/runtime/runtime.h"
#include "envoy/secret/secret_callbacks.h"
#include "envoy/secret/secret_provider.h"
#include "envoy/stats/stats.h"
#include "envoy/upstream/cluster_manager.h"

namespace Envoy {
namespace Secret {

/**
* SDS API implementation that fetches secrets from SDS server via Subscription.
*/
class SdsApi : public Init::Target,
public TlsCertificateConfigProvider,
public Config::SubscriptionCallbacks<envoy::api::v2::auth::Secret> {
public:
SdsApi(const LocalInfo::LocalInfo& local_info, Event::Dispatcher& dispatcher,
Runtime::RandomGenerator& random, Stats::Store& stats,
Upstream::ClusterManager& cluster_manager, Init::Manager& init_manager,
const envoy::api::v2::core::ConfigSource& sds_config, std::string sds_config_name,
std::function<void()> unregister_secret_provider);

~SdsApi() override;

// 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();
}

// SecretProvider
const Ssl::TlsCertificateConfig* secret() const override {
return tls_certificate_secrets_.get();
}

void addUpdateCallback(SecretCallbacks& callback) override {
update_callbacks_.push_back(&callback);
}
void removeUpdateCallback(SecretCallbacks& callback) override {
update_callbacks_.remove(&callback);
}

private:
void runInitializeCallbackIfAny();

const LocalInfo::LocalInfo& local_info_;
Event::Dispatcher& dispatcher_;
Runtime::RandomGenerator& random_;
Stats::Store& stats_;
Upstream::ClusterManager& cluster_manager_;

const envoy::api::v2::core::ConfigSource sds_config_;
std::unique_ptr<Config::Subscription<envoy::api::v2::auth::Secret>> subscription_;
std::function<void()> initialize_callback_;
const std::string sds_config_name_;

uint64_t secret_hash_;
std::function<void()> unregister_secret_provider_cb_;
Ssl::TlsCertificateConfigPtr tls_certificate_secrets_;
std::list<SecretCallbacks*> update_callbacks_;
};

typedef std::unique_ptr<SdsApi> SdsApiPtr;

} // namespace Secret
} // namespace Envoy
4 changes: 4 additions & 0 deletions source/common/secret/secret_provider_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ class TlsCertificateConfigProviderImpl : public TlsCertificateConfigProvider {

const Ssl::TlsCertificateConfig* secret() const override { return tls_certificate_.get(); }

void addUpdateCallback(SecretCallbacks&) override {}

void removeUpdateCallback(SecretCallbacks&) override {}

private:
Ssl::TlsCertificateConfigPtr tls_certificate_;
};
Expand Down
41 changes: 39 additions & 2 deletions source/common/ssl/ssl_socket.cc
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,35 @@ using Envoy::Network::PostIoAction;
namespace Envoy {
namespace Ssl {

namespace {
// This SslSocket will be used when SSL secret is not fetched from SDS server.
class NotReadySslSocket : public Network::TransportSocket, public Connection {
public:
// Ssl::Connection
bool peerCertificatePresented() const override { return false; }
std::string uriSanLocalCertificate() const override { return EMPTY_STRING; }
const std::string& sha256PeerCertificateDigest() const override { return EMPTY_STRING; }
std::string serialNumberPeerCertificate() const override { return EMPTY_STRING; }
std::string subjectPeerCertificate() const override { return EMPTY_STRING; }
std::string subjectLocalCertificate() const override { return EMPTY_STRING; }
std::string uriSanPeerCertificate() const override { return EMPTY_STRING; }
const std::string& urlEncodedPemEncodedPeerCertificate() const override { return EMPTY_STRING; }
std::vector<std::string> dnsSansPeerCertificate() const override { return {}; }
std::vector<std::string> dnsSansLocalCertificate() const override { return {}; }
// Network::TransportSocket
void setTransportSocketCallbacks(Network::TransportSocketCallbacks&) override {}
std::string protocol() const override { return EMPTY_STRING; }
bool canFlushClose() override { return true; }
void closeSocket(Network::ConnectionEvent) override {}
Network::IoResult doRead(Buffer::Instance&) override { return {PostIoAction::Close, 0, false}; }
Network::IoResult doWrite(Buffer::Instance&, bool) override {
return {PostIoAction::Close, 0, false};
}
void onConnected() override {}
const Ssl::Connection* ssl() const override { return this; }
};
} // namespace

SslSocket::SslSocket(ContextSharedPtr ctx, InitialState state)
: ctx_(std::dynamic_pointer_cast<ContextImpl>(ctx)), ssl_(ctx_->newSsl()) {
if (state == InitialState::Client) {
Expand Down Expand Up @@ -391,7 +420,11 @@ ClientSslSocketFactory::ClientSslSocketFactory(ClientContextConfigPtr config,
ssl_ctx_(manager_.createSslClientContext(stats_scope_, *config_)) {}

Network::TransportSocketPtr ClientSslSocketFactory::createTransportSocket() const {
return std::make_unique<Ssl::SslSocket>(ssl_ctx_, Ssl::InitialState::Client);
if (ssl_ctx_) {
return std::make_unique<Ssl::SslSocket>(ssl_ctx_, Ssl::InitialState::Client);
} else {
return std::make_unique<NotReadySslSocket>();
}
}

bool ClientSslSocketFactory::implementsSecureTransport() const { return true; }
Expand All @@ -405,7 +438,11 @@ ServerSslSocketFactory::ServerSslSocketFactory(ServerContextConfigPtr config,
ssl_ctx_(manager_.createSslServerContext(stats_scope_, *config_, server_names_)) {}

Network::TransportSocketPtr ServerSslSocketFactory::createTransportSocket() const {
return std::make_unique<Ssl::SslSocket>(ssl_ctx_, Ssl::InitialState::Server);
if (ssl_ctx_) {
return std::make_unique<Ssl::SslSocket>(ssl_ctx_, Ssl::InitialState::Server);
} else {
return std::make_unique<NotReadySslSocket>();
}
}

bool ServerSslSocketFactory::implementsSecureTransport() const { return true; }
Expand Down
19 changes: 19 additions & 0 deletions test/common/secret/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,22 @@ envoy_cc_test(
"//test/test_common:utility_lib",
],
)

envoy_cc_test(
name = "sds_api_test",
srcs = ["sds_api_test.cc"],
data = [
"//test/common/ssl/test_data:certs",
],
deps = [
"//source/common/secret:sds_api_lib",
"//test/mocks/grpc:grpc_mocks",
"//test/mocks/init:init_mocks",
"//test/mocks/secret:secret_mocks",
"//test/mocks/server:server_mocks",
"//test/test_common:environment_lib",
"//test/test_common:registry_lib",
"//test/test_common:utility_lib",
"@envoy_api//envoy/service/discovery/v2:sds_cc",
],
)
Loading