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
25 changes: 24 additions & 1 deletion api/envoy/api/v2/route/route.proto
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,27 @@ message RouteAction {
// Connection properties hash policy.
ConnectionProperties connection_properties = 3;
}

// The flag that shortcircuits the hash computing. This field provides a
// 'fallback' style of configuration: "if a terminal policy doesn't work,
// fallback to rest of the policy list", it saves time when the terminal
// policy works.
//
// If true, and there is already a hash computed, ignore rest of the
// list of hash polices.
// For example, if the following hash methods are configured:
//
// ========= ========
// specifier terminal
// ========= ========
// Header A true
// Header B false
// Header C false
// ========= ========
//
// The generateHash process ends if policy "header A" generates a hash, as
// it's a terminal policy.
bool terminal = 4;
}

// Specifies a list of hash policies to use for ring hash load balancing. Each
Expand All @@ -596,7 +617,9 @@ message RouteAction {
// hash policies fail to generate a hash, no hash will be produced for
// the route. In this case, the behavior is the same as if no hash policies
// were specified (i.e. the ring hash load balancer will choose a random
// backend).
// backend). If a hash policy has the "terminal" attribute set to true, and
// there is already a hash generated, the hash is returned immediately,
// ignoring the rest of the hash policy list.
repeated HashPolicy hash_policy = 15;

// Indicates that a HTTP/1.1 client connection to this particular route is allowed to
Expand Down
16 changes: 16 additions & 0 deletions include/envoy/secret/secret_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,22 @@ class SecretManager {
virtual TlsCertificateConfigProviderSharedPtr findOrCreateTlsCertificateProvider(
const envoy::api::v2::core::ConfigSource& config_source, const std::string& config_name,
Server::Configuration::TransportSocketFactoryContext& secret_provider_context) PURE;

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

} // namespace Secret
Expand Down
3 changes: 2 additions & 1 deletion source/common/grpc/google_async_client_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,8 @@ void GoogleAsyncStreamImpl::resetStream() {
}

void GoogleAsyncStreamImpl::writeQueued() {
if (!call_initialized_ || finish_pending_ || write_pending_ || write_pending_queue_.empty()) {
if (!call_initialized_ || finish_pending_ || write_pending_ || write_pending_queue_.empty() ||
draining_cq_) {
return;
}
write_pending_ = true;
Expand Down
42 changes: 30 additions & 12 deletions source/common/router/config_impl.cc
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,20 @@ ShadowPolicyImpl::ShadowPolicyImpl(const envoy::api::v2::route::RouteAction& con
runtime_key_ = config.request_mirror_policy().runtime_key();
}

class HeaderHashMethod : public HashPolicyImpl::HashMethod {
class HashMethodImplBase : public HashPolicyImpl::HashMethod {
public:
HeaderHashMethod(const std::string& header_name) : header_name_(header_name) {}
HashMethodImplBase(bool terminal) : terminal_(terminal) {}

bool terminal() const override { return terminal_; }

private:
const bool terminal_;
};

class HeaderHashMethod : public HashMethodImplBase {
public:
HeaderHashMethod(const std::string& header_name, bool terminal)
: HashMethodImplBase(terminal), header_name_(header_name) {}

absl::optional<uint64_t> evaluate(const Network::Address::Instance*,
const Http::HeaderMap& headers,
Expand All @@ -96,18 +107,17 @@ class HeaderHashMethod : public HashPolicyImpl::HashMethod {
const Http::LowerCaseString header_name_;
};

class CookieHashMethod : public HashPolicyImpl::HashMethod {
class CookieHashMethod : public HashMethodImplBase {
public:
CookieHashMethod(const std::string& key, const std::string& path,
const absl::optional<std::chrono::seconds>& ttl)
: key_(key), path_(path), ttl_(ttl) {}
const absl::optional<std::chrono::seconds>& ttl, bool terminal)
: HashMethodImplBase(terminal), key_(key), path_(path), ttl_(ttl) {}

absl::optional<uint64_t> evaluate(const Network::Address::Instance*,
const Http::HeaderMap& headers,
const HashPolicy::AddCookieCallback add_cookie) const override {
absl::optional<uint64_t> hash;
std::string value = Http::Utility::parseCookieValue(headers, key_);

if (value.empty() && ttl_.has_value()) {
value = add_cookie(key_, path_, ttl_.value());
hash = HashUtil::xxHash64(value);
Expand All @@ -124,8 +134,10 @@ class CookieHashMethod : public HashPolicyImpl::HashMethod {
const absl::optional<std::chrono::seconds> ttl_;
};

class IpHashMethod : public HashPolicyImpl::HashMethod {
class IpHashMethod : public HashMethodImplBase {
public:
IpHashMethod(bool terminal) : HashMethodImplBase(terminal) {}

absl::optional<uint64_t> evaluate(const Network::Address::Instance* downstream_addr,
const Http::HeaderMap&,
const HashPolicy::AddCookieCallback) const override {
Expand Down Expand Up @@ -153,20 +165,21 @@ HashPolicyImpl::HashPolicyImpl(
for (auto& hash_policy : hash_policies) {
switch (hash_policy.policy_specifier_case()) {
case envoy::api::v2::route::RouteAction::HashPolicy::kHeader:
hash_impls_.emplace_back(new HeaderHashMethod(hash_policy.header().header_name()));
hash_impls_.emplace_back(
new HeaderHashMethod(hash_policy.header().header_name(), hash_policy.terminal()));
break;
case envoy::api::v2::route::RouteAction::HashPolicy::kCookie: {
absl::optional<std::chrono::seconds> ttl;
if (hash_policy.cookie().has_ttl()) {
ttl = std::chrono::seconds(hash_policy.cookie().ttl().seconds());
}
hash_impls_.emplace_back(
new CookieHashMethod(hash_policy.cookie().name(), hash_policy.cookie().path(), ttl));
hash_impls_.emplace_back(new CookieHashMethod(
hash_policy.cookie().name(), hash_policy.cookie().path(), ttl, hash_policy.terminal()));
break;
}
case envoy::api::v2::route::RouteAction::HashPolicy::kConnectionProperties:
if (hash_policy.connection_properties().source_ip()) {
hash_impls_.emplace_back(new IpHashMethod());
hash_impls_.emplace_back(new IpHashMethod(hash_policy.terminal()));
}
break;
default:
Expand All @@ -190,6 +203,11 @@ HashPolicyImpl::generateHash(const Network::Address::Instance* downstream_addr,
const uint64_t old_value = hash ? ((hash.value() << 1) | (hash.value() >> 63)) : 0;
hash = old_value ^ new_hash.value();
}
// If the policy is a terminal policy and a hash has been generated, ignore
// the rest of the hash policies.
if (hash_impl->terminal() && hash) {
break;
}
}
return hash;
}
Expand Down Expand Up @@ -851,7 +869,7 @@ RouteConstSharedPtr VirtualHostImpl::getRouteFromEntries(const Http::HeaderMap&

const VirtualHostImpl* RouteMatcher::findVirtualHost(const Http::HeaderMap& headers) const {
// Fast path the case where we only have a default virtual host.
if (virtual_hosts_.empty() && default_virtual_host_) {
if (virtual_hosts_.empty() && wildcard_virtual_host_suffixes_.empty() && default_virtual_host_) {
return default_virtual_host_.get();
}

Expand Down
3 changes: 3 additions & 0 deletions source/common/router/config_impl.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,9 @@ class HashPolicyImpl : public HashPolicy {
virtual absl::optional<uint64_t> evaluate(const Network::Address::Instance* downstream_addr,
const Http::HeaderMap& headers,
const AddCookieCallback add_cookie) const PURE;

// If the method is a terminal method, ignore rest of the hash policy chain.
virtual bool terminal() const PURE;
};

typedef std::unique_ptr<HashMethod> HashMethodPtr;
Expand Down
1 change: 1 addition & 0 deletions source/common/secret/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ envoy_cc_library(
"//source/common/config:resources_lib",
"//source/common/config:subscription_factory_lib",
"//source/common/protobuf:utility_lib",
"//source/common/ssl:certificate_validation_context_config_impl_lib",
"//source/common/ssl:tls_certificate_config_impl_lib",
],
)
46 changes: 32 additions & 14 deletions source/common/secret/sds_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "common/config/resources.h"
#include "common/config/subscription_factory.h"
#include "common/protobuf/utility.h"
#include "common/ssl/certificate_validation_context_config_impl.h"
#include "common/ssl/tls_certificate_config_impl.h"

namespace Envoy {
Expand All @@ -15,11 +16,11 @@ 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()> destructor_cb)
: 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), clean_up_(destructor_cb) {
const envoy::api::v2::core::ConfigSource& sds_config,
const std::string& sds_config_name, std::function<void()> destructor_cb)
: secret_hash_(0), local_info_(local_info), dispatcher_(dispatcher), random_(random),
stats_(stats), cluster_manager_(cluster_manager), sds_config_(sds_config),
sds_config_name_(sds_config_name), clean_up_(destructor_cb) {
// 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
Expand Down Expand Up @@ -59,15 +60,7 @@ void SdsApi::onConfigUpdate(const ResourceVector& resources, const std::string&)
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());

update_callback_manager_.runCallbacks();
}
updateConfigHelper(secret);

runInitializeCallbackIfAny();
}
Expand All @@ -84,5 +77,30 @@ void SdsApi::runInitializeCallbackIfAny() {
}
}

void TlsCertificateSdsApi::updateConfigHelper(const envoy::api::v2::auth::Secret& secret) {
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());

update_callback_manager_.runCallbacks();
}
}

void CertificateValidationContextSdsApi::updateConfigHelper(
const envoy::api::v2::auth::Secret& secret) {
const uint64_t new_hash = MessageUtil::hash(secret);
if (new_hash != secret_hash_ &&
secret.type_case() == envoy::api::v2::auth::Secret::TypeCase::kValidationContext) {
secret_hash_ = new_hash;
certificate_validation_context_secrets_ =
std::make_unique<Ssl::CertificateValidationContextConfigImpl>(secret.validation_context());

update_callback_manager_.runCallbacks();
}
}

} // namespace Secret
} // namespace Envoy
80 changes: 67 additions & 13 deletions source/common/secret/sds_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,12 @@ 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,
const envoy::api::v2::core::ConfigSource& sds_config, const std::string& sds_config_name,
std::function<void()> destructor_cb);

// Init::Target
Expand All @@ -43,14 +42,11 @@ class SdsApi : public Init::Target,
return MessageUtil::anyConvert<envoy::api::v2::auth::Secret>(resource).name();
}

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

Common::CallbackHandle* addUpdateCallback(std::function<void()> callback) override {
return update_callback_manager_.add(callback);
}
protected:
// Updates local storage of dynamic secrets and invokes callbacks.
virtual void updateConfigHelper(const envoy::api::v2::auth::Secret&) PURE;
uint64_t secret_hash_;
Common::CallbackManager<> update_callback_manager_;

private:
void runInitializeCallbackIfAny();
Expand All @@ -66,13 +62,71 @@ class SdsApi : public Init::Target,
std::function<void()> initialize_callback_;
const std::string sds_config_name_;

uint64_t secret_hash_;
Cleanup clean_up_;
};

typedef std::shared_ptr<SdsApi> SdsApiSharedPtr;

/**
* TlsCertificateSdsApi implementation maintains and updates dynamic TLS certificate secrets.
*/
class TlsCertificateSdsApi : public SdsApi, public TlsCertificateConfigProvider {
public:
TlsCertificateSdsApi(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()> destructor_cb)
: SdsApi(local_info, dispatcher, random, stats, cluster_manager, init_manager, sds_config,
sds_config_name, destructor_cb) {}

// SecretProvider
const Ssl::TlsCertificateConfig* secret() const override {
return tls_certificate_secrets_.get();
}
Common::CallbackHandle* addUpdateCallback(std::function<void()> callback) override {
return update_callback_manager_.add(callback);
}

private:
// SdsApi
void updateConfigHelper(const envoy::api::v2::auth::Secret& secret) override;

Ssl::TlsCertificateConfigPtr tls_certificate_secrets_;
Common::CallbackManager<> update_callback_manager_;
};

typedef std::unique_ptr<SdsApi> SdsApiPtr;
/**
* CertificateValidationContextSdsApi implementation maintains and updates dynamic certificate
* validation context secrets.
*/
class CertificateValidationContextSdsApi : public SdsApi,
public CertificateValidationContextConfigProvider {
public:
CertificateValidationContextSdsApi(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()> destructor_cb)
: SdsApi(local_info, dispatcher, random, stats, cluster_manager, init_manager, sds_config,
sds_config_name, destructor_cb) {}

// SecretProvider
const Ssl::CertificateValidationContextConfig* secret() const override {
return certificate_validation_context_secrets_.get();
}
Common::CallbackHandle* addUpdateCallback(std::function<void()> callback) override {
return update_callback_manager_.add(callback);
}

private:
// SdsApi
void updateConfigHelper(const envoy::api::v2::auth::Secret& secret) override;

Ssl::CertificateValidationContextConfigPtr certificate_validation_context_secrets_;
};

} // namespace Secret
} // namespace Envoy
Loading