From b5e01e59f4a586c4bb43958968f591b11ce0d859 Mon Sep 17 00:00:00 2001 From: Brian Marks Date: Fri, 5 Jun 2026 18:15:33 -0400 Subject: [PATCH] Exclude sensitive configurations from configuration telemetry Add a `sensitive` flag to the configuration definition (zai_config_entry and its memoized entry) so a DD_* configuration can be marked in its CONFIG(...) declaration in ext/configuration.h. The configuration-telemetry enqueue loop over the DD_* config table skips entries whose flag is set; DD_API_KEY and DD_TRACE_ENABLED carry it. Remove the OTLP header configurations (OTEL_EXPORTER_OTLP_HEADERS, OTEL_EXPORTER_OTLP_METRICS_HEADERS, OTEL_EXPORTER_OTLP_LOGS_HEADERS) from the OpenTelemetry SDK configuration whitelist so they are not tracked for telemetry. Derive the "sensitive": true markers in metadata/supported-configurations.json from the flag in ext/configuration.h, plus the OTLP header variants the generator lists. Add a .phpt test and extend the loader functional test to assert these configuration values do not appear in the enqueued configuration telemetry while non-sensitive configurations remain reported. Co-Authored-By: Claude Opus 4.8 --- ext/configuration.h | 4 +- ext/telemetry.c | 3 +- .../test_configuration_telemetry.php | 12 ++ metadata/supported-configurations.json | 6 +- profiling/src/bindings/mod.rs | 2 + profiling/src/config.rs | 24 +++ src/DDTrace/OpenTelemetry/Configuration.php | 6 +- tests/ext/telemetry/sensitive_config.phpt | 160 ++++++++++++++++++ tooling/generate-supported-configurations.sh | 84 ++++++++- zend_abstract_interface/config/config.c | 1 + zend_abstract_interface/config/config.h | 4 + 11 files changed, 296 insertions(+), 10 deletions(-) create mode 100644 tests/ext/telemetry/sensitive_config.phpt diff --git a/ext/configuration.h b/ext/configuration.h index d6402649dcb..f8ac40168c1 100644 --- a/ext/configuration.h +++ b/ext/configuration.h @@ -45,7 +45,7 @@ enum datadog_sidecar_connection_mode { CONFIG(STRING, DD_AGENT_HOST, "localhost", .ini_change = zai_config_system_ini_change) \ CONFIG(STRING, DD_DOGSTATSD_URL, "http://localhost:8125") \ CONFIG(STRING, DD_DOGSTATSD_HOST, "localhost") \ - CONFIG(STRING, DD_API_KEY, "", .ini_change = zai_config_system_ini_change) \ + CONFIG(STRING, DD_API_KEY, "", .ini_change = zai_config_system_ini_change, .sensitive = true) \ CONFIG(INT, DD_DOGSTATSD_PORT, "8125") \ CONFIG(STRING, DD_ENV, "", .ini_change = datadog_alter_dd_env, \ .env_config_fallback = ddtrace_conf_otel_resource_attributes_env) \ @@ -59,7 +59,7 @@ enum datadog_sidecar_connection_mode { CONFIG(BOOL, DD_TRACE_CLI_ENABLED, "true") \ CONFIG(BOOL, DD_TRACE_DEBUG, "false", .ini_change = datadog_alter_dd_trace_debug) \ CONFIG(BOOL, DD_TRACE_ENABLED, "true", .ini_change = datadog_alter_dd_trace_disabled_config, \ - .env_config_fallback = ddtrace_conf_otel_traces_exporter) \ + .env_config_fallback = ddtrace_conf_otel_traces_exporter, .sensitive = true) \ CONFIG(BOOL, DD_INSTRUMENTATION_TELEMETRY_ENABLED, "true", .ini_change = zai_config_system_ini_change) \ CONFIG(BOOL, DD_TRACE_HEALTH_METRICS_ENABLED, "false", .ini_change = zai_config_system_ini_change) \ CONFIG(DOUBLE, DD_TRACE_HEALTH_METRICS_HEARTBEAT_SAMPLE_RATE, "0.001") \ diff --git a/ext/telemetry.c b/ext/telemetry.c index 94757f8c6a6..ee0d29dacf6 100644 --- a/ext/telemetry.c +++ b/ext/telemetry.c @@ -98,8 +98,7 @@ void datadog_telemetry_finalize() { #if ZTS ini = zend_hash_find_ptr(EG(ini_directives), ini->name); #endif - if (cfg->names[0].len != sizeof("DD_TRACE_ENABLED") - 1 - || memcmp(cfg->names[0].ptr, "DD_TRACE_ENABLED", sizeof("DD_TRACE_ENABLED") - 1) != 0) { // DD_TRACE_ENABLED is meaningless: always off at rshutdown + if (!cfg->sensitive) { ddog_ConfigurationOrigin origin = DDOG_CONFIGURATION_ORIGIN_ENV_VAR; switch (cfg->name_index) { case ZAI_CONFIG_ORIGIN_DEFAULT: diff --git a/loader/tests/functional/test_configuration_telemetry.php b/loader/tests/functional/test_configuration_telemetry.php index 565e667a9cb..0984485fa3e 100644 --- a/loader/tests/functional/test_configuration_telemetry.php +++ b/loader/tests/functional/test_configuration_telemetry.php @@ -11,6 +11,8 @@ 'DD_INJECT_FORCE=true', 'DD_INJECTION_ENABLED=tracer', // Normally set by the injector 'DD_SERVICE=loader', + 'DD_API_KEY=SENTINEL_DD_API_KEY', + 'DD_VERSION=1.2.3-loader-test', ]); assertMatchesFormat($output, '%A"loaded_by_ssi":true%s%A'); @@ -22,3 +24,13 @@ assertContains($content, '{"name":"instrumentation_source","value":"ssi","origin":"default","config_id":null,"seq_id":null}'); assertContains($content, '{"name":"ssi_injection_enabled","value":"tracer","origin":"env_var","config_id":null,"seq_id":null}'); assertContains($content, '{"name":"ssi_forced_injection_enabled","value":"True","origin":"env_var","config_id":null,"seq_id":null}'); + +// Sensitive configurations are excluded from configuration telemetry: neither +// the name nor the value is enqueued. DD_API_KEY and DD_TRACE_ENABLED carry the +// `sensitive` flag. +assertNotContains($content, 'SENTINEL_DD_API_KEY'); +assertNotContains($content, '"name":"DD_API_KEY"'); +assertNotContains($content, '"name":"DD_TRACE_ENABLED"'); + +// Non-sensitive configurations are still reported. +assertContains($content, '{"name":"DD_VERSION","value":"1.2.3-loader-test","origin":"env_var","config_id":null,"seq_id":null}'); diff --git a/metadata/supported-configurations.json b/metadata/supported-configurations.json index f5f379f0988..9eff67374d3 100644 --- a/metadata/supported-configurations.json +++ b/metadata/supported-configurations.json @@ -12,7 +12,8 @@ { "implementation": "C", "type": "string", - "default": "" + "default": "", + "sensitive": true } ], "DD_API_SECURITY_ENABLED": [ @@ -1103,7 +1104,8 @@ { "implementation": "A", "type": "boolean", - "default": "true" + "default": "true", + "sensitive": true } ], "DD_TRACE_EXEC_ANALYTICS_ENABLED": [ diff --git a/profiling/src/bindings/mod.rs b/profiling/src/bindings/mod.rs index 220be2cd123..807ef30a797 100644 --- a/profiling/src/bindings/mod.rs +++ b/profiling/src/bindings/mod.rs @@ -701,6 +701,7 @@ pub struct ZaiConfigEntry { pub parser: zai_custom_parse, pub displayer: zai_custom_display, pub env_config_fallback: zai_env_config_fallback, + pub sensitive: bool, } #[repr(C)] @@ -717,6 +718,7 @@ pub struct ZaiConfigMemoizedEntry { pub parser: zai_custom_parse, pub displayer: zai_custom_display, pub env_config_fallback: zai_env_config_fallback, + pub sensitive: bool, pub original_on_modify: Option< unsafe extern "C" fn( entry: *mut zend_ini_entry, diff --git a/profiling/src/config.rs b/profiling/src/config.rs index db8eac79de1..e1c76afc09f 100644 --- a/profiling/src/config.rs +++ b/profiling/src/config.rs @@ -1019,6 +1019,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: Some(parse_profiling_enabled), displayer: Some(display_profiling_enabled), env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(ProfilingExperimentalFeaturesEnabled), @@ -1031,6 +1032,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: None, displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(ProfilingEndpointCollectionEnabled), @@ -1043,6 +1045,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: None, displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(ProfilingExperimentalCpuTimeEnabled), @@ -1055,6 +1058,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: None, displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(ProfilingAllocationEnabled), @@ -1067,6 +1071,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: None, displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(ProfilingAllocationSamplingDistance), @@ -1079,6 +1084,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: Some(parse_sampling_distance_filter), displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(ProfilingExperimentalHeapLiveEnabled), @@ -1091,6 +1097,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: None, displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(ProfilingTimelineEnabled), @@ -1103,6 +1110,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: None, displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(ProfilingExceptionEnabled), @@ -1115,6 +1123,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: None, displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(ProfilingExceptionMessageEnabled), @@ -1127,6 +1136,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: None, displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(ProfilingExceptionSamplingDistance), @@ -1139,6 +1149,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: Some(parse_sampling_distance_filter), displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(ProfilingExperimentalIOEnabled), @@ -1151,6 +1162,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: None, displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(ProfilingLogLevel), @@ -1163,6 +1175,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: Some(parse_level_filter), displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(ProfilingOutputPprof), @@ -1175,6 +1188,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: Some(parse_utf8_string), displayer: None, env_config_fallback: None, + sensitive: false, }, // At the moment, wall-time cannot be fully disabled. This only // controls automatic collection (manual collection is still @@ -1190,6 +1204,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: None, displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(AgentHost), @@ -1202,6 +1217,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: Some(parse_utf8_string), displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(Env), @@ -1214,6 +1230,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: Some(parse_utf8_string), displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(Service), @@ -1226,6 +1243,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: Some(parse_utf8_string), displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(Tags), @@ -1242,6 +1260,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: None, displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(TraceAgentPort), @@ -1254,6 +1273,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: Some(parse_utf8_string), displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(TraceAgentUrl), @@ -1266,6 +1286,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: Some(parse_utf8_string), displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(Version), @@ -1278,6 +1299,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: Some(parse_utf8_string), displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(GitCommitSha), @@ -1290,6 +1312,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: Some(parse_utf8_string), displayer: None, env_config_fallback: None, + sensitive: false, }, zai_config_entry { id: transmute::(GitRepositoryUrl), @@ -1302,6 +1325,7 @@ pub(crate) fn minit(module_number: libc::c_int) { parser: Some(parse_utf8_string), displayer: None, env_config_fallback: None, + sensitive: false, }, ] }; diff --git a/src/DDTrace/OpenTelemetry/Configuration.php b/src/DDTrace/OpenTelemetry/Configuration.php index 26f0d8ae9e6..a36282c35fd 100644 --- a/src/DDTrace/OpenTelemetry/Configuration.php +++ b/src/DDTrace/OpenTelemetry/Configuration.php @@ -23,9 +23,9 @@ 'OTEL_EXPORTER_OTLP_METRICS_ENDPOINT', 'OTEL_EXPORTER_OTLP_LOGS_ENDPOINT', 'OTEL_EXPORTER_OTLP_ENDPOINT', - 'OTEL_EXPORTER_OTLP_METRICS_HEADERS', - 'OTEL_EXPORTER_OTLP_LOGS_HEADERS', - 'OTEL_EXPORTER_OTLP_HEADERS', + // The OTLP header configurations (OTEL_EXPORTER_OTLP_HEADERS, + // OTEL_EXPORTER_OTLP_METRICS_HEADERS, OTEL_EXPORTER_OTLP_LOGS_HEADERS) are + // sensitive and intentionally not tracked for configuration telemetry. 'OTEL_EXPORTER_OTLP_METRICS_TIMEOUT', 'OTEL_EXPORTER_OTLP_LOGS_TIMEOUT', 'OTEL_EXPORTER_OTLP_TIMEOUT', diff --git a/tests/ext/telemetry/sensitive_config.phpt b/tests/ext/telemetry/sensitive_config.phpt new file mode 100644 index 00000000000..688a326e32c --- /dev/null +++ b/tests/ext/telemetry/sensitive_config.phpt @@ -0,0 +1,160 @@ +--TEST-- +Sensitive configurations are excluded from configuration telemetry +--SKIPIF-- + +--ENV-- +DD_TRACE_GENERATE_ROOT_SPAN=0 +DD_TRACE_AUTOFINISH_SPANS=1 +DD_INSTRUMENTATION_TELEMETRY_ENABLED=1 +DD_AGENT_HOST= +DD_AUTOLOAD_NO_COMPILE= +DD_TRACE_GIT_METADATA_ENABLED=0 +DD_API_KEY=SENTINEL_DD_API_KEY +DD_VERSION=1.2.3-sensitive-test +--INI-- +datadog.trace.agent_url="file://{PWD}/sensitive-config-telemetry.out" +--FILE-- + +--EXPECTF-- +OTEL_EXPORTER_OTLP_HEADERS whitelisted: bool(false) +OTEL_EXPORTER_OTLP_METRICS_HEADERS whitelisted: bool(false) +OTEL_EXPORTER_OTLP_LOGS_HEADERS whitelisted: bool(false) +Included +sentinel values in telemetry: array(0) { +} +DD_API_KEY reported: bool(false) +DD_TRACE_ENABLED reported: bool(false) +OTEL_EXPORTER_OTLP_HEADERS reported: bool(false) +OTEL_EXPORTER_OTLP_METRICS_HEADERS reported: bool(false) +OTEL_EXPORTER_OTLP_LOGS_HEADERS reported: bool(false) +OTEL_EXPORTER_OTLP_ENDPOINT reported: bool(true) +DD_VERSION reported: bool(true) +--CLEAN-- +original_on_modify = NULL; memoized->env_config_fallback = entry->env_config_fallback; memoized->ini_change = entry->ini_change; + memoized->sensitive = entry->sensitive; return memoized; } diff --git a/zend_abstract_interface/config/config.h b/zend_abstract_interface/config/config.h index d7598cf35fd..542f7dc74e6 100644 --- a/zend_abstract_interface/config/config.h +++ b/zend_abstract_interface/config/config.h @@ -45,6 +45,8 @@ struct zai_config_entry_s { zai_custom_parse parser; zai_custom_display displayer; zai_env_config_fallback env_config_fallback; + // When true, this configuration is excluded from configuration telemetry. + bool sensitive; }; struct zai_config_name_s { @@ -76,6 +78,8 @@ struct zai_config_memoized_entry_s { zai_custom_parse parser; zai_custom_display displayer; zai_env_config_fallback env_config_fallback; + // When true, this configuration is excluded from configuration telemetry. + bool sensitive; ZEND_INI_MH((*original_on_modify)); // when some other extension has registered that INI };