Skip to main content

libdd_trace_obfuscation/
obfuscation_config.rs

1// Copyright 2023-Present Datadog, Inc. https://www.datadoghq.com/
2// SPDX-License-Identifier: Apache-2.0
3
4use log::{debug, error};
5use serde::Deserialize;
6use std::{collections::HashSet, env};
7
8use libdd_common::config::parse_env;
9
10use crate::{
11    replacer::{self, ReplaceRule},
12    sql::{SqlObfuscateConfig, SqlObfuscationMode},
13};
14
15#[derive(Debug, Default, Deserialize)]
16#[serde(default, deny_unknown_fields)]
17pub struct MemcachedConfig {
18    pub enabled: bool,
19    pub keep_command: bool,
20}
21
22#[derive(Debug, Default, Deserialize)]
23#[serde(default, deny_unknown_fields)]
24pub struct CreditCardConfig {
25    pub enabled: bool,
26    pub luhn: bool,
27    pub keep_values: HashSet<String>,
28}
29
30pub type JsonStringTransformer = fn(&str) -> String;
31
32#[derive(Debug, Default, Clone, Deserialize)]
33#[serde(default, deny_unknown_fields)]
34pub struct JsonObfuscatorConfig {
35    pub enabled: bool,
36    /// `keep_keys` will specify a set of keys for which their values will
37    /// not be obfuscated.
38    pub keep_keys: HashSet<String>,
39    /// `transform_keys` will specify a set of keys for which their values will be transformed
40    /// through `transformer`
41    #[serde(skip)]
42    pub transform_keys: HashSet<String>,
43    /// `transformer` is an optional String -> String function which will transform values
44    /// specified in `transform_keys`
45    #[serde(skip)]
46    pub transformer: Option<JsonStringTransformer>,
47}
48
49impl JsonObfuscatorConfig {
50    #[must_use]
51    pub fn disabled() -> Self {
52        Self {
53            enabled: false,
54            ..Default::default()
55        }
56    }
57
58    #[must_use]
59    pub fn enabled() -> Self {
60        Self {
61            enabled: true,
62            ..Default::default()
63        }
64    }
65}
66
67#[derive(Debug, Default, Deserialize)]
68#[serde(default, deny_unknown_fields)]
69pub struct RedisConfig {
70    pub enabled: bool,
71    pub remove_all_args: bool,
72}
73
74#[derive(Debug, Default, Deserialize)]
75#[serde(default, deny_unknown_fields)]
76pub struct HttpConfig {
77    // pub enabled: bool,
78    pub remove_query_string: bool,
79    pub remove_paths_with_digits: bool,
80}
81
82#[derive(Debug, Default, Deserialize)]
83#[serde(default, deny_unknown_fields)]
84pub struct ObfuscationConfig {
85    pub tag_replace_rules: Option<Vec<ReplaceRule>>,
86    pub http: HttpConfig,
87    pub memcached: MemcachedConfig,
88    pub redis: RedisConfig,
89    pub valkey: RedisConfig,
90    pub credit_cards: CreditCardConfig,
91    pub sql: SqlObfuscateConfig,
92    pub elasticsearch: JsonObfuscatorConfig,
93    pub opensearch: JsonObfuscatorConfig,
94    pub mongodb: JsonObfuscatorConfig,
95}
96
97// Small subset of `ObfuscationConfig` for stats obfuscation only
98#[derive(Default)]
99pub struct StatsObfuscationConfig {
100    pub sql_obfuscation_mode: SqlObfuscationMode,
101}
102
103impl ObfuscationConfig {
104    /// Builds the obfuscation config from environment variables.
105    ///
106    /// # Errors
107    ///
108    /// Returns an error if one of the regular expressions used by the config cannot be compiled.
109    pub fn new() -> Result<Self, Box<dyn std::error::Error>> {
110        let tag_replace_rules: Option<Vec<ReplaceRule>> = match env::var("DD_APM_REPLACE_TAGS") {
111            Ok(replace_rules_str) => match replacer::parse_rules_from_string(&replace_rules_str) {
112                Ok(res) => {
113                    debug!("Successfully parsed DD_APM_REPLACE_TAGS: {res:?}");
114                    Some(res)
115                }
116                Err(e) => {
117                    error!("Failed to parse DD_APM_REPLACE_TAGS: {e}");
118                    None
119                }
120            },
121            Err(_) => None,
122        };
123        let http_remove_query_string =
124            parse_env::bool("DD_APM_OBFUSCATION_HTTP_REMOVE_QUERY_STRING").unwrap_or(false);
125        let http_remove_path_digits =
126            parse_env::bool("DD_APM_OBFUSCATION_HTTP_REMOVE_PATHS_WITH_DIGITS").unwrap_or(false);
127        let obfuscation_redis_enabled =
128            parse_env::bool("DD_APM_OBFUSCATION_REDIS_ENABLED").unwrap_or(false);
129        let obfuscation_redis_remove_all_args =
130            parse_env::bool("DD_APM_OBFUSCATION_REDIS_REMOVE_ALL_ARGS").unwrap_or(false);
131
132        let obfuscate_memcached =
133            parse_env::bool("DD_APM_OBFUSCATION_MEMCACHED_ENABLED").unwrap_or(false);
134
135        Ok(Self {
136            tag_replace_rules,
137            http: HttpConfig {
138                remove_query_string: http_remove_query_string,
139                remove_paths_with_digits: http_remove_path_digits,
140            },
141            memcached: MemcachedConfig {
142                enabled: obfuscate_memcached,
143                keep_command: true,
144            },
145            credit_cards: CreditCardConfig {
146                enabled: true,
147                luhn: true,
148                keep_values: HashSet::new(),
149            },
150            redis: RedisConfig {
151                enabled: obfuscation_redis_enabled,
152                remove_all_args: obfuscation_redis_remove_all_args,
153            },
154            ..Default::default()
155        })
156    }
157}