mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-27 15:37:02 +00:00
refactor: Reimplement bucket replication system with enhanced architecture (#590)
* feat:refactor replication * use aws sdk for replication client * refactor/replication * merge main * fix lifecycle test
This commit is contained in:
@@ -111,7 +111,7 @@ impl LocalKmsClient {
|
||||
|
||||
/// Get the file path for a master key
|
||||
fn master_key_path(&self, key_id: &str) -> PathBuf {
|
||||
self.config.key_dir.join(format!("{}.key", key_id))
|
||||
self.config.key_dir.join(format!("{key_id}.key"))
|
||||
}
|
||||
|
||||
/// Load a master key from disk
|
||||
@@ -334,12 +334,11 @@ impl KmsClient for LocalKmsClient {
|
||||
if let Some(actual_value) = envelope.encryption_context.get(key) {
|
||||
if actual_value != expected_value {
|
||||
return Err(KmsError::context_mismatch(format!(
|
||||
"Context mismatch for key '{}': expected '{}', got '{}'",
|
||||
key, expected_value, actual_value
|
||||
"Context mismatch for key '{key}': expected '{expected_value}', got '{actual_value}'"
|
||||
)));
|
||||
}
|
||||
} else {
|
||||
return Err(KmsError::context_mismatch(format!("Missing context key '{}'", key)));
|
||||
return Err(KmsError::context_mismatch(format!("Missing context key '{key}'")));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -720,14 +719,14 @@ impl KmsBackend for LocalKmsBackend {
|
||||
.client
|
||||
.load_master_key(key_id)
|
||||
.await
|
||||
.map_err(|_| crate::error::KmsError::key_not_found(format!("Key {} not found", key_id)))?;
|
||||
.map_err(|_| crate::error::KmsError::key_not_found(format!("Key {key_id} not found")))?;
|
||||
|
||||
let (deletion_date_str, deletion_date_dt) = if request.force_immediate.unwrap_or(false) {
|
||||
// For immediate deletion, actually delete the key from filesystem
|
||||
let key_path = self.client.master_key_path(key_id);
|
||||
tokio::fs::remove_file(&key_path)
|
||||
.await
|
||||
.map_err(|e| crate::error::KmsError::internal_error(format!("Failed to delete key file: {}", e)))?;
|
||||
.map_err(|e| crate::error::KmsError::internal_error(format!("Failed to delete key file: {e}")))?;
|
||||
|
||||
// Remove from cache
|
||||
let mut cache = self.client.key_cache.write().await;
|
||||
@@ -773,9 +772,9 @@ impl KmsBackend for LocalKmsBackend {
|
||||
let key_path = self.client.master_key_path(key_id);
|
||||
let content = tokio::fs::read(&key_path)
|
||||
.await
|
||||
.map_err(|e| crate::error::KmsError::internal_error(format!("Failed to read key file: {}", e)))?;
|
||||
.map_err(|e| crate::error::KmsError::internal_error(format!("Failed to read key file: {e}")))?;
|
||||
let stored_key: crate::backends::local::StoredMasterKey = serde_json::from_slice(&content)
|
||||
.map_err(|e| crate::error::KmsError::internal_error(format!("Failed to parse stored key: {}", e)))?;
|
||||
.map_err(|e| crate::error::KmsError::internal_error(format!("Failed to parse stored key: {e}")))?;
|
||||
|
||||
// Decrypt the existing key material to preserve it
|
||||
let existing_key_material = if let Some(ref cipher) = self.client.master_cipher {
|
||||
@@ -821,13 +820,10 @@ impl KmsBackend for LocalKmsBackend {
|
||||
.client
|
||||
.load_master_key(key_id)
|
||||
.await
|
||||
.map_err(|_| crate::error::KmsError::key_not_found(format!("Key {} not found", key_id)))?;
|
||||
.map_err(|_| crate::error::KmsError::key_not_found(format!("Key {key_id} not found")))?;
|
||||
|
||||
if master_key.status != KeyStatus::PendingDeletion {
|
||||
return Err(crate::error::KmsError::invalid_key_state(format!(
|
||||
"Key {} is not pending deletion",
|
||||
key_id
|
||||
)));
|
||||
return Err(crate::error::KmsError::invalid_key_state(format!("Key {key_id} is not pending deletion")));
|
||||
}
|
||||
|
||||
// Cancel the deletion by resetting the state
|
||||
|
||||
@@ -89,10 +89,10 @@ impl VaultKmsClient {
|
||||
|
||||
let settings = settings_builder
|
||||
.build()
|
||||
.map_err(|e| KmsError::backend_error(format!("Failed to build Vault client settings: {}", e)))?;
|
||||
.map_err(|e| KmsError::backend_error(format!("Failed to build Vault client settings: {e}")))?;
|
||||
|
||||
let client =
|
||||
VaultClient::new(settings).map_err(|e| KmsError::backend_error(format!("Failed to create Vault client: {}", e)))?;
|
||||
VaultClient::new(settings).map_err(|e| KmsError::backend_error(format!("Failed to create Vault client: {e}")))?;
|
||||
|
||||
info!("Successfully connected to Vault at {}", config.address);
|
||||
|
||||
@@ -144,7 +144,7 @@ impl VaultKmsClient {
|
||||
|
||||
kv2::set(&self.client, &self.kv_mount, &path, key_data)
|
||||
.await
|
||||
.map_err(|e| KmsError::backend_error(format!("Failed to store key in Vault: {}", e)))?;
|
||||
.map_err(|e| KmsError::backend_error(format!("Failed to store key in Vault: {e}")))?;
|
||||
|
||||
debug!("Stored key {} in Vault at path {}", key_id, path);
|
||||
Ok(())
|
||||
@@ -176,7 +176,7 @@ impl VaultKmsClient {
|
||||
let secret: VaultKeyData = kv2::read(&self.client, &self.kv_mount, &path).await.map_err(|e| match e {
|
||||
vaultrs::error::ClientError::ResponseWrapError => KmsError::key_not_found(key_id),
|
||||
vaultrs::error::ClientError::APIError { code: 404, .. } => KmsError::key_not_found(key_id),
|
||||
_ => KmsError::backend_error(format!("Failed to read key from Vault: {}", e)),
|
||||
_ => KmsError::backend_error(format!("Failed to read key from Vault: {e}")),
|
||||
})?;
|
||||
|
||||
debug!("Retrieved key {} from Vault, tags: {:?}", key_id, secret.tags);
|
||||
@@ -200,7 +200,7 @@ impl VaultKmsClient {
|
||||
debug!("Key path doesn't exist in Vault (404), returning empty list");
|
||||
Ok(Vec::new())
|
||||
}
|
||||
Err(e) => Err(KmsError::backend_error(format!("Failed to list keys in Vault: {}", e))),
|
||||
Err(e) => Err(KmsError::backend_error(format!("Failed to list keys in Vault: {e}"))),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,7 +214,7 @@ impl VaultKmsClient {
|
||||
.await
|
||||
.map_err(|e| match e {
|
||||
vaultrs::error::ClientError::APIError { code: 404, .. } => KmsError::key_not_found(key_id),
|
||||
_ => KmsError::backend_error(format!("Failed to delete key metadata from Vault: {}", e)),
|
||||
_ => KmsError::backend_error(format!("Failed to delete key metadata from Vault: {e}")),
|
||||
})?;
|
||||
|
||||
debug!("Permanently deleted key {} metadata from Vault at path {}", key_id, path);
|
||||
@@ -649,7 +649,7 @@ impl KmsBackend for VaultKmsBackend {
|
||||
let mut key_metadata = match self.describe_key(describe_request).await {
|
||||
Ok(response) => response.key_metadata,
|
||||
Err(_) => {
|
||||
return Err(crate::error::KmsError::key_not_found(format!("Key {} not found", key_id)));
|
||||
return Err(crate::error::KmsError::key_not_found(format!("Key {key_id} not found")));
|
||||
}
|
||||
};
|
||||
|
||||
@@ -705,15 +705,12 @@ impl KmsBackend for VaultKmsBackend {
|
||||
let mut key_metadata = match self.describe_key(describe_request).await {
|
||||
Ok(response) => response.key_metadata,
|
||||
Err(_) => {
|
||||
return Err(crate::error::KmsError::key_not_found(format!("Key {} not found", key_id)));
|
||||
return Err(crate::error::KmsError::key_not_found(format!("Key {key_id} not found")));
|
||||
}
|
||||
};
|
||||
|
||||
if key_metadata.key_state != KeyState::PendingDeletion {
|
||||
return Err(crate::error::KmsError::invalid_key_state(format!(
|
||||
"Key {} is not pending deletion",
|
||||
key_id
|
||||
)));
|
||||
return Err(crate::error::KmsError::invalid_key_state(format!("Key {key_id} is not pending deletion")));
|
||||
}
|
||||
|
||||
// Cancel the deletion by resetting the state
|
||||
|
||||
@@ -315,7 +315,7 @@ impl KmsConfig {
|
||||
config.backend = match backend_type.to_lowercase().as_str() {
|
||||
"local" => KmsBackend::Local,
|
||||
"vault" => KmsBackend::Vault,
|
||||
_ => return Err(KmsError::configuration_error(format!("Unknown KMS backend: {}", backend_type))),
|
||||
_ => return Err(KmsError::configuration_error(format!("Unknown KMS backend: {backend_type}"))),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ impl ObjectEncryptionService {
|
||||
self.kms_manager
|
||||
.create_key(create_req)
|
||||
.await
|
||||
.map_err(|e| KmsError::backend_error(format!("Failed to auto-create SSE-S3 key {}: {}", actual_key_id, e)))?;
|
||||
.map_err(|e| KmsError::backend_error(format!("Failed to auto-create SSE-S3 key {actual_key_id}: {e}")))?;
|
||||
}
|
||||
} else {
|
||||
// For SSE-KMS, key must exist
|
||||
@@ -235,7 +235,7 @@ impl ObjectEncryptionService {
|
||||
key_id: actual_key_id.to_string(),
|
||||
};
|
||||
self.kms_manager.describe_key(describe_req).await.map_err(|_| {
|
||||
KmsError::invalid_operation(format!("SSE-KMS key '{}' not found. Please create it first.", actual_key_id))
|
||||
KmsError::invalid_operation(format!("SSE-KMS key '{actual_key_id}' not found. Please create it first."))
|
||||
})?;
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ impl ObjectEncryptionService {
|
||||
.kms_manager
|
||||
.generate_data_key(request)
|
||||
.await
|
||||
.map_err(|e| KmsError::backend_error(format!("Failed to generate data key: {}", e)))?;
|
||||
.map_err(|e| KmsError::backend_error(format!("Failed to generate data key: {e}")))?;
|
||||
|
||||
let plaintext_key = data_key.plaintext_key;
|
||||
|
||||
@@ -325,7 +325,7 @@ impl ObjectEncryptionService {
|
||||
.kms_manager
|
||||
.decrypt(decrypt_request)
|
||||
.await
|
||||
.map_err(|e| KmsError::backend_error(format!("Failed to decrypt data key: {}", e)))?;
|
||||
.map_err(|e| KmsError::backend_error(format!("Failed to decrypt data key: {e}")))?;
|
||||
|
||||
// Create cipher
|
||||
let cipher = create_cipher(&algorithm, &decrypt_response.plaintext)?;
|
||||
@@ -379,7 +379,7 @@ impl ObjectEncryptionService {
|
||||
// Validate key MD5 if provided
|
||||
if let Some(expected_md5) = customer_key_md5 {
|
||||
let actual_md5 = md5::compute(customer_key);
|
||||
let actual_md5_hex = format!("{:x}", actual_md5);
|
||||
let actual_md5_hex = format!("{actual_md5:x}");
|
||||
if actual_md5_hex != expected_md5.to_lowercase() {
|
||||
return Err(KmsError::validation_error("Customer key MD5 mismatch"));
|
||||
}
|
||||
@@ -487,12 +487,11 @@ impl ObjectEncryptionService {
|
||||
Some(actual_value) if actual_value == expected_value => continue,
|
||||
Some(actual_value) => {
|
||||
return Err(KmsError::context_mismatch(format!(
|
||||
"Context mismatch for '{}': expected '{}', got '{}'",
|
||||
key, expected_value, actual_value
|
||||
"Context mismatch for '{key}': expected '{expected_value}', got '{actual_value}'"
|
||||
)));
|
||||
}
|
||||
None => {
|
||||
return Err(KmsError::context_mismatch(format!("Missing context key '{}'", key)));
|
||||
return Err(KmsError::context_mismatch(format!("Missing context key '{key}'")));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -562,13 +561,13 @@ impl ObjectEncryptionService {
|
||||
.ok_or_else(|| KmsError::validation_error("Missing IV header"))?;
|
||||
let iv = base64::engine::general_purpose::STANDARD
|
||||
.decode(iv)
|
||||
.map_err(|e| KmsError::validation_error(format!("Invalid IV: {}", e)))?;
|
||||
.map_err(|e| KmsError::validation_error(format!("Invalid IV: {e}")))?;
|
||||
|
||||
let tag = if let Some(tag_str) = headers.get("x-rustfs-encryption-tag") {
|
||||
Some(
|
||||
base64::engine::general_purpose::STANDARD
|
||||
.decode(tag_str)
|
||||
.map_err(|e| KmsError::validation_error(format!("Invalid tag: {}", e)))?,
|
||||
.map_err(|e| KmsError::validation_error(format!("Invalid tag: {e}")))?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
@@ -577,14 +576,14 @@ impl ObjectEncryptionService {
|
||||
let encrypted_data_key = if let Some(key_str) = headers.get("x-rustfs-encryption-key") {
|
||||
base64::engine::general_purpose::STANDARD
|
||||
.decode(key_str)
|
||||
.map_err(|e| KmsError::validation_error(format!("Invalid encrypted key: {}", e)))?
|
||||
.map_err(|e| KmsError::validation_error(format!("Invalid encrypted key: {e}")))?
|
||||
} else {
|
||||
Vec::new() // Empty for SSE-C
|
||||
};
|
||||
|
||||
let encryption_context = if let Some(context_str) = headers.get("x-rustfs-encryption-context") {
|
||||
serde_json::from_str(context_str)
|
||||
.map_err(|e| KmsError::validation_error(format!("Invalid encryption context: {}", e)))?
|
||||
.map_err(|e| KmsError::validation_error(format!("Invalid encryption context: {e}")))?
|
||||
} else {
|
||||
HashMap::new()
|
||||
};
|
||||
|
||||
@@ -225,7 +225,7 @@ impl KmsError {
|
||||
impl From<url::ParseError> for KmsError {
|
||||
fn from(error: url::ParseError) -> Self {
|
||||
Self::ConfigurationError {
|
||||
message: format!("Invalid URL: {}", error),
|
||||
message: format!("Invalid URL: {error}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -233,7 +233,7 @@ impl From<url::ParseError> for KmsError {
|
||||
impl From<reqwest::Error> for KmsError {
|
||||
fn from(error: reqwest::Error) -> Self {
|
||||
Self::BackendError {
|
||||
message: format!("HTTP request failed: {}", error),
|
||||
message: format!("HTTP request failed: {error}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,7 @@ impl KmsServiceManager {
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
let err_msg = format!("Failed to create KMS backend: {}", e);
|
||||
let err_msg = format!("Failed to create KMS backend: {e}");
|
||||
error!("{}", err_msg);
|
||||
let mut status = self.status.write().await;
|
||||
*status = KmsServiceStatus::Error(err_msg.clone());
|
||||
@@ -218,7 +218,7 @@ impl KmsServiceManager {
|
||||
error!("KMS health check error: {}", e);
|
||||
// Update status to error
|
||||
let mut status = self.status.write().await;
|
||||
*status = KmsServiceStatus::Error(format!("Health check failed: {}", e));
|
||||
*status = KmsServiceStatus::Error(format!("Health check failed: {e}"));
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user