From 16aa652821434a43e4efd7f8d85a63b9fae3ff3c Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Thu, 4 Jul 2024 11:23:02 +0100 Subject: [PATCH 1/2] chore(deps): add url dependency It will be use to parse the MySQL path in the configuration. FOr example: ```toml [core.database] driver = "MySQL" path = "mysql://db_user:db_user_secret_password@mysql:3306/torrust_tracker" ``` --- Cargo.lock | 1 + packages/configuration/Cargo.toml | 1 + 2 files changed, 2 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 2215edfeb..4e25fc4c4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4018,6 +4018,7 @@ dependencies = [ "toml", "torrust-tracker-located-error", "torrust-tracker-primitives", + "url", "uuid", ] diff --git a/packages/configuration/Cargo.toml b/packages/configuration/Cargo.toml index 51260d082..90c816344 100644 --- a/packages/configuration/Cargo.toml +++ b/packages/configuration/Cargo.toml @@ -25,6 +25,7 @@ thiserror = "1" toml = "0" torrust-tracker-located-error = { version = "3.0.0-alpha.12-develop", path = "../located-error" } torrust-tracker-primitives = { version = "3.0.0-alpha.12-develop", path = "../primitives" } +url = "2.5.2" [dev-dependencies] uuid = { version = "1", features = ["v4"] } From 4673514bdd401c5546f0da7ec71eb7dd4c4f9c1a Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Thu, 4 Jul 2024 11:28:44 +0100 Subject: [PATCH 2/2] 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) }