mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-29 17:19:15 +08:00
refactor(config): Add new type for kms encrypted values (#1823)
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1811,6 +1811,7 @@ dependencies = [
|
|||||||
"diesel_models",
|
"diesel_models",
|
||||||
"error-stack",
|
"error-stack",
|
||||||
"external_services",
|
"external_services",
|
||||||
|
"masking",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"redis_interface",
|
"redis_interface",
|
||||||
"router_env",
|
"router_env",
|
||||||
|
|||||||
@ -20,24 +20,22 @@ request_body_limit = 16_384
|
|||||||
# Main SQL data store credentials
|
# Main SQL data store credentials
|
||||||
[master_database]
|
[master_database]
|
||||||
username = "db_user" # DB Username
|
username = "db_user" # DB Username
|
||||||
password = "db_pass" # DB Password. Only applicable when KMS is disabled.
|
password = "db_pass" # DB Password. Use base-64 encoded kms encrypted value here when kms is enabled
|
||||||
host = "localhost" # DB Host
|
host = "localhost" # DB Host
|
||||||
port = 5432 # DB Port
|
port = 5432 # DB Port
|
||||||
dbname = "hyperswitch_db" # Name of Database
|
dbname = "hyperswitch_db" # Name of Database
|
||||||
pool_size = 5 # Number of connections to keep open
|
pool_size = 5 # Number of connections to keep open
|
||||||
connection_timeout = 10 # Timeout for database connection in seconds
|
connection_timeout = 10 # Timeout for database connection in seconds
|
||||||
kms_encrypted_password = "" # Base64-encoded (KMS encrypted) ciphertext of the database password. Only applicable when KMS is enabled.
|
|
||||||
|
|
||||||
# Replica SQL data store credentials
|
# Replica SQL data store credentials
|
||||||
[replica_database]
|
[replica_database]
|
||||||
username = "replica_user" # DB Username
|
username = "replica_user" # DB Username
|
||||||
password = "replica_pass" # DB Password. Only applicable when KMS is disabled.
|
password = "db_pass" # DB Password. Use base-64 encoded kms encrypted value here when kms is enabled
|
||||||
host = "localhost" # DB Host
|
host = "localhost" # DB Host
|
||||||
port = 5432 # DB Port
|
port = 5432 # DB Port
|
||||||
dbname = "hyperswitch_db" # Name of Database
|
dbname = "hyperswitch_db" # Name of Database
|
||||||
pool_size = 5 # Number of connections to keep open
|
pool_size = 5 # Number of connections to keep open
|
||||||
connection_timeout = 10 # Timeout for database connection in seconds
|
connection_timeout = 10 # Timeout for database connection in seconds
|
||||||
kms_encrypted_password = "" # Base64-encoded (KMS encrypted) ciphertext of the database password. Only applicable when KMS is enabled.
|
|
||||||
|
|
||||||
# Redis credentials
|
# Redis credentials
|
||||||
[redis]
|
[redis]
|
||||||
@ -101,6 +99,8 @@ admin_api_key = "test_admin" # admin API key for admin authentication. Only
|
|||||||
kms_encrypted_admin_api_key = "" # Base64-encoded (KMS encrypted) ciphertext of the admin_api_key. Only applicable when KMS is enabled.
|
kms_encrypted_admin_api_key = "" # Base64-encoded (KMS encrypted) ciphertext of the admin_api_key. Only applicable when KMS is enabled.
|
||||||
jwt_secret = "secret" # JWT secret used for user authentication. Only applicable when KMS is disabled.
|
jwt_secret = "secret" # JWT secret used for user authentication. Only applicable when KMS is disabled.
|
||||||
kms_encrypted_jwt_secret = "" # Base64-encoded (KMS encrypted) ciphertext of the jwt_secret. Only applicable when KMS is enabled.
|
kms_encrypted_jwt_secret = "" # Base64-encoded (KMS encrypted) ciphertext of the jwt_secret. Only applicable when KMS is enabled.
|
||||||
|
recon_admin_api_key = "recon_test_admin" # recon_admin API key for recon authentication. Only applicable when KMS is disabled.
|
||||||
|
kms_encrypted_recon_admin_api_key = "" # Base64-encoded (KMS encrypted) ciphertext of the recon_admin_api_key. Only applicable when KMS is enabled
|
||||||
|
|
||||||
# Locker settings contain details for accessing a card locker, a
|
# Locker settings contain details for accessing a card locker, a
|
||||||
# PCI Compliant storage entity which stores payment method information
|
# PCI Compliant storage entity which stores payment method information
|
||||||
|
|||||||
@ -33,6 +33,8 @@ connection_timeout = 10
|
|||||||
[secrets]
|
[secrets]
|
||||||
admin_api_key = "test_admin"
|
admin_api_key = "test_admin"
|
||||||
master_enc_key = "73ad7bbbbc640c845a150f67d058b279849370cd2c1f3c67c4dd6c869213e13a"
|
master_enc_key = "73ad7bbbbc640c845a150f67d058b279849370cd2c1f3c67c4dd6c869213e13a"
|
||||||
|
jwt_secret = "secret"
|
||||||
|
recon_admin_api_key = "recon_test_admin"
|
||||||
|
|
||||||
[locker]
|
[locker]
|
||||||
host = ""
|
host = ""
|
||||||
|
|||||||
@ -40,6 +40,7 @@ pool_size = 5
|
|||||||
admin_api_key = "test_admin"
|
admin_api_key = "test_admin"
|
||||||
jwt_secret = "secret"
|
jwt_secret = "secret"
|
||||||
master_enc_key = "73ad7bbbbc640c845a150f67d058b279849370cd2c1f3c67c4dd6c869213e13a"
|
master_enc_key = "73ad7bbbbc640c845a150f67d058b279849370cd2c1f3c67c4dd6c869213e13a"
|
||||||
|
recon_admin_api_key = "recon_test_admin"
|
||||||
|
|
||||||
[locker]
|
[locker]
|
||||||
host = ""
|
host = ""
|
||||||
|
|||||||
@ -4,7 +4,7 @@
|
|||||||
//!
|
//!
|
||||||
|
|
||||||
use error_stack::{IntoReport, ResultExt};
|
use error_stack::{IntoReport, ResultExt};
|
||||||
use masking::{ExposeInterface, Secret, Strategy};
|
use masking::{ExposeInterface, PeekInterface, Secret, Strategy};
|
||||||
use quick_xml::de;
|
use quick_xml::de;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@ -428,6 +428,30 @@ impl ConfigExt for String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T, U> ConfigExt for Secret<T, U>
|
||||||
|
where
|
||||||
|
T: ConfigExt + Default + PartialEq<T>,
|
||||||
|
U: Strategy<T>,
|
||||||
|
{
|
||||||
|
fn is_default(&self) -> bool
|
||||||
|
where
|
||||||
|
T: Default + PartialEq<T>,
|
||||||
|
{
|
||||||
|
*self.peek() == T::default()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_empty_after_trim(&self) -> bool {
|
||||||
|
self.peek().is_empty_after_trim()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_default_or_empty(&self) -> bool
|
||||||
|
where
|
||||||
|
T: Default + PartialEq<T>,
|
||||||
|
{
|
||||||
|
self.peek().is_default() || self.peek().is_empty_after_trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Extension trait for deserializing XML strings using `quick-xml` crate
|
/// Extension trait for deserializing XML strings using `quick-xml` crate
|
||||||
pub trait XmlExt {
|
pub trait XmlExt {
|
||||||
///
|
///
|
||||||
|
|||||||
@ -28,6 +28,7 @@ tokio = { version = "1.28.2", features = ["macros", "rt-multi-thread"] }
|
|||||||
# First Party Crates
|
# First Party Crates
|
||||||
common_utils = { version = "0.1.0", path = "../common_utils", features = ["signals"] }
|
common_utils = { version = "0.1.0", path = "../common_utils", features = ["signals"] }
|
||||||
external_services = { version = "0.1.0", path = "../external_services" }
|
external_services = { version = "0.1.0", path = "../external_services" }
|
||||||
|
masking = { version = "0.1.0", path = "../masking" }
|
||||||
redis_interface = { version = "0.1.0", path = "../redis_interface" }
|
redis_interface = { version = "0.1.0", path = "../redis_interface" }
|
||||||
router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] }
|
router_env = { version = "0.1.0", path = "../router_env", features = ["log_extra_implicit_fields", "log_custom_entries_to_extra"] }
|
||||||
diesel_models = { version = "0.1.0", path = "../diesel_models", features = ["kv_store"] }
|
diesel_models = { version = "0.1.0", path = "../diesel_models", features = ["kv_store"] }
|
||||||
|
|||||||
@ -1,7 +1,9 @@
|
|||||||
use bb8::PooledConnection;
|
use bb8::PooledConnection;
|
||||||
use diesel::PgConnection;
|
use diesel::PgConnection;
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
use external_services::kms;
|
use external_services::kms::{self, decrypt::KmsDecrypt};
|
||||||
|
#[cfg(not(feature = "kms"))]
|
||||||
|
use masking::PeekInterface;
|
||||||
|
|
||||||
use crate::settings::Database;
|
use crate::settings::Database;
|
||||||
|
|
||||||
@ -20,17 +22,17 @@ pub async fn redis_connection(
|
|||||||
pub async fn diesel_make_pg_pool(
|
pub async fn diesel_make_pg_pool(
|
||||||
database: &Database,
|
database: &Database,
|
||||||
_test_transaction: bool,
|
_test_transaction: bool,
|
||||||
#[cfg(feature = "kms")] kms_config: &kms::KmsConfig,
|
#[cfg(feature = "kms")] kms_client: &'static kms::KmsClient,
|
||||||
) -> PgPool {
|
) -> PgPool {
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
let password = kms::get_kms_client(kms_config)
|
let password = database
|
||||||
|
.password
|
||||||
|
.decrypt_inner(kms_client)
|
||||||
.await
|
.await
|
||||||
.decrypt(&database.kms_encrypted_password)
|
.expect("Failed to decrypt password");
|
||||||
.await
|
|
||||||
.expect("Failed to KMS decrypt database password");
|
|
||||||
|
|
||||||
#[cfg(not(feature = "kms"))]
|
#[cfg(not(feature = "kms"))]
|
||||||
let password = &database.password;
|
let password = &database.password.peek();
|
||||||
|
|
||||||
let database_url = format!(
|
let database_url = format!(
|
||||||
"postgres://{}:{}@{}:{}/{}",
|
"postgres://{}:{}@{}:{}/{}",
|
||||||
|
|||||||
@ -22,7 +22,7 @@ impl Store {
|
|||||||
&config.master_database,
|
&config.master_database,
|
||||||
test_transaction,
|
test_transaction,
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
&config.kms,
|
external_services::kms::get_kms_client(&config.kms).await,
|
||||||
)
|
)
|
||||||
.await,
|
.await,
|
||||||
redis_conn: Arc::new(crate::connection::redis_connection(config).await),
|
redis_conn: Arc::new(crate::connection::redis_connection(config).await),
|
||||||
|
|||||||
@ -11,6 +11,11 @@ use serde::Deserialize;
|
|||||||
|
|
||||||
use crate::errors;
|
use crate::errors;
|
||||||
|
|
||||||
|
#[cfg(feature = "kms")]
|
||||||
|
pub type Password = kms::KmsValue;
|
||||||
|
#[cfg(not(feature = "kms"))]
|
||||||
|
pub type Password = masking::Secret<String>;
|
||||||
|
|
||||||
#[derive(clap::Parser, Default)]
|
#[derive(clap::Parser, Default)]
|
||||||
#[cfg_attr(feature = "vergen", command(version = router_env::version!()))]
|
#[cfg_attr(feature = "vergen", command(version = router_env::version!()))]
|
||||||
pub struct CmdLineConf {
|
pub struct CmdLineConf {
|
||||||
@ -35,15 +40,12 @@ pub struct Settings {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Database {
|
pub struct Database {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
#[cfg(not(feature = "kms"))]
|
pub password: Password,
|
||||||
pub password: String,
|
|
||||||
pub host: String,
|
pub host: String,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub dbname: String,
|
pub dbname: String,
|
||||||
pub pool_size: u32,
|
pub pool_size: u32,
|
||||||
pub connection_timeout: u64,
|
pub connection_timeout: u64,
|
||||||
#[cfg(feature = "kms")]
|
|
||||||
pub kms_encrypted_password: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize)]
|
#[derive(Debug, Clone, Deserialize)]
|
||||||
@ -60,15 +62,12 @@ impl Default for Database {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
username: String::new(),
|
username: String::new(),
|
||||||
#[cfg(not(feature = "kms"))]
|
password: Password::default(),
|
||||||
password: String::new(),
|
|
||||||
host: "localhost".into(),
|
host: "localhost".into(),
|
||||||
port: 5432,
|
port: 5432,
|
||||||
dbname: String::new(),
|
dbname: String::new(),
|
||||||
pool_size: 5,
|
pool_size: 5,
|
||||||
connection_timeout: 10,
|
connection_timeout: 10,
|
||||||
#[cfg(feature = "kms")]
|
|
||||||
kms_encrypted_password: String::new(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -107,23 +106,11 @@ impl Database {
|
|||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
#[cfg(not(feature = "kms"))]
|
when(self.password.is_default_or_empty(), || {
|
||||||
{
|
Err(errors::DrainerError::ConfigParsingError(
|
||||||
when(self.password.is_default_or_empty(), || {
|
"database user password must not be empty".into(),
|
||||||
Err(errors::DrainerError::ConfigParsingError(
|
))
|
||||||
"database user password must not be empty".into(),
|
})
|
||||||
))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "kms")]
|
|
||||||
{
|
|
||||||
when(self.kms_encrypted_password.is_default_or_empty(), || {
|
|
||||||
Err(errors::DrainerError::ConfigParsingError(
|
|
||||||
"database KMS encrypted password must not be empty".into(),
|
|
||||||
))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,10 @@ use aws_sdk_kms::{config::Region, primitives::Blob, Client};
|
|||||||
use base64::Engine;
|
use base64::Engine;
|
||||||
use common_utils::errors::CustomResult;
|
use common_utils::errors::CustomResult;
|
||||||
use error_stack::{IntoReport, ResultExt};
|
use error_stack::{IntoReport, ResultExt};
|
||||||
|
use masking::{PeekInterface, Secret};
|
||||||
use router_env::logger;
|
use router_env::logger;
|
||||||
|
/// decrypting data using the AWS KMS SDK.
|
||||||
|
pub mod decrypt;
|
||||||
|
|
||||||
use crate::{consts, metrics};
|
use crate::{consts, metrics};
|
||||||
|
|
||||||
@ -15,7 +18,7 @@ static KMS_CLIENT: tokio::sync::OnceCell<KmsClient> = tokio::sync::OnceCell::con
|
|||||||
|
|
||||||
/// Returns a shared KMS client, or initializes a new one if not previously initialized.
|
/// Returns a shared KMS client, or initializes a new one if not previously initialized.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub async fn get_kms_client(config: &KmsConfig) -> &KmsClient {
|
pub async fn get_kms_client(config: &KmsConfig) -> &'static KmsClient {
|
||||||
KMS_CLIENT.get_or_init(|| KmsClient::new(config)).await
|
KMS_CLIENT.get_or_init(|| KmsClient::new(config)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -113,6 +116,10 @@ pub enum KmsError {
|
|||||||
/// An error occurred UTF-8 decoding KMS decrypted output.
|
/// An error occurred UTF-8 decoding KMS decrypted output.
|
||||||
#[error("Failed to UTF-8 decode decryption output")]
|
#[error("Failed to UTF-8 decode decryption output")]
|
||||||
Utf8DecodingFailed,
|
Utf8DecodingFailed,
|
||||||
|
|
||||||
|
/// The KMS client has not been initialized.
|
||||||
|
#[error("The KMS client has not been initialized")]
|
||||||
|
KmsClientNotInitialized,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl KmsConfig {
|
impl KmsConfig {
|
||||||
@ -129,3 +136,14 @@ impl KmsConfig {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A wrapper around a KMS value that can be decrypted.
|
||||||
|
#[derive(Clone, Debug, Default, serde::Deserialize, Eq, PartialEq)]
|
||||||
|
#[serde(transparent)]
|
||||||
|
pub struct KmsValue(Secret<String>);
|
||||||
|
|
||||||
|
impl common_utils::ext_traits::ConfigExt for KmsValue {
|
||||||
|
fn is_empty_after_trim(&self) -> bool {
|
||||||
|
self.0.peek().is_empty_after_trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
34
crates/external_services/src/kms/decrypt.rs
Normal file
34
crates/external_services/src/kms/decrypt.rs
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
use common_utils::errors::CustomResult;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
/// This trait performs in place decryption of the structure on which this is implemented
|
||||||
|
pub trait KmsDecrypt {
|
||||||
|
/// The output type of the decryption
|
||||||
|
type Output;
|
||||||
|
/// Decrypts the structure given a KMS client
|
||||||
|
async fn decrypt_inner(self, kms_client: &KmsClient) -> CustomResult<Self::Output, KmsError>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
|
||||||
|
/// Tries to use the Singleton client to decrypt the structure
|
||||||
|
async fn try_decrypt_inner(self) -> CustomResult<Self::Output, KmsError>
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let client = KMS_CLIENT.get().ok_or(KmsError::KmsClientNotInitialized)?;
|
||||||
|
self.decrypt_inner(client).await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl KmsDecrypt for &KmsValue {
|
||||||
|
type Output = String;
|
||||||
|
async fn decrypt_inner(self, kms_client: &KmsClient) -> CustomResult<Self::Output, KmsError> {
|
||||||
|
kms_client
|
||||||
|
.decrypt(self.0.peek())
|
||||||
|
.await
|
||||||
|
.attach_printable("Failed to decrypt KMS value")
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,8 +1,10 @@
|
|||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
|
|
||||||
use api_models::{enums, payment_methods::RequiredFieldInfo};
|
use api_models::{enums, payment_methods::RequiredFieldInfo};
|
||||||
|
#[cfg(feature = "kms")]
|
||||||
|
use external_services::kms::KmsValue;
|
||||||
|
|
||||||
use super::settings::{ConnectorFields, PaymentMethodType, RequiredFieldFinal};
|
use super::settings::{ConnectorFields, Password, PaymentMethodType, RequiredFieldFinal};
|
||||||
|
|
||||||
impl Default for super::settings::Server {
|
impl Default for super::settings::Server {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
@ -21,35 +23,12 @@ impl Default for super::settings::Database {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
username: String::new(),
|
username: String::new(),
|
||||||
#[cfg(not(feature = "kms"))]
|
password: Password::default(),
|
||||||
password: String::new(),
|
|
||||||
host: "localhost".into(),
|
host: "localhost".into(),
|
||||||
port: 5432,
|
port: 5432,
|
||||||
dbname: String::new(),
|
dbname: String::new(),
|
||||||
pool_size: 5,
|
pool_size: 5,
|
||||||
connection_timeout: 10,
|
connection_timeout: 10,
|
||||||
#[cfg(feature = "kms")]
|
|
||||||
kms_encrypted_password: String::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for super::settings::Secrets {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self {
|
|
||||||
#[cfg(not(feature = "kms"))]
|
|
||||||
jwt_secret: "secret".into(),
|
|
||||||
#[cfg(not(feature = "kms"))]
|
|
||||||
admin_api_key: "test_admin".into(),
|
|
||||||
#[cfg(not(feature = "kms"))]
|
|
||||||
recon_admin_api_key: "recon_test_admin".into(),
|
|
||||||
master_enc_key: "".into(),
|
|
||||||
#[cfg(feature = "kms")]
|
|
||||||
kms_encrypted_jwt_secret: "".into(),
|
|
||||||
#[cfg(feature = "kms")]
|
|
||||||
kms_encrypted_admin_api_key: "".into(),
|
|
||||||
#[cfg(feature = "kms")]
|
|
||||||
kms_encrypted_recon_admin_api_key: "".into(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -809,7 +788,7 @@ impl Default for super::settings::ApiKeys {
|
|||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
kms_encrypted_hash_key: String::new(),
|
kms_encrypted_hash_key: KmsValue::default(),
|
||||||
|
|
||||||
/// Hex-encoded 32-byte long (64 characters long when hex-encoded) key used for calculating
|
/// Hex-encoded 32-byte long (64 characters long when hex-encoded) key used for calculating
|
||||||
/// hashes of API keys
|
/// hashes of API keys
|
||||||
|
|||||||
@ -1,60 +1,46 @@
|
|||||||
use common_utils::errors::CustomResult;
|
use common_utils::errors::CustomResult;
|
||||||
use external_services::kms;
|
use external_services::kms::{decrypt::KmsDecrypt, KmsClient, KmsError};
|
||||||
use masking::ExposeInterface;
|
use masking::ExposeInterface;
|
||||||
|
|
||||||
use crate::configs::settings;
|
use crate::configs::settings;
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
// This trait performs inplace decryption of the structure on which this is implemented
|
|
||||||
pub trait KmsDecrypt {
|
|
||||||
async fn decrypt_inner(self, kms_config: &kms::KmsConfig) -> CustomResult<Self, kms::KmsError>
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl KmsDecrypt for settings::Jwekey {
|
impl KmsDecrypt for settings::Jwekey {
|
||||||
async fn decrypt_inner(self, kms_config: &kms::KmsConfig) -> CustomResult<Self, kms::KmsError> {
|
type Output = Self;
|
||||||
let client = kms::get_kms_client(kms_config).await;
|
|
||||||
|
|
||||||
// If this pattern required repetition, a macro approach needs to be deviced
|
async fn decrypt_inner(
|
||||||
let (
|
mut self,
|
||||||
locker_encryption_key1,
|
kms_client: &KmsClient,
|
||||||
locker_encryption_key2,
|
) -> CustomResult<Self::Output, KmsError> {
|
||||||
locker_decryption_key1,
|
(
|
||||||
locker_decryption_key2,
|
self.locker_encryption_key2,
|
||||||
vault_encryption_key,
|
self.locker_decryption_key1,
|
||||||
vault_private_key,
|
self.locker_encryption_key1,
|
||||||
tunnel_private_key,
|
self.locker_decryption_key2,
|
||||||
|
self.vault_encryption_key,
|
||||||
|
self.vault_private_key,
|
||||||
|
self.tunnel_private_key,
|
||||||
) = tokio::try_join!(
|
) = tokio::try_join!(
|
||||||
client.decrypt(self.locker_encryption_key1),
|
kms_client.decrypt(self.locker_encryption_key1),
|
||||||
client.decrypt(self.locker_encryption_key2),
|
kms_client.decrypt(self.locker_encryption_key2),
|
||||||
client.decrypt(self.locker_decryption_key1),
|
kms_client.decrypt(self.locker_decryption_key1),
|
||||||
client.decrypt(self.locker_decryption_key2),
|
kms_client.decrypt(self.locker_decryption_key2),
|
||||||
client.decrypt(self.vault_encryption_key),
|
kms_client.decrypt(self.vault_encryption_key),
|
||||||
client.decrypt(self.vault_private_key),
|
kms_client.decrypt(self.vault_private_key),
|
||||||
client.decrypt(self.tunnel_private_key),
|
kms_client.decrypt(self.tunnel_private_key),
|
||||||
)?;
|
)?;
|
||||||
|
Ok(self)
|
||||||
Ok(Self {
|
|
||||||
locker_key_identifier1: self.locker_key_identifier1,
|
|
||||||
locker_key_identifier2: self.locker_key_identifier2,
|
|
||||||
locker_encryption_key1,
|
|
||||||
locker_encryption_key2,
|
|
||||||
locker_decryption_key1,
|
|
||||||
locker_decryption_key2,
|
|
||||||
vault_encryption_key,
|
|
||||||
vault_private_key,
|
|
||||||
tunnel_private_key,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
impl KmsDecrypt for settings::ActiveKmsSecrets {
|
impl KmsDecrypt for settings::ActiveKmsSecrets {
|
||||||
async fn decrypt_inner(self, kms_config: &kms::KmsConfig) -> CustomResult<Self, kms::KmsError> {
|
type Output = Self;
|
||||||
Ok(Self {
|
async fn decrypt_inner(
|
||||||
jwekey: self.jwekey.expose().decrypt_inner(kms_config).await?.into(),
|
mut self,
|
||||||
})
|
kms_client: &KmsClient,
|
||||||
|
) -> CustomResult<Self::Output, KmsError> {
|
||||||
|
self.jwekey = self.jwekey.expose().decrypt_inner(kms_client).await?.into();
|
||||||
|
Ok(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,10 @@ use crate::{
|
|||||||
core::errors::{ApplicationError, ApplicationResult},
|
core::errors::{ApplicationError, ApplicationResult},
|
||||||
env::{self, logger, Env},
|
env::{self, logger, Env},
|
||||||
};
|
};
|
||||||
|
#[cfg(feature = "kms")]
|
||||||
|
pub type Password = kms::KmsValue;
|
||||||
|
#[cfg(not(feature = "kms"))]
|
||||||
|
pub type Password = masking::Secret<String>;
|
||||||
|
|
||||||
#[derive(clap::Parser, Default)]
|
#[derive(clap::Parser, Default)]
|
||||||
#[cfg_attr(feature = "vergen", command(version = router_env::version!()))]
|
#[cfg_attr(feature = "vergen", command(version = router_env::version!()))]
|
||||||
@ -338,7 +342,7 @@ where
|
|||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Default, Deserialize, Clone)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Secrets {
|
pub struct Secrets {
|
||||||
#[cfg(not(feature = "kms"))]
|
#[cfg(not(feature = "kms"))]
|
||||||
@ -347,13 +351,13 @@ pub struct Secrets {
|
|||||||
pub admin_api_key: String,
|
pub admin_api_key: String,
|
||||||
#[cfg(not(feature = "kms"))]
|
#[cfg(not(feature = "kms"))]
|
||||||
pub recon_admin_api_key: String,
|
pub recon_admin_api_key: String,
|
||||||
pub master_enc_key: String,
|
pub master_enc_key: Password,
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
pub kms_encrypted_jwt_secret: String,
|
pub kms_encrypted_jwt_secret: kms::KmsValue,
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
pub kms_encrypted_admin_api_key: String,
|
pub kms_encrypted_admin_api_key: kms::KmsValue,
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
pub kms_encrypted_recon_admin_api_key: String,
|
pub kms_encrypted_recon_admin_api_key: kms::KmsValue,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
@ -414,15 +418,12 @@ pub struct Server {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub struct Database {
|
pub struct Database {
|
||||||
pub username: String,
|
pub username: String,
|
||||||
#[cfg(not(feature = "kms"))]
|
pub password: Password,
|
||||||
pub password: String,
|
|
||||||
pub host: String,
|
pub host: String,
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
pub dbname: String,
|
pub dbname: String,
|
||||||
pub pool_size: u32,
|
pub pool_size: u32,
|
||||||
pub connection_timeout: u64,
|
pub connection_timeout: u64,
|
||||||
#[cfg(feature = "kms")]
|
|
||||||
pub kms_encrypted_password: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Clone)]
|
#[derive(Debug, Deserialize, Clone)]
|
||||||
@ -566,7 +567,7 @@ pub struct ApiKeys {
|
|||||||
/// Base64-encoded (KMS encrypted) ciphertext of the key used for calculating hashes of API
|
/// Base64-encoded (KMS encrypted) ciphertext of the key used for calculating hashes of API
|
||||||
/// keys
|
/// keys
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
pub kms_encrypted_hash_key: String,
|
pub kms_encrypted_hash_key: kms::KmsValue,
|
||||||
|
|
||||||
/// Hex-encoded 32-byte long (64 characters long when hex-encoded) key used for calculating
|
/// Hex-encoded 32-byte long (64 characters long when hex-encoded) key used for calculating
|
||||||
/// hashes of API keys
|
/// hashes of API keys
|
||||||
|
|||||||
@ -99,23 +99,11 @@ impl super::settings::Database {
|
|||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
|
|
||||||
#[cfg(not(feature = "kms"))]
|
when(self.password.is_default_or_empty(), || {
|
||||||
{
|
Err(ApplicationError::InvalidConfigurationValueError(
|
||||||
when(self.password.is_default_or_empty(), || {
|
"database user password must not be empty".into(),
|
||||||
Err(ApplicationError::InvalidConfigurationValueError(
|
))
|
||||||
"database user password must not be empty".into(),
|
})
|
||||||
))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "kms")]
|
|
||||||
{
|
|
||||||
when(self.kms_encrypted_password.is_default_or_empty(), || {
|
|
||||||
Err(ApplicationError::InvalidConfigurationValueError(
|
|
||||||
"database KMS encrypted password must not be empty".into(),
|
|
||||||
))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,10 @@ use diesel::PgConnection;
|
|||||||
use error_stack::{IntoReport, ResultExt};
|
use error_stack::{IntoReport, ResultExt};
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
use external_services::kms;
|
use external_services::kms;
|
||||||
|
#[cfg(feature = "kms")]
|
||||||
|
use external_services::kms::decrypt::KmsDecrypt;
|
||||||
|
#[cfg(not(feature = "kms"))]
|
||||||
|
use masking::PeekInterface;
|
||||||
|
|
||||||
use crate::{configs::settings::Database, errors};
|
use crate::{configs::settings::Database, errors};
|
||||||
|
|
||||||
@ -41,17 +45,18 @@ pub async fn redis_connection(
|
|||||||
pub async fn diesel_make_pg_pool(
|
pub async fn diesel_make_pg_pool(
|
||||||
database: &Database,
|
database: &Database,
|
||||||
test_transaction: bool,
|
test_transaction: bool,
|
||||||
#[cfg(feature = "kms")] kms_config: &kms::KmsConfig,
|
#[cfg(feature = "kms")] kms_client: &kms::KmsClient,
|
||||||
) -> PgPool {
|
) -> PgPool {
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
let password = kms::get_kms_client(kms_config)
|
let password = database
|
||||||
.await
|
.password
|
||||||
.decrypt(&database.kms_encrypted_password)
|
.clone()
|
||||||
|
.decrypt_inner(kms_client)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to KMS decrypt database password");
|
.expect("Failed to KMS decrypt database password");
|
||||||
|
|
||||||
#[cfg(not(feature = "kms"))]
|
#[cfg(not(feature = "kms"))]
|
||||||
let password = &database.password;
|
let password = &database.password.peek();
|
||||||
|
|
||||||
let database_url = format!(
|
let database_url = format!(
|
||||||
"postgres://{}:{}@{}:{}/{}",
|
"postgres://{}:{}@{}:{}/{}",
|
||||||
|
|||||||
@ -27,19 +27,22 @@ const API_KEY_EXPIRY_NAME: &str = "API_KEY_EXPIRY";
|
|||||||
#[cfg(feature = "email")]
|
#[cfg(feature = "email")]
|
||||||
const API_KEY_EXPIRY_RUNNER: &str = "API_KEY_EXPIRY_WORKFLOW";
|
const API_KEY_EXPIRY_RUNNER: &str = "API_KEY_EXPIRY_WORKFLOW";
|
||||||
|
|
||||||
|
#[cfg(feature = "kms")]
|
||||||
|
use external_services::kms::decrypt::KmsDecrypt;
|
||||||
|
|
||||||
static HASH_KEY: tokio::sync::OnceCell<StrongSecret<[u8; PlaintextApiKey::HASH_KEY_LEN]>> =
|
static HASH_KEY: tokio::sync::OnceCell<StrongSecret<[u8; PlaintextApiKey::HASH_KEY_LEN]>> =
|
||||||
tokio::sync::OnceCell::const_new();
|
tokio::sync::OnceCell::const_new();
|
||||||
|
|
||||||
pub async fn get_hash_key(
|
pub async fn get_hash_key(
|
||||||
api_key_config: &settings::ApiKeys,
|
api_key_config: &settings::ApiKeys,
|
||||||
#[cfg(feature = "kms")] kms_config: &kms::KmsConfig,
|
#[cfg(feature = "kms")] kms_client: &kms::KmsClient,
|
||||||
) -> errors::RouterResult<&'static StrongSecret<[u8; PlaintextApiKey::HASH_KEY_LEN]>> {
|
) -> errors::RouterResult<&'static StrongSecret<[u8; PlaintextApiKey::HASH_KEY_LEN]>> {
|
||||||
HASH_KEY
|
HASH_KEY
|
||||||
.get_or_try_init(|| async {
|
.get_or_try_init(|| async {
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
let hash_key = kms::get_kms_client(kms_config)
|
let hash_key = api_key_config
|
||||||
.await
|
.kms_encrypted_hash_key
|
||||||
.decrypt(&api_key_config.kms_encrypted_hash_key)
|
.decrypt_inner(kms_client)
|
||||||
.await
|
.await
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
.attach_printable("Failed to KMS decrypt API key hashing key")?;
|
.attach_printable("Failed to KMS decrypt API key hashing key")?;
|
||||||
@ -130,7 +133,7 @@ impl PlaintextApiKey {
|
|||||||
pub async fn create_api_key(
|
pub async fn create_api_key(
|
||||||
state: &AppState,
|
state: &AppState,
|
||||||
api_key_config: &settings::ApiKeys,
|
api_key_config: &settings::ApiKeys,
|
||||||
#[cfg(feature = "kms")] kms_config: &kms::KmsConfig,
|
#[cfg(feature = "kms")] kms_client: &kms::KmsClient,
|
||||||
api_key: api::CreateApiKeyRequest,
|
api_key: api::CreateApiKeyRequest,
|
||||||
merchant_id: String,
|
merchant_id: String,
|
||||||
) -> RouterResponse<api::CreateApiKeyResponse> {
|
) -> RouterResponse<api::CreateApiKeyResponse> {
|
||||||
@ -150,7 +153,7 @@ pub async fn create_api_key(
|
|||||||
let hash_key = get_hash_key(
|
let hash_key = get_hash_key(
|
||||||
api_key_config,
|
api_key_config,
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
kms_config,
|
kms_client,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
let plaintext_api_key = PlaintextApiKey::new(consts::API_KEY_LENGTH);
|
let plaintext_api_key = PlaintextApiKey::new(consts::API_KEY_LENGTH);
|
||||||
@ -555,7 +558,7 @@ mod tests {
|
|||||||
let hash_key = get_hash_key(
|
let hash_key = get_hash_key(
|
||||||
&settings.api_keys,
|
&settings.api_keys,
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
&settings.kms,
|
external_services::kms::get_kms_client(&settings.kms).await,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@ -46,7 +46,7 @@ pub async fn api_key_create(
|
|||||||
state,
|
state,
|
||||||
&state.conf.api_keys,
|
&state.conf.api_keys,
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
&state.conf.kms,
|
external_services::kms::get_kms_client(&state.conf.kms).await,
|
||||||
payload,
|
payload,
|
||||||
merchant_id.clone(),
|
merchant_id.clone(),
|
||||||
)
|
)
|
||||||
|
|||||||
@ -1,6 +1,8 @@
|
|||||||
use actix_web::{web, Scope};
|
use actix_web::{web, Scope};
|
||||||
#[cfg(feature = "email")]
|
#[cfg(feature = "email")]
|
||||||
use external_services::email::{AwsSes, EmailClient};
|
use external_services::email::{AwsSes, EmailClient};
|
||||||
|
#[cfg(feature = "kms")]
|
||||||
|
use external_services::kms::{self, decrypt::KmsDecrypt};
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
#[cfg(feature = "dummy_connector")]
|
#[cfg(feature = "dummy_connector")]
|
||||||
@ -14,8 +16,6 @@ use super::{cache::*, health::*};
|
|||||||
use super::{configs::*, customers::*, mandates::*, payments::*, refunds::*};
|
use super::{configs::*, customers::*, mandates::*, payments::*, refunds::*};
|
||||||
#[cfg(feature = "oltp")]
|
#[cfg(feature = "oltp")]
|
||||||
use super::{ephemeral_key::*, payment_methods::*, webhooks::*};
|
use super::{ephemeral_key::*, payment_methods::*, webhooks::*};
|
||||||
#[cfg(feature = "kms")]
|
|
||||||
use crate::configs::kms;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
configs::settings,
|
configs::settings,
|
||||||
db::{MockDb, StorageImpl, StorageInterface},
|
db::{MockDb, StorageImpl, StorageInterface},
|
||||||
@ -64,6 +64,8 @@ impl AppState {
|
|||||||
storage_impl: StorageImpl,
|
storage_impl: StorageImpl,
|
||||||
shut_down_signal: oneshot::Sender<()>,
|
shut_down_signal: oneshot::Sender<()>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
#[cfg(feature = "kms")]
|
||||||
|
let kms_client = kms::get_kms_client(&conf.kms).await;
|
||||||
let testable = storage_impl == StorageImpl::PostgresqlTest;
|
let testable = storage_impl == StorageImpl::PostgresqlTest;
|
||||||
let store: Box<dyn StorageInterface> = match storage_impl {
|
let store: Box<dyn StorageInterface> = match storage_impl {
|
||||||
StorageImpl::Postgresql | StorageImpl::PostgresqlTest => {
|
StorageImpl::Postgresql | StorageImpl::PostgresqlTest => {
|
||||||
@ -74,12 +76,10 @@ impl AppState {
|
|||||||
|
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
#[allow(clippy::expect_used)]
|
#[allow(clippy::expect_used)]
|
||||||
let kms_secrets = kms::KmsDecrypt::decrypt_inner(
|
let kms_secrets = settings::ActiveKmsSecrets {
|
||||||
settings::ActiveKmsSecrets {
|
jwekey: conf.jwekey.clone().into(),
|
||||||
jwekey: conf.jwekey.clone().into(),
|
}
|
||||||
},
|
.decrypt_inner(kms_client)
|
||||||
&conf.kms,
|
|
||||||
)
|
|
||||||
.await
|
.await
|
||||||
.expect("Failed while performing KMS decryption");
|
.expect("Failed while performing KMS decryption");
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,9 @@ use std::sync::{atomic, Arc};
|
|||||||
|
|
||||||
use error_stack::{IntoReport, ResultExt};
|
use error_stack::{IntoReport, ResultExt};
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
use external_services::kms;
|
use external_services::kms::{self, decrypt::KmsDecrypt};
|
||||||
|
#[cfg(not(feature = "kms"))]
|
||||||
|
use masking::PeekInterface;
|
||||||
use redis_interface::{errors as redis_errors, PubsubInterface, RedisValue};
|
use redis_interface::{errors as redis_errors, PubsubInterface, RedisValue};
|
||||||
use tokio::sync::oneshot;
|
use tokio::sync::oneshot;
|
||||||
|
|
||||||
@ -166,11 +168,13 @@ impl Store {
|
|||||||
async_spawn!({
|
async_spawn!({
|
||||||
redis_clone.on_error(shut_down_signal).await;
|
redis_clone.on_error(shut_down_signal).await;
|
||||||
});
|
});
|
||||||
|
#[cfg(feature = "kms")]
|
||||||
|
let kms_client = kms::get_kms_client(&config.kms).await;
|
||||||
|
|
||||||
let master_enc_key = get_master_enc_key(
|
let master_enc_key = get_master_enc_key(
|
||||||
config,
|
config,
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
&config.kms,
|
kms_client,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
@ -179,7 +183,7 @@ impl Store {
|
|||||||
&config.master_database,
|
&config.master_database,
|
||||||
test_transaction,
|
test_transaction,
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
&config.kms,
|
kms_client,
|
||||||
)
|
)
|
||||||
.await,
|
.await,
|
||||||
#[cfg(feature = "olap")]
|
#[cfg(feature = "olap")]
|
||||||
@ -187,7 +191,7 @@ impl Store {
|
|||||||
&config.replica_database,
|
&config.replica_database,
|
||||||
test_transaction,
|
test_transaction,
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
&config.kms,
|
kms_client,
|
||||||
)
|
)
|
||||||
.await,
|
.await,
|
||||||
redis_conn,
|
redis_conn,
|
||||||
@ -248,13 +252,14 @@ impl Store {
|
|||||||
#[allow(clippy::expect_used)]
|
#[allow(clippy::expect_used)]
|
||||||
async fn get_master_enc_key(
|
async fn get_master_enc_key(
|
||||||
conf: &crate::configs::settings::Settings,
|
conf: &crate::configs::settings::Settings,
|
||||||
#[cfg(feature = "kms")] kms_config: &kms::KmsConfig,
|
#[cfg(feature = "kms")] kms_client: &kms::KmsClient,
|
||||||
) -> Vec<u8> {
|
) -> Vec<u8> {
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
let master_enc_key = hex::decode(
|
let master_enc_key = hex::decode(
|
||||||
kms::get_kms_client(kms_config)
|
conf.secrets
|
||||||
.await
|
.master_enc_key
|
||||||
.decrypt(&conf.secrets.master_enc_key)
|
.clone()
|
||||||
|
.decrypt_inner(kms_client)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to decrypt master enc key"),
|
.expect("Failed to decrypt master enc key"),
|
||||||
)
|
)
|
||||||
@ -262,7 +267,7 @@ async fn get_master_enc_key(
|
|||||||
|
|
||||||
#[cfg(not(feature = "kms"))]
|
#[cfg(not(feature = "kms"))]
|
||||||
let master_enc_key =
|
let master_enc_key =
|
||||||
hex::decode(&conf.secrets.master_enc_key).expect("Failed to decode from hex");
|
hex::decode(conf.secrets.master_enc_key.peek()).expect("Failed to decode from hex");
|
||||||
|
|
||||||
master_enc_key
|
master_enc_key
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4,7 +4,7 @@ use async_trait::async_trait;
|
|||||||
use common_utils::date_time;
|
use common_utils::date_time;
|
||||||
use error_stack::{report, IntoReport, ResultExt};
|
use error_stack::{report, IntoReport, ResultExt};
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
use external_services::kms;
|
use external_services::kms::{self, decrypt::KmsDecrypt};
|
||||||
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
|
use jsonwebtoken::{decode, Algorithm, DecodingKey, Validation};
|
||||||
use masking::{PeekInterface, StrongSecret};
|
use masking::{PeekInterface, StrongSecret};
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ where
|
|||||||
api_keys::get_hash_key(
|
api_keys::get_hash_key(
|
||||||
&config.api_keys,
|
&config.api_keys,
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
&config.kms,
|
kms::get_kms_client(&config.kms).await,
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
};
|
};
|
||||||
@ -151,14 +151,14 @@ static ADMIN_API_KEY: tokio::sync::OnceCell<StrongSecret<String>> =
|
|||||||
|
|
||||||
pub async fn get_admin_api_key(
|
pub async fn get_admin_api_key(
|
||||||
secrets: &settings::Secrets,
|
secrets: &settings::Secrets,
|
||||||
#[cfg(feature = "kms")] kms_config: &kms::KmsConfig,
|
#[cfg(feature = "kms")] kms_client: &kms::KmsClient,
|
||||||
) -> RouterResult<&'static StrongSecret<String>> {
|
) -> RouterResult<&'static StrongSecret<String>> {
|
||||||
ADMIN_API_KEY
|
ADMIN_API_KEY
|
||||||
.get_or_try_init(|| async {
|
.get_or_try_init(|| async {
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
let admin_api_key = kms::get_kms_client(kms_config)
|
let admin_api_key = secrets
|
||||||
.await
|
.kms_encrypted_admin_api_key
|
||||||
.decrypt(&secrets.kms_encrypted_admin_api_key)
|
.decrypt_inner(kms_client)
|
||||||
.await
|
.await
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
.attach_printable("Failed to KMS decrypt admin API key")?;
|
.attach_printable("Failed to KMS decrypt admin API key")?;
|
||||||
@ -191,7 +191,7 @@ where
|
|||||||
let admin_api_key = get_admin_api_key(
|
let admin_api_key = get_admin_api_key(
|
||||||
&conf.secrets,
|
&conf.secrets,
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
&conf.kms,
|
kms::get_kms_client(&conf.kms).await,
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@ -456,14 +456,14 @@ static JWT_SECRET: tokio::sync::OnceCell<StrongSecret<String>> = tokio::sync::On
|
|||||||
|
|
||||||
pub async fn get_jwt_secret(
|
pub async fn get_jwt_secret(
|
||||||
secrets: &settings::Secrets,
|
secrets: &settings::Secrets,
|
||||||
#[cfg(feature = "kms")] kms_config: &kms::KmsConfig,
|
#[cfg(feature = "kms")] kms_client: &kms::KmsClient,
|
||||||
) -> RouterResult<&'static StrongSecret<String>> {
|
) -> RouterResult<&'static StrongSecret<String>> {
|
||||||
JWT_SECRET
|
JWT_SECRET
|
||||||
.get_or_try_init(|| async {
|
.get_or_try_init(|| async {
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
let jwt_secret = kms::get_kms_client(kms_config)
|
let jwt_secret = secrets
|
||||||
.await
|
.kms_encrypted_jwt_secret
|
||||||
.decrypt(&secrets.kms_encrypted_jwt_secret)
|
.decrypt_inner(kms_client)
|
||||||
.await
|
.await
|
||||||
.change_context(errors::ApiErrorResponse::InternalServerError)
|
.change_context(errors::ApiErrorResponse::InternalServerError)
|
||||||
.attach_printable("Failed to KMS decrypt JWT secret")?;
|
.attach_printable("Failed to KMS decrypt JWT secret")?;
|
||||||
@ -484,7 +484,7 @@ where
|
|||||||
let secret = get_jwt_secret(
|
let secret = get_jwt_secret(
|
||||||
&conf.secrets,
|
&conf.secrets,
|
||||||
#[cfg(feature = "kms")]
|
#[cfg(feature = "kms")]
|
||||||
&conf.kms,
|
kms::get_kms_client(&conf.kms).await,
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
.peek()
|
.peek()
|
||||||
|
|||||||
Reference in New Issue
Block a user