Skip to content
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ syntax = "proto3";

package envoy.extensions.tracers.opentelemetry.samplers.v3;

import "envoy/config/core/v3/http_uri.proto";
import "envoy/config/core/v3/http_service.proto";

import "udpa/annotations/status.proto";

Expand All @@ -13,10 +13,9 @@ option go_package = "github.com/envoyproxy/go-control-plane/envoy/extensions/tra
option (udpa.annotations.file_status).package_version_status = ACTIVE;

// [#protodoc-title: Dynatrace Sampler config]

// Configuration for the Dynatrace Sampler extension.
// [#extension: envoy.tracers.opentelemetry.samplers.dynatrace]

// [#next-free-field: 6]
message DynatraceSamplerConfig {
// The Dynatrace tenant.
//
Expand All @@ -28,19 +27,21 @@ message DynatraceSamplerConfig {
// The value can be obtained from the Envoy deployment page in Dynatrace.
int32 cluster_id = 2;

// The HTTP URI to fetch the sampler configuration (root spans per minute). For example:
// The HTTP service to fetch the sampler configuration from the Dynatrace API (root spans per minute). For example:
//
// .. code-block:: yaml
//
// http_uri:
// uri: <tenant>.dev.dynatracelabs.com/api/v2/samplingConfiguration
// cluster: dynatrace
// timeout: 10s
// http_service:
// http_uri:
// cluster: dynatrace
// uri: <tenant>.dev.dynatracelabs.com/api/v2/samplingConfiguration
// timeout: 10s
// request_headers_to_add:
// - header:
// key : "authorization"
// value: "Api-Token dt..."
//
config.core.v3.HttpUri http_uri = 3;

// The access token to fetch the sampling configuration from the Dynatrace API
string token = 4;
config.core.v3.HttpService http_service = 3;

// Default number of root spans per minute, used when the value can't be obtained from the Dynatrace API.
//
Expand All @@ -49,5 +50,5 @@ message DynatraceSamplerConfig {
// - ``root_spans_per_minute`` is unset
// - ``root_spans_per_minute`` is set to 0
//
uint32 root_spans_per_minute = 5;
uint32 root_spans_per_minute = 4;
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ envoy_cc_library(
"//source/common/config:datasource_lib",
"//source/extensions/tracers/opentelemetry:opentelemetry_tracer_lib",
"//source/extensions/tracers/opentelemetry/samplers:sampler_lib",
"@envoy_api//envoy/config/core/v3:pkg_cc_proto",
"@envoy_api//envoy/extensions/tracers/opentelemetry/samplers/v3:pkg_cc_proto",
],
)
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,12 @@ SamplerConfigProviderImpl::SamplerConfigProviderImpl(
Server::Configuration::TracerFactoryContext& context,
const envoy::extensions::tracers::opentelemetry::samplers::v3::DynatraceSamplerConfig& config)
: cluster_manager_(context.serverFactoryContext().clusterManager()),
http_uri_(config.http_uri()),
authorization_header_value_(absl::StrCat("Api-Token ", config.token())),
sampler_config_(config.root_spans_per_minute()) {
http_uri_(config.http_service().http_uri()), sampler_config_(config.root_spans_per_minute()) {

for (const auto& header_value_option : config.http_service().request_headers_to_add()) {
parsed_headers_to_add_.push_back({Http::LowerCaseString(header_value_option.header().key()),
header_value_option.header().value()});
}

timer_ = context.serverFactoryContext().mainThreadDispatcher().createTimer([this]() -> void {
const auto thread_local_cluster = cluster_manager_.getThreadLocalCluster(http_uri_.cluster());
Expand All @@ -47,8 +50,9 @@ SamplerConfigProviderImpl::SamplerConfigProviderImpl(
} else {
Http::RequestMessagePtr message = Http::Utility::prepareHeaders(http_uri_);
message->headers().setReferenceMethod(Http::Headers::get().MethodValues.Get);
message->headers().setReference(Http::CustomHeaders::get().Authorization,
authorization_header_value_);
for (const auto& header_pair : parsed_headers_to_add_) {
message->headers().setReference(header_pair.first, header_pair.second);
}
Comment on lines -50 to +55
Copy link
Member

@wbpcode wbpcode Mar 27, 2024

Choose a reason for hiding this comment

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

LGTM overall except this one. Basically, we think the token or passwd should be kept in secret. And if an raw string is used then we should mark it as sensitive and the config_dump will hide it when dump all the configurations.

So, if you use the request_headers_to_add, the token may be exposed in the dumped configuration file.

If you think it's OK, then I am also OK to this design.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank's for the hint. I have discussed this with @joaopgrassi. We are aware of the problem but we would like to keep it as is for now. We would have to add the [(udpa.annotations.sensitive) = true] annotation to the http_servicefield. This would make it very hard to debug a wrong config.

Copy link
Member

Choose a reason for hiding this comment

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

My suggestion is to keep the token in another independent field and mark it as sensitive. But if you guys think the current implementation is OK. Then I am happy to approve.

Copy link
Member

Choose a reason for hiding this comment

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

Waiting your last confirmation, then I will merge this PR.

/wait-any

Copy link
Contributor

@joaopgrassi joaopgrassi Mar 28, 2024

Choose a reason for hiding this comment

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

@wbpcode We have discussed internally and we prefer to keep it as is. We already have the same situation in the HTTP exporter, where the token is also passed/used in the http headers, so we prefer to keep the config consistent to not confuse users. It also makes the Istio part of the configuration easier, because we can then re-use the headers of the HTTP exporter for the sampler, as in 99% of the cases they will be configured together by customers (http exporter + Dynatrace sampler).

active_request_ = thread_local_cluster->httpAsyncClient().send(
std::move(message), *this,
Http::AsyncClient::RequestOptions().setTimeout(std::chrono::milliseconds(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include <utility>
#include <vector>

#include "envoy/config/core/v3/http_service.pb.h"
#include "envoy/config/core/v3/http_uri.pb.h"
#include "envoy/extensions/tracers/opentelemetry/samplers/v3/dynatrace_sampler.pb.h"
#include "envoy/http/async_client.h"
#include "envoy/http/message.h"
Expand Down Expand Up @@ -65,7 +67,7 @@ class SamplerConfigProviderImpl : public SamplerConfigProvider,
Event::TimerPtr timer_;
Upstream::ClusterManager& cluster_manager_;
envoy::config::core::v3::HttpUri http_uri_;
const std::string authorization_header_value_;
std::vector<std::pair<const Http::LowerCaseString, const std::string>> parsed_headers_to_add_;
Http::AsyncClient::Request* active_request_{};
SamplerConfig sampler_config_;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,15 @@ class SamplerConfigProviderTest : public testing::Test {
const std::string yaml_string = R"EOF(
tenant: "abc12345"
cluster_id: -1743916452
token: "tokenval"
http_uri:
cluster: "cluster_name"
uri: "https://testhost.com/api/v2/samplingConfiguration"
timeout: 0.250s
http_service:
http_uri:
cluster: "cluster_name"
uri: "https://testhost.com/api/v2/samplingConfiguration"
timeout: 0.250s
request_headers_to_add:
- header:
key: "authorization"
value: "Api-Token tokenval"
root_spans_per_minute: 1000
)EOF";
TestUtility::loadFromYaml(yaml_string, proto_config_);
Expand All @@ -58,7 +62,6 @@ class SamplerConfigProviderTest : public testing::Test {
};

MATCHER_P(MessageMatcher, unusedArg, "") {
// prefix 'Api-Token' should be added to 'tokenval' set via SamplerConfigProvider constructor
return (arg->headers()
.get(Http::CustomHeaders::get().Authorization)[0]
->value()
Expand Down Expand Up @@ -206,11 +209,11 @@ TEST_F(SamplerConfigProviderTest, TestValueConfigured) {
const std::string yaml_string = R"EOF(
tenant: "abc12345"
cluster_id: -1743916452
token: "tokenval"
http_uri:
cluster: "cluster_name"
uri: "https://testhost.com/otlp/v1/traces"
timeout: 0.250s
http_service:
http_uri:
cluster: "cluster_name"
uri: "https://testhost.com/otlp/v1/traces"
timeout: 0.250s
root_spans_per_minute: 3456
)EOF";

Expand All @@ -226,11 +229,11 @@ TEST_F(SamplerConfigProviderTest, TestNoValueConfigured) {
const std::string yaml_string = R"EOF(
tenant: "abc12345"
cluster_id: -1743916452
token: "tokenval"
http_uri:
cluster: "cluster_name"
uri: "https://testhost.com/otlp/v1/traces"
timeout: 500s
http_service:
http_uri:
cluster: "cluster_name"
uri: "https://testhost.com/otlp/v1/traces"
timeout: 500s
)EOF";

envoy::extensions::tracers::opentelemetry::samplers::v3::DynatraceSamplerConfig proto_config;
Expand All @@ -246,11 +249,11 @@ TEST_F(SamplerConfigProviderTest, TestValueZeroConfigured) {
const std::string yaml_string = R"EOF(
tenant: "abc12345"
cluster_id: -1743916452
token: "tokenval"
http_uri:
cluster: "cluster_name"
uri: "https://testhost.com/otlp/v1/traces"
timeout: 0.250s
http_service:
http_uri:
cluster: "cluster_name"
uri: "https://testhost.com/otlp/v1/traces"
timeout: 0.250s
root_spans_per_minute: 0
)EOF";

Expand Down