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: clusters and listeners read static secrets from Bootstrap.static_resources #3465

Merged

Conversation

mangchiandjjoe
Copy link
Contributor

Description:
Clusters and listeners read secrets from the static resources in the bootstrap configuration.
Reading secrets from the Secret Discovery Service will follow.

Risk Level: Low

Fixes #1194

Signed-off-by: Jae Kim [email protected]

@mangchiandjjoe mangchiandjjoe changed the title sds: clusters and listeners read static secrets from Bootstrap.static_resources [WIP] sds: clusters and listeners read static secrets from Bootstrap.static_resources May 23, 2018
@mattklein123
Copy link
Member

@mangchiandjjoe please check CI.

@mangchiandjjoe mangchiandjjoe changed the title [WIP] sds: clusters and listeners read static secrets from Bootstrap.static_resources sds: clusters and listeners read static secrets from Bootstrap.static_resources May 23, 2018
@lizan
Copy link
Member

lizan commented May 23, 2018

CI seems to be OK now.

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

Some quick comments to get started. Mostly nits. A few other things:

  1. Will need release notes
  2. Likely there are doc fields that need to be unhidden. Can you check that?

hdrs = ["secret_manager.h"],
deps = [
":secret_interface",
"//source/common/json:json_loader_lib",
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't be needed in public interface

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

namespace Secret {

/**
* Secret contains certificate chain and private key
Copy link
Member

Choose a reason for hiding this comment

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

nit: Please go through and end all comments with full stop ('.').

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

virtual const std::string& name() const PURE;

/**
* @return a string of certificate chain
Copy link
Member

Choose a reason for hiding this comment

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

Should we use strings here? Or vectors of bytes? @PiotrSikora any preference?

@@ -0,0 +1,38 @@
#pragma once

#include <google/protobuf/util/json_util.h>
Copy link
Member

Choose a reason for hiding this comment

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

not needed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed


#include "envoy/secret/secret.h"

#include "common/json/json_loader.h"
Copy link
Member

Choose a reason for hiding this comment

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

not needed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

@@ -3,6 +3,7 @@
#include <string>

#include "envoy/network/transport_socket.h"
#include "envoy/secret/secret_manager.h"
Copy link
Member

Choose a reason for hiding this comment

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

not needed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

@@ -130,5 +131,20 @@ void BootstrapJson::translateBootstrap(const Json::Object& json_config,
}
}

void BootstrapJson::translateStaticSecretsBootstrap(
Copy link
Member

Choose a reason for hiding this comment

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

We won't support SDS in v1 config. Please remove all v1 config references in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed the method and related test cases

const std::string& privateKey() const override { return private_key_; }

private:
std::string name_;
Copy link
Member

Choose a reason for hiding this comment

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

const for all of these

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done


bool SecretManagerImpl::addOrUpdateStaticSecret(const SecretSharedPtr secret) {
static_secrets_[secret->name()] = secret;
return true;
Copy link
Member

Choose a reason for hiding this comment

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

looks like it can't fail. Please make this a void function for now.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed to void

public:
SecretManagerImpl(){};

virtual ~SecretManagerImpl() {}
Copy link
Member

Choose a reason for hiding this comment

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

del

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

@mattklein123 mattklein123 self-assigned this May 24, 2018
Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

Thank you. Flushing comments.

namespace Secret {

/**
* A manager for all static secrets.
Copy link
Member

Choose a reason for hiding this comment

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

Aren't we eventually going to use this for dynamic secrets also? What is the long term plan for this code?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We are going to support dynamic secrets. SecretManager will also manage SdsApi clients.
Added TODO comment

/**
* @param secret Updated Secret.
*/

Copy link
Member

Choose a reason for hiding this comment

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

nit: del newline

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

* @param secret Updated Secret.
*/

virtual void addOrUpdateStaticSecret(const SecretSharedPtr secret) PURE;
Copy link
Member

Choose a reason for hiding this comment

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

nit: const doesn't do anything here, just remove. Better would probably be to make this a SecretConstSharedPtr and have const inside the pointer definition.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

/**
* @return the static secret for the given name.
*/
virtual const SecretSharedPtr staticSecret(const std::string& name) const PURE;
Copy link
Member

Choose a reason for hiding this comment

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

Same question here. Can we remove static from all of these method definitions? Aren't we going to do dynamic updates later?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Renamed to findSecret

namespace Envoy {
namespace Secret {

typedef std::unordered_map<std::string, SecretSharedPtr> SecretSharedPtrMap;
Copy link
Member

Choose a reason for hiding this comment

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

This typedef is unnecessary and/or can be moved private within the impl class.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved in to the private

? ""
: Config::DataSource::read(config.tls_certificates()[0].private_key(), true)),
private_key_([&config, &secret_manager] {
if (!config.tls_certificates().empty()) {
Copy link
Member

Choose a reason for hiding this comment

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

Can this logic be lifted out into a utility and used both here and above? it looks the same.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Those are same structure but calling method are different.
function might requires either an argument to choose certificate chain/private key or function pointer.
I am not sure that is efficient.

Copy link
Member

Choose a reason for hiding this comment

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

Efficiency doesn't really matter in this path. I would rather have common handling for some of the error handling logic. Please see what you can do here to consolidate.

@@ -139,17 +169,21 @@ ServerContextConfigImpl::ServerContextConfigImpl(
return ret;
}()) {
// TODO(PiotrSikora): Support multiple TLS certificates.
if (config.common_tls_context().tls_certificates().size() != 1) {
if (config.common_tls_context().tls_certificates().size() != 1 &&
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this is right. You need there to be at least 1 cert across both sources, right? Assuming so, please add a test that should fail with this code and then fix it.

Copy link
Contributor

Choose a reason for hiding this comment

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

There need to be exactly 1 cert.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Modified to check the sum is 1

for (ssize_t i = 0; i < secrets.size(); i++) {
ENVOY_LOG(debug, "static secret #{}: {}", i, secrets[i].name());
server.secretManager().addOrUpdateStaticSecret(
Secret::SecretSharedPtr(new Secret::SecretImpl(secrets[i])));
Copy link
Member

Choose a reason for hiding this comment

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

nit: std::make_shared

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@@ -248,6 +248,9 @@ void InstanceImpl::initialize(Options& options,
listener_manager_.reset(
new ListenerManagerImpl(*this, listener_component_factory_, worker_factory_));

// Shared storage of secrets from SDS
secret_manager_.reset(new Secret::SecretManagerImpl());
Copy link
Member

Choose a reason for hiding this comment

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

You should be able to initialize this in the initializer list.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved to the initializer list

namespace Secret {

/**
* Secret contains certificate chain and private key.
Copy link
Contributor

Choose a reason for hiding this comment

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

That diverges quite a lot from the API, in which Secret is opaque container for various secrets (certificates, session ticket keys, etc.).

Could we perhaps make this opaque interface that returns Protobuf::Message& which can then be used for config generation?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added a new virtual method and removed TLS certificate related methods.

Copy link
Member

Choose a reason for hiding this comment

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

Please update the class comment now that it is more general.

/**
* @return the static secret for the given name.
*/
virtual const SecretSharedPtr staticSecret(const std::string& name) const PURE;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we care whether the secret was configured as static or not? I think the only difference between "static" and "dynamic" secrets is going to be the missing config source, so perhaps something like that:

const SecretSharedPtr findSecret(const std::string& name, const std::string &source);

could be used for both, with source being EMPTY_STRING for the static secrets?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

renamed the method.
Since source paramter is not going to be used for now, I will add source parameter when it supports dynamic secret.

@@ -139,17 +169,21 @@ ServerContextConfigImpl::ServerContextConfigImpl(
return ret;
}()) {
// TODO(PiotrSikora): Support multiple TLS certificates.
if (config.common_tls_context().tls_certificates().size() != 1) {
if (config.common_tls_context().tls_certificates().size() != 1 &&
config.common_tls_context().tls_certificate_sds_secret_configs().size() != 1) {
Copy link
Contributor

Choose a reason for hiding this comment

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

We can have only 1 in total, so perhaps test if (sum != 1).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

namespace Envoy {
namespace Secret {

class SecretImpl : public Secret {
Copy link
Contributor

Choose a reason for hiding this comment

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

This should really be class TlsCertificateConfigImpl : public Secret or something like that, otherwise it won't work scale for other secret types.

Perhaps move it to source/common/ssl as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

Making progress. Thank you! Some more comments.

namespace Secret {

/**
* Secret contains certificate chain and private key.
Copy link
Member

Choose a reason for hiding this comment

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

Please update the class comment now that it is more general.

/**
* @param secret Updated Secret.
*/
virtual void addOrUpdateStaticSecret(SecretSharedPtr secret) PURE;
Copy link
Member

Choose a reason for hiding this comment

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

Asking again. Why does this need to be called addOrUpdateStaticSecret? Why can't it just be addOrUpdateSecret?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Modified

virtual void addOrUpdateStaticSecret(SecretSharedPtr secret) PURE;

/**
* @return the static secret for the given name.
Copy link
Member

Choose a reason for hiding this comment

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

update method comment

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

? ""
: Config::DataSource::read(config.tls_certificates()[0].private_key(), true)),
private_key_([&config, &secret_manager] {
if (!config.tls_certificates().empty()) {
Copy link
Member

Choose a reason for hiding this comment

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

Efficiency doesn't really matter in this path. I would rather have common handling for some of the error handling logic. Please see what you can do here to consolidate.

config.tls_certificate_sds_secret_configs()[0].name()));
}

return std::dynamic_pointer_cast<Ssl::TlsCertificateConfigImpl>(static_secret)
Copy link
Member

Choose a reason for hiding this comment

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

This dynamic cast here is not great. There is nothing to prevent the user from having the wrong type of object mapped to name. Feel free to follow up with @PiotrSikora, but we either need to do extra error checking logic prior to dynamic cast, or potentially store the type of object in the secret manager and then have a map for each type of object with name -> object mapping. Same comment applies below. Please make sure you have a test case for this config issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I added the error checking for the date type of secret and the test case.

public:
TlsCertificateConfigImpl(const envoy::api::v2::auth::Secret& config);

const Protobuf::Message& message() const override { return message_; }
Copy link
Member

Choose a reason for hiding this comment

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

Not sure I fully understand this class now. Why do you need to return the abstract message here if you end up doing the dynamic cast to this object and getting cert chain and private key? Per my other comments I think we need to rework all of this to avoid dynamic_casts entirely.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed to return the same config data type.

const SecretSharedPtr findSecret(const std::string& name) const override;

private:
typedef std::unordered_map<std::string, SecretSharedPtr> SecretSharedPtrMap;
Copy link
Contributor

Choose a reason for hiding this comment

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

can Secret be stored as unique_ptr?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Secret needs to be shared pointer to typecast with std::dynamic_pointer_cast.

} else {
return std::string("");
}
}()),
Copy link
Contributor

Choose a reason for hiding this comment

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

IMHO, it is easier to read if making it as a separate function.

Copy link
Member

Choose a reason for hiding this comment

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

+1, please figure out how to break up / simplify the logic in this function.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Created separated functions

envoy::api::v2::auth::Secret message;
message.CopyFrom(config);
return message;
}()),
Copy link
Contributor

Choose a reason for hiding this comment

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

Line 9 to 13 is overkill. I think it can be simplified as: message_(config)

Copy link
Member

Choose a reason for hiding this comment

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

Per my other comment you can remove all of this entirely, I don't think it's needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Replaced

envoy::api::v2::auth::Secret cfg;
cfg.CopyFrom(message);
return cfg;
}()),
Copy link
Contributor

Choose a reason for hiding this comment

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

can it be simplified as message_(message)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Replaced


TEST(ClientContextConfigImplTest, StaticTlsCertificates) {
std::string kExpectedCertificateChain =
R"EOF(-----BEGIN CERTIFICATE-----
Copy link
Contributor

Choose a reason for hiding this comment

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

are these two strings the same as ones in secret_manager_impl_test.cc? If so, can they be defined once?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Created and used separated test data library.

/**
* @return protobuf message that initialized the secret.
*/
virtual const Protobuf::Message& message() const PURE;
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this function is actually used anywhere. It can be removed from the interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

/**
* @param secret Updated Secret.
*/
virtual void addOrUpdateSecret(SecretSharedPtr secret) PURE;
Copy link
Member

Choose a reason for hiding this comment

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

nit: const SecretSharedPtr&

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Modified

namespace {

template <typename T>
const std::shared_ptr<T> findSecretAndCheckType(const Secret::SecretManager& secret_manager,
Copy link
Member

Choose a reason for hiding this comment

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

This function will end up getting used for other types of secrets. I would add this to the primary interface.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Moved to SecretManager interface

} else {
return std::string("");
}
}()),
Copy link
Member

Choose a reason for hiding this comment

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

+1, please figure out how to break up / simplify the logic in this function.

envoy::api::v2::auth::Secret message;
message.CopyFrom(config);
return message;
}()),
Copy link
Member

Choose a reason for hiding this comment

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

Per my other comment you can remove all of this entirely, I don't think it's needed.

return message;
}()),
name_(config.name()), certificate_chain_(Config::DataSource::read(
config.tls_certificate().certificate_chain(), true)),
Copy link
Member

Choose a reason for hiding this comment

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

The way you have structured the code, there is no guarantee that tls_certificate() is actually defined. You need more error checking here.

I don't feel that strongly about it, but I would in general prefer we do away w/ the dynamic casting and just have a base secret interface, and then sub-class interfaces for each secret type, and then potentially a way to lookup a secret in the manager by type. I think that would consolidate a lot of the error checking logic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Created multi layered map and added template functions that add and lookup secret based on the type.
map<key, map<name, secret>>

@@ -17,14 +21,37 @@ class SecretManager {
virtual ~SecretManager() {}

/**
* @param secret a raw pointer of the derived class of Secret.
*/
template <typename T> void addOrUpdateSecret(T* secret) {
Copy link
Contributor

Choose a reason for hiding this comment

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

It is so clear about the owership transfer when using raw pointer. Can we pass in shared_ptr ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed variable type


SecretSharedPtrMap static_secrets_;
std::unordered_map<std::string, std::unordered_map<std::string, SecretSharedPtr>> secrets_;
Copy link
Contributor

Choose a reason for hiding this comment

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

please add comment about two dimension maps, what is the first level key, what is the second level key and its value

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Added comment

}
return secret->privateKey();
} else {
return std::string("");
Copy link
Contributor

Choose a reason for hiding this comment

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

These two functions are similar. like to combine them. How about this:

string readConfig(config, mgr, READ_TCL_CERT_FN, READ_CONFIG_IMPL) {
if (!config.tls_certificates().empty()) {
return Config::DataSource::read(READ_TCL_CERT_FN(config.tls_certificates()[0])), true); } else if (!config.tls_certificate_sds_secret_configs().empty()) {
+
return READ_CONFIG_IMPL(second_impl);
}

typedef std::function<const DataStore& (const tcl_certificate&)> read_tcl_cert_fn;

* add or update secret grouped by type.
* @param secret a shared_ptr of an implementation of Secret.
*/
template <typename T> void addOrUpdateSecret(const SecretSharedPtr& secret) {
Copy link
Member

Choose a reason for hiding this comment

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

-1 to this heavy use of RTTI:
https://google.github.io/styleguide/cppguide.html#Run-Time_Type_Information__RTTI_

You should be able to add a type() method returning an string (or better enum) to Secret interface and just use it from here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Created enum SecretType and removed RTTI

secrets_[type] = {};
type_secrets = secrets_.find(type);
secrets_[secret->type()] = {};
type_secrets = secrets_.find(secret->type());
}

type_secrets->second[secret->name()] = secret;
Copy link
Contributor

@qiwzhang qiwzhang May 31, 2018

Choose a reason for hiding this comment

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

I think you can combine 7 to 13 to:

map[type][name] = secret;

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

Thanks, getting close.

@@ -22,4 +22,3 @@ message FileBasedMetadataConfig {
// if no prefix is set, the default is to use no prefix
string header_prefix = 3;
}

Copy link
Member

Choose a reason for hiding this comment

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

Please merge master

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It is weird. I haven't change the file. After I copied the file from the master branch I still see this change.

Copy link
Contributor

Choose a reason for hiding this comment

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

Could you fix that?

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(
Copy link
Member

Choose a reason for hiding this comment

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

This is where the code should be to check for type. It doesn't make sense to assume a secret here is of this type given that we have a oneof(). Please add some type of factory function that switches on the type of secret and then create the appropriate type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Currently we only support TLS certificate, the constructor of TlsCertificateConfigImpl is checking error. But I agree with your idea. I will create a SecretReadFactory function.

if (!secret) {
throw EnvoyException(fmt::format("Static secret is not defined: {}", name));
}
return read_secret(std::dynamic_pointer_cast<Ssl::TlsCertificateConfigImpl>(secret));
Copy link
Member

Choose a reason for hiding this comment

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

I'm still really not a fan of this dynamic cast stuff. Is there any real benefit to doing it this way? Can we just have the secret manager manage different types of secrets and have different add/get functions for each of them? They can all derive from a base secret type so that common logic can be done across all secrets at a later time?

With that said, I don't feel super strongly about this. @PiotrSikora any opinions here given what we need to do in the future in this area?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@PiotrSikora Could you please update your opinions?

Copy link
Member

Choose a reason for hiding this comment

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

slightly +1 to @mattklein123. We can move back to this dynamic cast stuff where the type of secret increases.

}

const SecretSharedPtr
SecretManagerImpl::loadSecret(const envoy::api::v2::auth::Secret& secret) const {
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems that this should be a static function since it is NOT using any object's data.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Combined loadSecret to addOrUpdateSecret

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(server.secretManager().loadSecret(secrets[i]));
Copy link
Contributor

Choose a reason for hiding this comment

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

these two calls can be combined into one call?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Combined loadSecret to addOrUpdateSecret


class SecretManagerImplTest : public testing::Test {};

TEST_F(SecretManagerImplTest, WeightedClusterFallthroughConfig) {
Copy link
Contributor

Choose a reason for hiding this comment

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

does the test case name match the test?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Changed the test name

tls_certificate->mutable_certificate_chain()->set_filename(
"test/common/ssl/test_data/selfsigned_cert.pem");
tls_certificate->mutable_private_key()->set_filename(
"test/common/ssl/test_data/selfsigned_key.pem");
Copy link
Contributor

Choose a reason for hiding this comment

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

use loadFromYaml()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Modified

}
return read_secret(std::dynamic_pointer_cast<Ssl::TlsCertificateConfigImpl>(secret));
} else {
return std::string("");
Copy link
Member

Choose a reason for hiding this comment

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

nit: EMPTY_STRING

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Replaced

const envoy::api::v2::auth::CommonTlsContext& config, Secret::SecretManager& secret_manager,
const std::function<std::string(const envoy::api::v2::auth::TlsCertificate& tls_certificate)>&
read_inline_config,
const std::function<std::string(const std::shared_ptr<Ssl::TlsCertificateConfigImpl>& secret)>&
Copy link
Member

Choose a reason for hiding this comment

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

the std::function doesn't have to take shared_ptr, just use const Ssl::TlsCertificateConfigImpl&

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Modified

if (!secret) {
throw EnvoyException(fmt::format("Static secret is not defined: {}", name));
}
return read_secret(std::dynamic_pointer_cast<Ssl::TlsCertificateConfigImpl>(secret));
Copy link
Member

Choose a reason for hiding this comment

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

slightly +1 to @mattklein123. We can move back to this dynamic cast stuff where the type of secret increases.

@@ -0,0 +1,47 @@
#include <string>
Copy link
Member

Choose a reason for hiding this comment

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

Seems these certificate data are from test/common/ssl/test_data/, which might be regenerated/modified in future.

You can just use TestEnvironment::readFileToStringForTest to read those data and use as expected strings.

Copy link
Member

@lizan lizan left a comment

Choose a reason for hiding this comment

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

Almost good to go, one comment in test.

if (!secret) {
throw EnvoyException(fmt::format("Static secret is not defined: {}", name));
}
return read_secret(*(std::dynamic_pointer_cast<Ssl::TlsCertificateConfigImpl>(secret)).get());
Copy link
Member

Choose a reason for hiding this comment

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

nit: dynamic_cast<const Ssl::TlsCertificateConfigImpl&>(*secret) should work.

Copy link
Member

Choose a reason for hiding this comment

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

I would rather just get rid of the dynamic casts. I don't think it's necessary. Since @PiotrSikora hasn't responded back, can we please rework this so that the secret manager supports different types of secrets with a normal class structure? I think that will be the cleanest moving forward. Thank you!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I will update. Thanks.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed dynamic casts

Copy link
Contributor

Choose a reason for hiding this comment

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

Sorry, those comments get hidden with every push and I've missed this one... I believe the find<TYPE>() is what I suggested to @mangchiandjjoe when we were discussing this together with @qiwzhang last week, so the current code works fine for me.

@@ -0,0 +1,47 @@
#include <string>
Copy link
Member

Choose a reason for hiding this comment

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

delete these two files.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

@@ -0,0 +1,11 @@
#include "mocks.h"
Copy link
Member

Choose a reason for hiding this comment

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

nit: full path test/mocks/secret/mocks.h

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

#pragma once

#include <string>
#include <unordered_map>
Copy link
Member

Choose a reason for hiding this comment

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

nit: unused

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

@@ -0,0 +1,28 @@
#pragma once

#include <chrono>
Copy link
Member

Choose a reason for hiding this comment

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

nit: clean unused includes

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

lizan
lizan previously approved these changes Jun 8, 2018
* @param name a name of the TlsCertificateSecret.
* @return the TlsCertificate secret. Returns nullptr if the secret is not found.
*/
virtual const TlsCertificateSecretSharedPtr
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we can return const TlsCertificateSecret* here... we immediately extract raw pointer from the result of the call to this function anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Modified

public:
TlsCertificateSecretImpl(const std::string& name,
const envoy::api::v2::auth::TlsCertificate& config);
virtual ~TlsCertificateSecretImpl() {}
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think this destructor is needed?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

namespace Envoy {
namespace Ssl {

class TlsCertificateSecretImpl : public Secret::TlsCertificateSecret {
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe I mentioned this before, but this should be called TlsCertificateConfigImpl, since there is nothing Secret-specific about this, and both old-style certificates and SDS certificates could be represented by this object.

Basically, this should be materialized version of the TlsCertificate from the proto.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Renamed back to TlsCertificateConfigImpl

const envoy::api::v2::auth::TlsCertificate& config);
virtual ~TlsCertificateSecretImpl() {}

const std::string& name() const override { return name_; }
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't believe this is ever used? It's just a lookup key for SecretManager, so it doesn't have to be part of the value itself.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

* @return the TlsCertificate secret. Returns nullptr if the secret is not found.
*/
virtual const TlsCertificateSecretSharedPtr
findTlsCertificateSecret(const std::string& name) const PURE;
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Secret seems to be redundant here, perhaps secretManager->findTlsCertificate() is meaningful enough?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Renamed

@mattklein123
Copy link
Member

@mangchiandjjoe please ping me once @lizan and @PiotrSikora sign off. I'm generally OK with what we have now so will take a final pass once that is complete.

namespace Secret {

/**
* An instance of the TlsCertificateSecret
Copy link
Contributor

Choose a reason for hiding this comment

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

Following the other change, this should be part of Envoy::Ssl and not Envoy::Secret (nothing Secret-specific here).

Also, s/TlsCertificateSecret/TlsCertificateConfig/g.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Renamed Secret::Secret to Ssl::TlsCertificateConfig

Copy link
Contributor

@PiotrSikora PiotrSikora left a comment

Choose a reason for hiding this comment

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

Other than moving stuff out from Envoy::Secret and renaming the interface, code LGTM.

@@ -172,6 +173,7 @@ envoy_cc_library(
hdrs = ["transport_socket_config.h"],
deps = [
"//include/envoy/network:transport_socket_interface",
"//include/envoy/secret:secret_manager_interface",
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: This isn't needed anymore.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

Copy link
Contributor Author

Choose a reason for hiding this comment

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

secret_manager_interface is required here again.

/**
* Return the instance of secret manager.
*/
virtual Secret::SecretManager& secretManager() PURE;
Copy link
Contributor

@PiotrSikora PiotrSikora Jun 8, 2018

Choose a reason for hiding this comment

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

Actually, looking at this again, what's the reasoning for Ssl::ContextManager having SecretManager()?
Should you use the global one from Server::InstanceImpl->secretManager()?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Consumer of the secret manager is the createTransportSocketFactory of UpstreamSslSocketFactory and DownstreamSslSocketFactory.

sslContextManager() of Server::Configuration::TransportSocketFactoryContext& context is
available in that function.

Is there a direct access to the global one?

Copy link
Contributor

Choose a reason for hiding this comment

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

Looking at the code, I'd rather move it one level up and put it in Configuration::TransportSocketFactoryContext next to the sslContextManager(). That way, it's still going to be accessible in createTransportSocketFactory().

Note: perhaps once you add dynamic SDS there will be a justification for tying Ssl::ContextManager and Secret::SecretManager together (although, it would be probably the other way around), but I don't think there is a reason to do that just yet.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ListenerImpl and ClusterInfoImpl are implementations of Configuration::TransportSocketFactoryContext interface.
If I need to add secretManager() to those class, I need to update constructors of derived classes and instance creation codes .
Adding secretManager() to Ssl::ContextManagerImpl seems to be easy to use.
Please let me know your opinion.

Copy link
Contributor

Choose a reason for hiding this comment

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

ListenerImpl and ClusterInfoImpl are implementations of Configuration::TransportSocketFactoryContext interface.
If I need to add secretManager() to those class, I need to update constructors of derived classes and instance creation codes .

Adding secretManager() to listeners should be as easy as adding this line:

Secret::SecretManager& secretManager() override { return parent_.server_.secretManager(); }

For clusters, you'd indeed need to change the constructor, and I'd even say that you might change it to pass ClusterManagerImpl, to match listener, and then you should be able to return parent_.server_.secretManager() as well (perhaps in separate PR).

cc @mattklein123 for thoughts on this.

Copy link
Member

Choose a reason for hiding this comment

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

I'm fine either way. If @PiotrSikora has thought about it and thinks it's better I would just take that approach.

Copy link
Contributor Author

@mangchiandjjoe mangchiandjjoe Jun 11, 2018

Choose a reason for hiding this comment

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

ok I am moving the method to the Configuration::TransportSocketFactoryContext interface.
But It requires 25 files changes and related test cases. It would take some time.

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

Thanks, approach LGTM. Just some last nits. Please also merge master. Not sure what you have the credentials build file change in there. @PiotrSikora @lizan PTAL for one last pass.


/**
* @param secret a protobuf message of envoy::api::v2::auth::Secret.
* @throw an EnovyEception when the secret type is not implemented yet.
Copy link
Member

Choose a reason for hiding this comment

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

typo "EnovyEception". "@throw an EnvoyException if the secret is invalid or not supported."

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

namespace Ssl {

/**
* An instance of the TlsCertificateConfig
Copy link
Member

Choose a reason for hiding this comment

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

This comment isn't very useful. Either remove or say more about what this is for. Also, periods end of sentences in this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed.

virtual const std::string& privateKey() const PURE;
};

typedef std::shared_ptr<TlsCertificateConfig> TlsCertificateConfigSharedPtr;
Copy link
Member

Choose a reason for hiding this comment

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

Do we need shared pointers if we are only going to return const raw pointers? Can we make this unique_ptr?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

To support dynamic secret, secrets managed by secrets managed referenced by multiple components.
It can be changed to unique_ptr but it needs to be change back to shared_ptr in the next PR.
I will update it to unique_ptr.


class SecretManagerImpl : public SecretManager, Logger::Loggable<Logger::Id::upstream> {
public:
SecretManagerImpl() {}
Copy link
Member

Choose a reason for hiding this comment

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

delete

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

const Ssl::TlsCertificateConfig* findTlsCertificate(const std::string& name) const override;

private:
// manages pairs of name and Ssl::TlsCertificateConfigSharedPtr
Copy link
Member

Choose a reason for hiding this comment

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

comment obvious, delete

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

@@ -299,6 +299,8 @@ class ListenerImpl : public Network::ListenerConfig,
Ssl::ContextManager& sslContextManager() override { return parent_.server_.sslContextManager(); }
Stats::Scope& statsScope() const override { return *listener_scope_; }

Secret::SecretManager& secretManager() override { return parent_.server_.secretManager(); }
Copy link
Member

Choose a reason for hiding this comment

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

nit: del newline before this line

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Removed

@mangchiandjjoe
Copy link
Contributor Author

@mattklein123 I am not sure how I can resolve the conflict.
I ran the "tools/check_format.py fix" command before I check in and it updated the file.
I copied the original content, but it makes format job failed.
It looks it was added recently by @lizan
Please let me know how I can resolve the issue?

@lizan
Copy link
Member

lizan commented Jun 13, 2018

since you're not changing any files in api try:

git checkout upstream/master -- api/config/ then commit

@lizan
Copy link
Member

lizan commented Jun 13, 2018

@mangchiandjjoe can you fix the build?

Copy link
Member

@lizan lizan left a comment

Choose a reason for hiding this comment

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

@mangchiandjjoe the one of last commits messed DCO again, can you squash once? Otherwise LGTM.

@mangchiandjjoe mangchiandjjoe force-pushed the sds_static_secret_integration branch from 9be0bc6 to c8fe612 Compare June 14, 2018 05:11
lizan
lizan previously approved these changes Jun 14, 2018
Copy link
Contributor

@PiotrSikora PiotrSikora left a comment

Choose a reason for hiding this comment

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

You can avoid a lot of those changes if you pass SecretManager to Upstream::ProdClusterManagerFactory() instead of passing it as the argument in all the calls.

Also, instead of passing and storing each of TransportSocketFactoryContext's members (Ssl::ContextManager, Secret::SecretManager and Stats::Store) separately, we should probably be passing TransportSocketFactoryContext itself.

@JimmyCYJ
Copy link
Member

@lizan and @PiotrSikora, this PR is ready for review now. could you please take a look?

Copy link
Member

@mattklein123 mattklein123 left a comment

Choose a reason for hiding this comment

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

This change is still missing doc updates (there are likely fields that need to be unhidden in the docs). In the interest of moving this along, I'm going to merge this, but please fix this and other nits in a follow up (there are still comments missing periods at end of sentences, etc.). Thank you!

@@ -46,6 +46,13 @@ bool FilterChainUtility::buildFilterChain(
void MainImpl::initialize(const envoy::config::bootstrap::v2::Bootstrap& bootstrap,
Instance& server,
Upstream::ClusterManagerFactory& cluster_manager_factory) {
const auto& secrets = bootstrap.static_resources().secrets();
ENVOY_LOG(info, "loading {} static secret(s)", secrets.size());
for (ssize_t i = 0; i < secrets.size(); i++) {
Copy link
Member

Choose a reason for hiding this comment

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

nit: this should be size_t I think

@mattklein123 mattklein123 merged commit 08c1399 into envoyproxy:master Jun 18, 2018
stevenzzzz added a commit to stevenzzzz/envoy that referenced this pull request Jun 19, 2018
…p.static_resources (envoyproxy#3465)"

revert to get clean master

This reverts commit 08c1399.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants