Skip to content

Commit e00feef

Browse files
committed
Merge #988: Overhaul tracker keys: option to disable auth key expiration
d7dfc3b feat: [#978] add semantic validation for configuration (Jose Celano) e8e935c feat: [#978] add a config option to disable cheking keys' expiration (Jose Celano) Pull request description: When the tracker is running in private mode you can disable checking keys' expiration in the configuration with: ```toml [core] private = false [core.private_mode] check_keys_expiration = false ``` All keys will be valid as long as they exist in the database. ACKs for top commit: josecelano: ACK d7dfc3b Tree-SHA512: 0605a43f2c9962efece92327261c2cc7448a35e8961260b14164162408288d87e7055ba1764778ba94d1849f575fe7110328d95830f8a5fa8ea062d186a52066
2 parents 680f642 + d7dfc3b commit e00feef

7 files changed

Lines changed: 122 additions & 8 deletions

File tree

packages/configuration/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
//!
66
//! The current version for configuration is [`v2`].
77
pub mod v2;
8+
pub mod validator;
89

910
use std::collections::HashMap;
1011
use std::env;

packages/configuration/src/v2/core.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
use derive_more::{Constructor, Display};
12
use serde::{Deserialize, Serialize};
23

34
use super::network::Network;
45
use crate::v2::database::Database;
6+
use crate::validator::{SemanticValidationError, Validator};
57
use crate::{AnnouncePolicy, TrackerPolicy};
68

79
#[allow(clippy::struct_excessive_bools)]
@@ -32,6 +34,10 @@ pub struct Core {
3234
#[serde(default = "Core::default_private")]
3335
pub private: bool,
3436

37+
// Configuration specific when the tracker is running in private mode.
38+
#[serde(default = "Core::default_private_mode")]
39+
pub private_mode: Option<PrivateMode>,
40+
3541
// Tracker policy configuration.
3642
#[serde(default = "Core::default_tracker_policy")]
3743
pub tracker_policy: TrackerPolicy,
@@ -54,6 +60,7 @@ impl Default for Core {
5460
listed: Self::default_listed(),
5561
net: Self::default_network(),
5662
private: Self::default_private(),
63+
private_mode: Self::default_private_mode(),
5764
tracker_policy: Self::default_tracker_policy(),
5865
tracker_usage_statistics: Self::default_tracker_usage_statistics(),
5966
}
@@ -85,10 +92,53 @@ impl Core {
8592
false
8693
}
8794

95+
fn default_private_mode() -> Option<PrivateMode> {
96+
if Self::default_private() {
97+
Some(PrivateMode::default())
98+
} else {
99+
None
100+
}
101+
}
102+
88103
fn default_tracker_policy() -> TrackerPolicy {
89104
TrackerPolicy::default()
90105
}
91106
fn default_tracker_usage_statistics() -> bool {
92107
true
93108
}
94109
}
110+
111+
/// Configuration specific when the tracker is running in private mode.
112+
#[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone, Copy, Constructor, Display)]
113+
pub struct PrivateMode {
114+
/// A flag to disable expiration date for peer keys.
115+
///
116+
/// When true, if the keys is not permanent the expiration date will be
117+
/// ignored. The key will be accepted even if it has expired.
118+
#[serde(default = "PrivateMode::default_check_keys_expiration")]
119+
pub check_keys_expiration: bool,
120+
}
121+
122+
impl Default for PrivateMode {
123+
fn default() -> Self {
124+
Self {
125+
check_keys_expiration: Self::default_check_keys_expiration(),
126+
}
127+
}
128+
}
129+
130+
impl PrivateMode {
131+
fn default_check_keys_expiration() -> bool {
132+
true
133+
}
134+
}
135+
136+
impl Validator for Core {
137+
fn validate(&self) -> Result<(), SemanticValidationError> {
138+
if self.private_mode.is_some() && !self.private {
139+
return Err(SemanticValidationError::UselessPrivateModeSection);
140+
}
141+
142+
Ok(())
143+
}
144+
}

packages/configuration/src/v2/mod.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@ use self::health_check_api::HealthCheckApi;
251251
use self::http_tracker::HttpTracker;
252252
use self::tracker_api::HttpApi;
253253
use self::udp_tracker::UdpTracker;
254+
use crate::validator::{SemanticValidationError, Validator};
254255
use crate::{Error, Info, Metadata, Version};
255256

256257
/// This configuration version
@@ -394,6 +395,12 @@ impl Configuration {
394395
}
395396
}
396397

