From 4673514bdd401c5546f0da7ec71eb7dd4c4f9c1a Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Thu, 4 Jul 2024 11:28:44 +0100 Subject: [PATCH] fix: [#948] mask secrets in logs --- packages/configuration/src/lib.rs | 4 +- packages/configuration/src/v2/database.rs | 41 +++++++++++++++++++- packages/configuration/src/v2/mod.rs | 14 ++++++- packages/configuration/src/v2/tracker_api.rs | 6 +++ src/bootstrap/app.rs | 2 +- 5 files changed, 62 insertions(+), 5 deletions(-) diff --git a/packages/configuration/src/lib.rs b/packages/configuration/src/lib.rs index e5bfa6eb7..32831409d 100644 --- a/packages/configuration/src/lib.rs +++ b/packages/configuration/src/lib.rs @@ -48,7 +48,7 @@ pub type AccessTokens = HashMap; pub const LATEST_VERSION: &str = "2"; /// Info about the configuration specification. -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Display)] +#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Display, Clone)] pub struct Metadata { #[serde(default = "Metadata::default_version")] #[serde(flatten)] @@ -70,7 +70,7 @@ impl Metadata { } /// The configuration version. -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Display)] +#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Display, Clone)] pub struct Version { #[serde(default = "Version::default_semver")] version: String, diff --git a/packages/configuration/src/v2/database.rs b/packages/configuration/src/v2/database.rs index b029175ce..932db552c 100644 --- a/packages/configuration/src/v2/database.rs +++ b/packages/configuration/src/v2/database.rs @@ -1,5 +1,6 @@ use serde::{Deserialize, Serialize}; use torrust_tracker_primitives::DatabaseDriver; +use url::Url; #[allow(clippy::struct_excessive_bools)] #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)] @@ -13,7 +14,7 @@ pub struct Database { /// For `Sqlite3`, the format is `path/to/database.db`, for example: /// `./storage/tracker/lib/database/sqlite3.db`. /// For `Mysql`, the format is `mysql://db_user:db_user_password:port/db_name`, for - /// example: `root:password@localhost:3306/torrust`. + /// example: `mysql://root:password@localhost:3306/torrust`. #[serde(default = "Database::default_path")] pub path: String, } @@ -35,4 +36,42 @@ impl Database { fn default_path() -> String { String::from("./storage/tracker/lib/database/sqlite3.db") } + + /// Masks secrets in the configuration. + /// + /// # Panics + /// + /// Will panic if the database path for `MySQL` is not a valid URL. + pub fn mask_secrets(&mut self) { + match self.driver { + DatabaseDriver::Sqlite3 => { + // Nothing to mask + } + DatabaseDriver::MySQL => { + let mut url = Url::parse(&self.path).expect("path for MySQL driver should be a valid URL"); + url.set_password(Some("***")).expect("url password should be changed"); + self.path = url.to_string(); + } + } + } +} + +#[cfg(test)] +mod tests { + + use torrust_tracker_primitives::DatabaseDriver; + + use super::Database; + + #[test] + fn it_should_allow_masking_the_mysql_user_password() { + let mut database = Database { + driver: DatabaseDriver::MySQL, + path: "mysql://root:password@localhost:3306/torrust".to_string(), + }; + + database.mask_secrets(); + + assert_eq!(database.path, "mysql://root:***@localhost:3306/torrust".to_string()); + } } diff --git a/packages/configuration/src/v2/mod.rs b/packages/configuration/src/v2/mod.rs index 3425dc0de..141bab00f 100644 --- a/packages/configuration/src/v2/mod.rs +++ b/packages/configuration/src/v2/mod.rs @@ -263,7 +263,7 @@ const CONFIG_OVERRIDE_PREFIX: &str = "TORRUST_TRACKER_CONFIG_OVERRIDE_"; const CONFIG_OVERRIDE_SEPARATOR: &str = "__"; /// Core configuration for the tracker. -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Default)] +#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Default, Clone)] pub struct Configuration { /// Configuration metadata. #[serde(flatten)] @@ -380,6 +380,18 @@ impl Configuration { // code-review: do we need to use Figment also to serialize into json? serde_json::to_string_pretty(self).expect("Could not encode JSON value") } + + /// Masks secrets in the configuration. + #[must_use] + pub fn mask_secrets(mut self) -> Self { + self.core.database.mask_secrets(); + + if let Some(ref mut api) = self.http_api { + api.mask_secrets(); + } + + self + } } #[cfg(test)] diff --git a/packages/configuration/src/v2/tracker_api.rs b/packages/configuration/src/v2/tracker_api.rs index 1a2e0cbf0..dbbff7995 100644 --- a/packages/configuration/src/v2/tracker_api.rs +++ b/packages/configuration/src/v2/tracker_api.rs @@ -61,6 +61,12 @@ impl HttpApi { pub fn override_admin_token(&mut self, api_admin_token: &str) { self.access_tokens.insert("admin".to_string(), api_admin_token.to_string()); } + + pub fn mask_secrets(&mut self) { + for token in self.access_tokens.values_mut() { + *token = "***".to_string(); + } + } } #[cfg(test)] diff --git a/src/bootstrap/app.rs b/src/bootstrap/app.rs index 023520507..cfb84a2d1 100644 --- a/src/bootstrap/app.rs +++ b/src/bootstrap/app.rs @@ -30,7 +30,7 @@ pub fn setup() -> (Configuration, Arc) { let tracker = initialize_with_configuration(&configuration); - info!("Configuration:\n{}", configuration.to_json()); + info!("Configuration:\n{}", configuration.clone().mask_secrets().to_json()); (configuration, tracker) }