diff --git a/services/backup/Cargo.lock b/services/backup/Cargo.lock --- a/services/backup/Cargo.lock +++ b/services/backup/Cargo.lock @@ -513,6 +513,32 @@ "tracing", ] +[[package]] +name = "aws-sdk-secretsmanager" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502ccd2a5469223f03116ed1ef8d310bfe3caa0e8398b968439cd8e76e4ae91c" +dependencies = [ + "aws-credential-types", + "aws-endpoint", + "aws-http", + "aws-sig-auth", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-json", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand 1.9.0", + "http", + "regex", + "tokio-stream", + "tower", + "tracing", +] + [[package]] name = "aws-sdk-sso" version = "0.28.0" @@ -956,6 +982,7 @@ "anyhow", "aws-config", "aws-sdk-dynamodb", + "aws-sdk-secretsmanager", "aws-types", "base64 0.21.2", "bytes", diff --git a/services/blob/Cargo.lock b/services/blob/Cargo.lock --- a/services/blob/Cargo.lock +++ b/services/blob/Cargo.lock @@ -503,6 +503,32 @@ "url", ] +[[package]] +name = "aws-sdk-secretsmanager" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502ccd2a5469223f03116ed1ef8d310bfe3caa0e8398b968439cd8e76e4ae91c" +dependencies = [ + "aws-credential-types", + "aws-endpoint", + "aws-http", + "aws-sig-auth", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-json", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http", + "regex", + "tokio-stream", + "tower", + "tracing", +] + [[package]] name = "aws-sdk-sso" version = "0.28.0" @@ -1020,6 +1046,7 @@ "anyhow", "aws-config", "aws-sdk-dynamodb", + "aws-sdk-secretsmanager", "aws-types", "base64 0.21.0", "chrono", diff --git a/services/comm-services-lib/Cargo.lock b/services/comm-services-lib/Cargo.lock --- a/services/comm-services-lib/Cargo.lock +++ b/services/comm-services-lib/Cargo.lock @@ -473,6 +473,32 @@ "tracing", ] +[[package]] +name = "aws-sdk-secretsmanager" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502ccd2a5469223f03116ed1ef8d310bfe3caa0e8398b968439cd8e76e4ae91c" +dependencies = [ + "aws-credential-types", + "aws-endpoint", + "aws-http", + "aws-sig-auth", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-json", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http", + "regex", + "tokio-stream", + "tower", + "tracing", +] + [[package]] name = "aws-sdk-sso" version = "0.28.0" @@ -855,6 +881,7 @@ "anyhow", "aws-config", "aws-sdk-dynamodb", + "aws-sdk-secretsmanager", "aws-types", "base64 0.21.0", "bytes", diff --git a/services/comm-services-lib/Cargo.toml b/services/comm-services-lib/Cargo.toml --- a/services/comm-services-lib/Cargo.toml +++ b/services/comm-services-lib/Cargo.toml @@ -28,6 +28,7 @@ serde_json = "1.0" aws-config = "0.55" aws-sdk-dynamodb = "0.27" +aws-sdk-secretsmanager = "0.27" aws-types = "0.55" base64 = "0.21" chrono = "0.4" diff --git a/services/comm-services-lib/src/auth/mod.rs b/services/comm-services-lib/src/auth/mod.rs --- a/services/comm-services-lib/src/auth/mod.rs +++ b/services/comm-services-lib/src/auth/mod.rs @@ -1,3 +1,5 @@ +mod service; mod types; +pub use service::*; pub use types::*; diff --git a/services/comm-services-lib/src/auth/service.rs b/services/comm-services-lib/src/auth/service.rs new file mode 100644 --- /dev/null +++ b/services/comm-services-lib/src/auth/service.rs @@ -0,0 +1,75 @@ +use aws_sdk_secretsmanager::Client as SecretsManagerClient; +use chrono::{DateTime, Duration, NaiveDateTime, Utc}; + +use super::{AuthorizationCredential, ServicesAuthToken, UserIdentity}; + +const SECRET_NAME: &str = "servicesToken"; +/// duration for which we consider previous token valid +/// after rotation +const ROTATION_PROTECTION_PERIOD: i64 = 3; // seconds + +// AWS managed version tags for secrets +const AWSCURRENT: &str = "AWSCURRENT"; +const AWSPREVIOUS: &str = "AWSPREVIOUS"; + +async fn get_services_token_version( + client: &SecretsManagerClient, + version: impl Into, +) -> Result { + let result = client + .get_secret_value() + .secret_id(SECRET_NAME) + .version_id(version) + .send() + .await?; + + let token = result + .secret_string() + .expect("Services token secret is not a string. This should not happen"); + Ok(ServicesAuthToken::new(token.to_string())) +} + +async fn time_since_rotation( + client: &SecretsManagerClient, +) -> Result, aws_sdk_secretsmanager::Error> { + let result = client + .describe_secret() + .secret_id(SECRET_NAME) + .send() + .await?; + + let duration = result + .last_rotated_date() + .and_then(|date| date.to_millis().ok()) + .and_then(NaiveDateTime::from_timestamp_millis) + .map(|naive| DateTime::::from_utc(naive, Utc)) + .map(|last_rotated| Utc::now().signed_duration_since(last_rotated)); + Ok(duration) +} + +async fn verify_services_token( + client: &SecretsManagerClient, + token_to_verify: &ServicesAuthToken, +) -> Result { + let actual_token = get_services_token_version(client, AWSCURRENT).await?; + + // we need to always get it to achieve constant time eq + let last_rotated = time_since_rotation(client).await?; + let was_recently_rotated = last_rotated + .filter(|rotation_time| { + *rotation_time < Duration::seconds(ROTATION_PROTECTION_PERIOD) + }) + .is_some(); + + let is_valid = *token_to_verify == actual_token; + // token might have just been rotated. In this case check the previous token + // this case makes the function non-constant time, but it happens very rarely + if !is_valid && was_recently_rotated { + let previous_token = + get_services_token_version(client, AWSPREVIOUS).await?; + let previous_valid = *token_to_verify == previous_token; + return Ok(previous_valid); + } + + Ok(is_valid) +} diff --git a/services/commtest/Cargo.lock b/services/commtest/Cargo.lock --- a/services/commtest/Cargo.lock +++ b/services/commtest/Cargo.lock @@ -200,6 +200,32 @@ "tracing", ] +[[package]] +name = "aws-sdk-secretsmanager" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502ccd2a5469223f03116ed1ef8d310bfe3caa0e8398b968439cd8e76e4ae91c" +dependencies = [ + "aws-credential-types", + "aws-endpoint", + "aws-http", + "aws-sig-auth", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-json", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand 1.9.0", + "http", + "regex", + "tokio-stream", + "tower", + "tracing", +] + [[package]] name = "aws-sdk-sso" version = "0.28.0" @@ -628,6 +654,7 @@ "anyhow", "aws-config", "aws-sdk-dynamodb", + "aws-sdk-secretsmanager", "aws-types", "base64 0.21.3", "chrono", diff --git a/services/feature-flags/Cargo.lock b/services/feature-flags/Cargo.lock --- a/services/feature-flags/Cargo.lock +++ b/services/feature-flags/Cargo.lock @@ -357,6 +357,32 @@ "tracing", ] +[[package]] +name = "aws-sdk-secretsmanager" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502ccd2a5469223f03116ed1ef8d310bfe3caa0e8398b968439cd8e76e4ae91c" +dependencies = [ + "aws-credential-types", + "aws-endpoint", + "aws-http", + "aws-sig-auth", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-json", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http", + "regex", + "tokio-stream", + "tower", + "tracing", +] + [[package]] name = "aws-sdk-sso" version = "0.28.0" @@ -754,6 +780,7 @@ "anyhow", "aws-config", "aws-sdk-dynamodb", + "aws-sdk-secretsmanager", "aws-types", "base64", "chrono", diff --git a/services/reports/Cargo.lock b/services/reports/Cargo.lock --- a/services/reports/Cargo.lock +++ b/services/reports/Cargo.lock @@ -525,6 +525,32 @@ "tracing", ] +[[package]] +name = "aws-sdk-secretsmanager" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502ccd2a5469223f03116ed1ef8d310bfe3caa0e8398b968439cd8e76e4ae91c" +dependencies = [ + "aws-credential-types", + "aws-endpoint", + "aws-http", + "aws-sig-auth", + "aws-smithy-async", + "aws-smithy-client", + "aws-smithy-http", + "aws-smithy-http-tower", + "aws-smithy-json", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand 1.9.0", + "http", + "regex", + "tokio-stream", + "tower", + "tracing", +] + [[package]] name = "aws-sdk-sso" version = "0.28.0" @@ -952,6 +978,7 @@ "anyhow", "aws-config", "aws-sdk-dynamodb", + "aws-sdk-secretsmanager", "aws-types", "base64 0.21.3", "bytes",