398+
impl Validator for Configuration {
399+
fn validate(&self) -> Result<(), SemanticValidationError> {
400+
self.core.validate()
401+
}
402+
}
403+
397404
#[cfg(test)]
398405
mod tests {
399406

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//! Trait to validate semantic errors.
2+
//!
3+
//! Errors could involve more than one configuration option. Some configuration
4+
//! combinations can be incompatible.
5+
use thiserror::Error;
6+
7+
/// Errors that can occur validating the configuration.
8+
#[derive(Error, Debug)]
9+
pub enum SemanticValidationError {
10+
#[error("Private mode section in configuration can only be included when the tracker is running in private mode.")]
11+
UselessPrivateModeSection,
12+
}
13+
14+
pub trait Validator {
15+
/// # Errors
16+
///
17+
/// Will return an error if the configuration is invalid.
18+
fn validate(&self) -> Result<(), SemanticValidationError>;
19+
}

src/bootstrap/app.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use std::sync::Arc;
1515

1616
use torrust_tracker_clock::static_time;
17+
use torrust_tracker_configuration::validator::Validator;
1718
use torrust_tracker_configuration::Configuration;
1819
use tracing::info;
1920

@@ -24,10 +25,18 @@ use crate::core::Tracker;
2425
use crate::shared::crypto::ephemeral_instance_keys;
2526

2627
/// It loads the configuration from the environment and builds the main domain [`Tracker`] struct.
28+
///
29+
/// # Panics
30+
///
31+
/// Setup can file if the configuration is invalid.
2732
#[must_use]
2833
pub fn setup() -> (Configuration, Arc<Tracker>) {
2934
let configuration = initialize_configuration();
3035

36+
if let Err(e) = configuration.validate() {
37+
panic!("Configuration error: {e}");
38+
}
39+
3140
let tracker = initialize_with_configuration(&configuration);
3241

3342
info!("Configuration:\n{}", configuration.clone().mask_secrets().to_json());

src/core/auth.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
//! Tracker keys are tokens used to authenticate the tracker clients when the tracker runs
55
//! in `private` or `private_listed` modes.
66
//!
7-
//! There are services to [`generate_key`] and [`verify_key`] authentication keys.
7+
//! There are services to [`generate_key`] and [`verify_key_expiration`] authentication keys.
88
//!
99
//! Authentication keys are used only by [`HTTP`](crate::servers::http) trackers. All keys have an expiration time, that means
1010
//! they are only valid during a period of time. After that time the expiring key will no longer be valid.
@@ -33,7 +33,7 @@
3333
//!
3434
//! // And you can later verify it with:
3535
//!
36-
//! assert!(auth::verify_key(&expiring_key).is_ok());
36+
//! assert!(auth::verify_key_expiration(&expiring_key).is_ok());
3737
//! ```
3838
3939
use std::panic::Location;
@@ -106,7 +106,7 @@ pub fn generate_key(lifetime: Option<Duration>) -> PeerKey {
106106
///
107107
/// - `Error::KeyExpired` if `auth_key.valid_until` is past the `current_time`.
108108
/// - `Error::KeyInvalid` if `auth_key.valid_until` is past the `None`.
109-
pub fn verify_key(auth_key: &PeerKey) -> Result<(), Error> {
109+
pub fn verify_key_expiration(auth_key: &PeerKey) -> Result<(), Error> {
110110
let current_time: DurationSinceUnixEpoch = CurrentClock::now();
111111

112112
match auth_key.valid_until {
@@ -322,7 +322,7 @@ mod tests {
322322
fn should_be_generated_with_a_expiration_time() {
323323
let expiring_key = auth::generate_key(Some(Duration::new(9999, 0)));
324324

325-
assert!(auth::verify_key(&expiring_key).is_ok());
325+
assert!(auth::verify_key_expiration(&expiring_key).is_ok());
326326
}
327327

328328
#[test]
@@ -336,12 +336,12 @@ mod tests {
336336
// Mock the time has passed 10 sec.
337337
clock::Stopped::local_add(&Duration::from_secs(10)).unwrap();
338338

339-
assert!(auth::verify_key(&expiring_key).is_ok());
339+
assert!(auth::verify_key_expiration(&expiring_key).is_ok());
340340

341341
// Mock the time has passed another 10 sec.
342342
clock::Stopped::local_add(&Duration::from_secs(10)).unwrap();
343343

344-
assert!(auth::verify_key(&expiring_key).is_err());
344+
assert!(auth::verify_key_expiration(&expiring_key).is_err());
345345
}
346346
}
347347
}

src/core/mod.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -996,7 +996,16 @@ impl Tracker {
996996
location: Location::caller(),
997997
key: Box::new(key.clone()),
998998
}),
999-
Some(key) => auth::verify_key(key),
999+
Some(key) => match self.config.private_mode {
1000+
Some(private_mode) => {
1001+
if private_mode.check_keys_expiration {
1002+
return auth::verify_key_expiration(key);
1003+
}
1004+
1005+
Ok(())
1006+
}
1007+
None => auth::verify_key_expiration(key),
1008+
},
10001009
}
10011010
}
10021011

@@ -1779,8 +1788,9 @@ mod tests {
17791788
use std::time::Duration;
17801789

17811790
use torrust_tracker_clock::clock::Time;
1791+
use torrust_tracker_configuration::v2::core::PrivateMode;
17821792

1783-
use crate::core::auth;
1793+
use crate::core::auth::{self, Key};
17841794
use crate::core::tests::the_tracker::private_tracker;
17851795
use crate::CurrentClock;
17861796

@@ -1829,6 +1839,24 @@ mod tests {
18291839
assert!(tracker.verify_auth_key(&expiring_key.key()).await.is_ok());
18301840
}
18311841

1842+
#[tokio::test]
1843+
async fn it_should_accept_an_expired_key_when_checking_expiration_is_disabled_in_configuration() {
1844+
let mut tracker = private_tracker();
1845+
1846+
tracker.config.private_mode = Some(PrivateMode {
1847+
check_keys_expiration: false,
1848+
});
1849+
1850+
let past_time = Some(Duration::ZERO);
1851+
1852+
let expiring_key = tracker
1853+
.add_auth_key(Key::new("YZSl4lMZupRuOpSRC3krIKR5BPB14nrJ").unwrap(), past_time)
1854+
.await
1855+
.unwrap();
1856+
1857+
assert!(tracker.authenticate(&expiring_key.key()).await.is_ok());
1858+
}
1859+
18321860
#[tokio::test]
18331861
async fn it_should_fail_verifying_an_unregistered_authentication_key() {
18341862
let tracker = private_tracker();

0 commit comments

Comments
 (0)