mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-27 15:37:02 +00:00
Fix KMS configuration synchronization across cluster nodes (#855)
* Initial plan * Add KMS configuration persistence to cluster storage Co-authored-by: houseme <4829346+houseme@users.noreply.github.com> * Apply code formatting to KMS configuration changes Co-authored-by: houseme <4829346+houseme@users.noreply.github.com> * add comment * fix fmt * fix * Fix overlapping dependabot cargo configurations Co-authored-by: houseme <4829346+houseme@users.noreply.github.com> * improve code for comment and replace `Once_Cell` to `std::sync::OnceLock` --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: houseme <4829346+houseme@users.noreply.github.com> Co-authored-by: houseme <housemecn@gmail.com> Co-authored-by: loverustfs <155562731+loverustfs@users.noreply.github.com>
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
|
||||
//! API types for KMS dynamic configuration
|
||||
|
||||
use crate::config::{KmsBackend, KmsConfig, VaultAuthMethod};
|
||||
use crate::config::{BackendConfig, CacheConfig, KmsBackend, KmsConfig, LocalConfig, TlsConfig, VaultAuthMethod, VaultConfig};
|
||||
use crate::service_manager::KmsServiceStatus;
|
||||
use crate::types::{KeyMetadata, KeyUsage};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -212,12 +212,12 @@ impl From<&KmsConfig> for KmsConfigSummary {
|
||||
};
|
||||
|
||||
let backend_summary = match &config.backend_config {
|
||||
crate::config::BackendConfig::Local(local_config) => BackendSummary::Local {
|
||||
BackendConfig::Local(local_config) => BackendSummary::Local {
|
||||
key_dir: local_config.key_dir.clone(),
|
||||
has_master_key: local_config.master_key.is_some(),
|
||||
file_permissions: local_config.file_permissions,
|
||||
},
|
||||
crate::config::BackendConfig::Vault(vault_config) => BackendSummary::Vault {
|
||||
BackendConfig::Vault(vault_config) => BackendSummary::Vault {
|
||||
address: vault_config.address.clone(),
|
||||
auth_method_type: match &vault_config.auth_method {
|
||||
VaultAuthMethod::Token { .. } => "token".to_string(),
|
||||
@@ -248,7 +248,7 @@ impl ConfigureLocalKmsRequest {
|
||||
KmsConfig {
|
||||
backend: KmsBackend::Local,
|
||||
default_key_id: self.default_key_id.clone(),
|
||||
backend_config: crate::config::BackendConfig::Local(crate::config::LocalConfig {
|
||||
backend_config: BackendConfig::Local(LocalConfig {
|
||||
key_dir: self.key_dir.clone(),
|
||||
master_key: self.master_key.clone(),
|
||||
file_permissions: self.file_permissions,
|
||||
@@ -256,7 +256,7 @@ impl ConfigureLocalKmsRequest {
|
||||
timeout: Duration::from_secs(self.timeout_seconds.unwrap_or(30)),
|
||||
retry_attempts: self.retry_attempts.unwrap_or(3),
|
||||
enable_cache: self.enable_cache.unwrap_or(true),
|
||||
cache_config: crate::config::CacheConfig {
|
||||
cache_config: CacheConfig {
|
||||
max_keys: self.max_cached_keys.unwrap_or(1000),
|
||||
ttl: Duration::from_secs(self.cache_ttl_seconds.unwrap_or(3600)),
|
||||
enable_metrics: true,
|
||||
@@ -271,7 +271,7 @@ impl ConfigureVaultKmsRequest {
|
||||
KmsConfig {
|
||||
backend: KmsBackend::Vault,
|
||||
default_key_id: self.default_key_id.clone(),
|
||||
backend_config: crate::config::BackendConfig::Vault(crate::config::VaultConfig {
|
||||
backend_config: BackendConfig::Vault(VaultConfig {
|
||||
address: self.address.clone(),
|
||||
auth_method: self.auth_method.clone(),
|
||||
namespace: self.namespace.clone(),
|
||||
@@ -279,7 +279,7 @@ impl ConfigureVaultKmsRequest {
|
||||
kv_mount: self.kv_mount.clone().unwrap_or_else(|| "secret".to_string()),
|
||||
key_path_prefix: self.key_path_prefix.clone().unwrap_or_else(|| "rustfs/kms/keys".to_string()),
|
||||
tls: if self.skip_tls_verify.unwrap_or(false) {
|
||||
Some(crate::config::TlsConfig {
|
||||
Some(TlsConfig {
|
||||
ca_cert_path: None,
|
||||
client_cert_path: None,
|
||||
client_key_path: None,
|
||||
@@ -292,7 +292,7 @@ impl ConfigureVaultKmsRequest {
|
||||
timeout: Duration::from_secs(self.timeout_seconds.unwrap_or(30)),
|
||||
retry_attempts: self.retry_attempts.unwrap_or(3),
|
||||
enable_cache: self.enable_cache.unwrap_or(true),
|
||||
cache_config: crate::config::CacheConfig {
|
||||
cache_config: CacheConfig {
|
||||
max_keys: self.max_cached_keys.unwrap_or(1000),
|
||||
ttl: Duration::from_secs(self.cache_ttl_seconds.unwrap_or(3600)),
|
||||
enable_metrics: true,
|
||||
|
||||
@@ -200,6 +200,16 @@ pub struct BackendInfo {
|
||||
|
||||
impl BackendInfo {
|
||||
/// Create a new backend info
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `backend_type` - The type of the backend
|
||||
/// * `version` - The version of the backend
|
||||
/// * `endpoint` - The endpoint or location of the backend
|
||||
/// * `healthy` - Whether the backend is healthy
|
||||
///
|
||||
/// # Returns
|
||||
/// A new BackendInfo instance
|
||||
///
|
||||
pub fn new(backend_type: String, version: String, endpoint: String, healthy: bool) -> Self {
|
||||
Self {
|
||||
backend_type,
|
||||
@@ -211,6 +221,14 @@ impl BackendInfo {
|
||||
}
|
||||
|
||||
/// Add metadata to the backend info
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `key` - Metadata key
|
||||
/// * `value` - Metadata value
|
||||
///
|
||||
/// # Returns
|
||||
/// Updated BackendInfo instance
|
||||
///
|
||||
pub fn with_metadata(mut self, key: String, value: String) -> Self {
|
||||
self.metadata.insert(key, value);
|
||||
self
|
||||
|
||||
@@ -34,6 +34,13 @@ pub struct KmsCache {
|
||||
|
||||
impl KmsCache {
|
||||
/// Create a new KMS cache with the specified capacity
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `capacity` - Maximum number of entries in the cache
|
||||
///
|
||||
/// # Returns
|
||||
/// A new instance of `KmsCache`
|
||||
///
|
||||
pub fn new(capacity: u64) -> Self {
|
||||
Self {
|
||||
key_metadata_cache: Cache::builder()
|
||||
@@ -48,22 +55,47 @@ impl KmsCache {
|
||||
}
|
||||
|
||||
/// Get key metadata from cache
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `key_id` - The ID of the key to retrieve metadata for
|
||||
///
|
||||
/// # Returns
|
||||
/// An `Option` containing the `KeyMetadata` if found, or `None` if not found
|
||||
///
|
||||
pub async fn get_key_metadata(&self, key_id: &str) -> Option<KeyMetadata> {
|
||||
self.key_metadata_cache.get(key_id).await
|
||||
}
|
||||
|
||||
/// Put key metadata into cache
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `key_id` - The ID of the key to store metadata for
|
||||
/// * `metadata` - The `KeyMetadata` to store in the cache
|
||||
///
|
||||
pub async fn put_key_metadata(&mut self, key_id: &str, metadata: &KeyMetadata) {
|
||||
self.key_metadata_cache.insert(key_id.to_string(), metadata.clone()).await;
|
||||
self.key_metadata_cache.run_pending_tasks().await;
|
||||
}
|
||||
|
||||
/// Get data key from cache
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `key_id` - The ID of the key to retrieve the data key for
|
||||
///
|
||||
/// # Returns
|
||||
/// An `Option` containing the `CachedDataKey` if found, or `None` if not found
|
||||
///
|
||||
pub async fn get_data_key(&self, key_id: &str) -> Option<CachedDataKey> {
|
||||
self.data_key_cache.get(key_id).await
|
||||
}
|
||||
|
||||
/// Put data key into cache
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `key_id` - The ID of the key to store the data key for
|
||||
/// * `plaintext` - The plaintext data key bytes
|
||||
/// * `ciphertext` - The ciphertext data key bytes
|
||||
///
|
||||
pub async fn put_data_key(&mut self, key_id: &str, plaintext: &[u8], ciphertext: &[u8]) {
|
||||
let cached_key = CachedDataKey {
|
||||
plaintext: plaintext.to_vec(),
|
||||
@@ -75,11 +107,19 @@ impl KmsCache {
|
||||
}
|
||||
|
||||
/// Remove key metadata from cache
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `key_id` - The ID of the key to remove metadata for
|
||||
///
|
||||
pub async fn remove_key_metadata(&mut self, key_id: &str) {
|
||||
self.key_metadata_cache.remove(key_id).await;
|
||||
}
|
||||
|
||||
/// Remove data key from cache
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `key_id` - The ID of the key to remove the data key for
|
||||
///
|
||||
pub async fn remove_data_key(&mut self, key_id: &str) {
|
||||
self.data_key_cache.remove(key_id).await;
|
||||
}
|
||||
@@ -95,6 +135,10 @@ impl KmsCache {
|
||||
}
|
||||
|
||||
/// Get cache statistics (hit count, miss count)
|
||||
///
|
||||
/// # Returns
|
||||
/// A tuple containing total entries and total misses
|
||||
///
|
||||
pub fn stats(&self) -> (u64, u64) {
|
||||
let metadata_stats = (
|
||||
self.key_metadata_cache.entry_count(),
|
||||
|
||||
@@ -52,6 +52,16 @@ pub struct AesCipher {
|
||||
|
||||
impl AesCipher {
|
||||
/// Create a new AES cipher with the given key
|
||||
///
|
||||
/// #Arguments
|
||||
/// * `key` - A byte slice representing the AES-256 key (32 bytes)
|
||||
///
|
||||
/// #Errors
|
||||
/// Returns `KmsError` if the key size is invalid
|
||||
///
|
||||
/// #Returns
|
||||
/// A Result containing the AesCipher instance
|
||||
///
|
||||
pub fn new(key: &[u8]) -> Result<Self> {
|
||||
if key.len() != 32 {
|
||||
return Err(KmsError::invalid_key_size(32, key.len()));
|
||||
@@ -142,6 +152,16 @@ pub struct ChaCha20Cipher {
|
||||
|
||||
impl ChaCha20Cipher {
|
||||
/// Create a new ChaCha20 cipher with the given key
|
||||
///
|
||||
/// #Arguments
|
||||
/// * `key` - A byte slice representing the ChaCha20-Poly1305 key (32 bytes)
|
||||
///
|
||||
/// #Errors
|
||||
/// Returns `KmsError` if the key size is invalid
|
||||
///
|
||||
/// #Returns
|
||||
/// A Result containing the ChaCha20Cipher instance
|
||||
///
|
||||
pub fn new(key: &[u8]) -> Result<Self> {
|
||||
if key.len() != 32 {
|
||||
return Err(KmsError::invalid_key_size(32, key.len()));
|
||||
@@ -228,6 +248,14 @@ impl ObjectCipher for ChaCha20Cipher {
|
||||
}
|
||||
|
||||
/// Create a cipher instance for the given algorithm and key
|
||||
///
|
||||
/// #Arguments
|
||||
/// * `algorithm` - The encryption algorithm to use
|
||||
/// * `key` - A byte slice representing the encryption key
|
||||
///
|
||||
/// #Returns
|
||||
/// A Result containing a boxed ObjectCipher instance
|
||||
///
|
||||
pub fn create_cipher(algorithm: &EncryptionAlgorithm, key: &[u8]) -> Result<Box<dyn ObjectCipher>> {
|
||||
match algorithm {
|
||||
EncryptionAlgorithm::Aes256 | EncryptionAlgorithm::AwsKms => Ok(Box::new(AesCipher::new(key)?)),
|
||||
@@ -236,6 +264,13 @@ pub fn create_cipher(algorithm: &EncryptionAlgorithm, key: &[u8]) -> Result<Box<
|
||||
}
|
||||
|
||||
/// Generate a random IV for the given algorithm
|
||||
///
|
||||
/// #Arguments
|
||||
/// * `algorithm` - The encryption algorithm for which to generate the IV
|
||||
///
|
||||
/// #Returns
|
||||
/// A vector containing the generated IV bytes
|
||||
///
|
||||
pub fn generate_iv(algorithm: &EncryptionAlgorithm) -> Vec<u8> {
|
||||
let iv_size = match algorithm {
|
||||
EncryptionAlgorithm::Aes256 | EncryptionAlgorithm::AwsKms => 12,
|
||||
|
||||
@@ -18,6 +18,12 @@ use crate::encryption::ciphers::{create_cipher, generate_iv};
|
||||
use crate::error::{KmsError, Result};
|
||||
use crate::manager::KmsManager;
|
||||
use crate::types::*;
|
||||
use base64::Engine;
|
||||
use rand::random;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Cursor;
|
||||
use tokio::io::{AsyncRead, AsyncReadExt};
|
||||
use tracing::{debug, info};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
/// Data key for object encryption
|
||||
@@ -36,12 +42,6 @@ impl Drop for DataKey {
|
||||
self.plaintext_key.zeroize();
|
||||
}
|
||||
}
|
||||
use base64::Engine;
|
||||
use rand::random;
|
||||
use std::collections::HashMap;
|
||||
use std::io::Cursor;
|
||||
use tokio::io::{AsyncRead, AsyncReadExt};
|
||||
use tracing::{debug, info};
|
||||
|
||||
/// Service for encrypting and decrypting S3 objects with KMS integration
|
||||
pub struct ObjectEncryptionService {
|
||||
@@ -59,51 +59,110 @@ pub struct EncryptionResult {
|
||||
|
||||
impl ObjectEncryptionService {
|
||||
/// Create a new object encryption service
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `kms_manager` - KMS manager to use for key operations
|
||||
///
|
||||
/// # Returns
|
||||
/// New ObjectEncryptionService instance
|
||||
///
|
||||
pub fn new(kms_manager: KmsManager) -> Self {
|
||||
Self { kms_manager }
|
||||
}
|
||||
|
||||
/// Create a new master key (delegates to KMS manager)
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `request` - CreateKeyRequest with key parameters
|
||||
///
|
||||
/// # Returns
|
||||
/// CreateKeyResponse with created key details
|
||||
///
|
||||
pub async fn create_key(&self, request: CreateKeyRequest) -> Result<CreateKeyResponse> {
|
||||
self.kms_manager.create_key(request).await
|
||||
}
|
||||
|
||||
/// Describe a master key (delegates to KMS manager)
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `request` - DescribeKeyRequest with key ID
|
||||
///
|
||||
/// # Returns
|
||||
/// DescribeKeyResponse with key metadata
|
||||
///
|
||||
pub async fn describe_key(&self, request: DescribeKeyRequest) -> Result<DescribeKeyResponse> {
|
||||
self.kms_manager.describe_key(request).await
|
||||
}
|
||||
|
||||
/// List master keys (delegates to KMS manager)
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `request` - ListKeysRequest with listing parameters
|
||||
///
|
||||
/// # Returns
|
||||
/// ListKeysResponse with list of keys
|
||||
///
|
||||
pub async fn list_keys(&self, request: ListKeysRequest) -> Result<ListKeysResponse> {
|
||||
self.kms_manager.list_keys(request).await
|
||||
}
|
||||
|
||||
/// Generate a data encryption key (delegates to KMS manager)
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `request` - GenerateDataKeyRequest with key parameters
|
||||
///
|
||||
/// # Returns
|
||||
/// GenerateDataKeyResponse with generated key details
|
||||
///
|
||||
pub async fn generate_data_key(&self, request: GenerateDataKeyRequest) -> Result<GenerateDataKeyResponse> {
|
||||
self.kms_manager.generate_data_key(request).await
|
||||
}
|
||||
|
||||
/// Get the default key ID
|
||||
///
|
||||
/// # Returns
|
||||
/// Option with default key ID if configured
|
||||
///
|
||||
pub fn get_default_key_id(&self) -> Option<&String> {
|
||||
self.kms_manager.get_default_key_id()
|
||||
}
|
||||
|
||||
/// Get cache statistics
|
||||
///
|
||||
/// # Returns
|
||||
/// Option with (hits, misses) if caching is enabled
|
||||
///
|
||||
pub async fn cache_stats(&self) -> Option<(u64, u64)> {
|
||||
self.kms_manager.cache_stats().await
|
||||
}
|
||||
|
||||
/// Clear the cache
|
||||
///
|
||||
/// # Returns
|
||||
/// Result indicating success or failure
|
||||
///
|
||||
pub async fn clear_cache(&self) -> Result<()> {
|
||||
self.kms_manager.clear_cache().await
|
||||
}
|
||||
|
||||
/// Get backend health status
|
||||
///
|
||||
/// # Returns
|
||||
/// Result indicating if backend is healthy
|
||||
///
|
||||
pub async fn health_check(&self) -> Result<bool> {
|
||||
self.kms_manager.health_check().await
|
||||
}
|
||||
|
||||
/// Create a data encryption key for object encryption
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `kms_key_id` - Optional KMS key ID to use (uses default if None)
|
||||
/// * `context` - ObjectEncryptionContext with bucket and object key
|
||||
///
|
||||
/// # Returns
|
||||
/// Tuple with DataKey and encrypted key blob
|
||||
///
|
||||
pub async fn create_data_key(
|
||||
&self,
|
||||
kms_key_id: &Option<String>,
|
||||
@@ -146,6 +205,14 @@ impl ObjectEncryptionService {
|
||||
}
|
||||
|
||||
/// Decrypt a data encryption key
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `encrypted_key` - Encrypted data key blob
|
||||
/// * `context` - ObjectEncryptionContext with bucket and object key
|
||||
///
|
||||
/// # Returns
|
||||
/// DataKey with decrypted key
|
||||
///
|
||||
pub async fn decrypt_data_key(&self, encrypted_key: &[u8], _context: &ObjectEncryptionContext) -> Result<DataKey> {
|
||||
let decrypt_request = DecryptRequest {
|
||||
ciphertext: encrypted_key.to_vec(),
|
||||
@@ -429,6 +496,17 @@ impl ObjectEncryptionService {
|
||||
}
|
||||
|
||||
/// Decrypt object with customer-provided key (SSE-C)
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `bucket` - S3 bucket name
|
||||
/// * `object_key` - S3 object key
|
||||
/// * `ciphertext` - Encrypted data
|
||||
/// * `metadata` - Encryption metadata
|
||||
/// * `customer_key` - Customer-provided 256-bit key
|
||||
///
|
||||
/// # Returns
|
||||
/// Decrypted data as a reader
|
||||
///
|
||||
pub async fn decrypt_object_with_customer_key(
|
||||
&self,
|
||||
bucket: &str,
|
||||
@@ -481,6 +559,14 @@ impl ObjectEncryptionService {
|
||||
}
|
||||
|
||||
/// Validate encryption context
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `actual` - Actual encryption context from metadata
|
||||
/// * `expected` - Expected encryption context to validate against
|
||||
///
|
||||
/// # Returns
|
||||
/// Result indicating success or context mismatch
|
||||
///
|
||||
fn validate_encryption_context(&self, actual: &HashMap<String, String>, expected: &HashMap<String, String>) -> Result<()> {
|
||||
for (key, expected_value) in expected {
|
||||
match actual.get(key) {
|
||||
@@ -499,6 +585,13 @@ impl ObjectEncryptionService {
|
||||
}
|
||||
|
||||
/// Convert encryption metadata to HTTP headers for S3 compatibility
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `metadata` - EncryptionMetadata to convert
|
||||
///
|
||||
/// # Returns
|
||||
/// HashMap of HTTP headers
|
||||
///
|
||||
pub fn metadata_to_headers(&self, metadata: &EncryptionMetadata) -> HashMap<String, String> {
|
||||
let mut headers = HashMap::new();
|
||||
|
||||
@@ -542,6 +635,13 @@ impl ObjectEncryptionService {
|
||||
}
|
||||
|
||||
/// Parse encryption metadata from HTTP headers
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `headers` - HashMap of HTTP headers
|
||||
///
|
||||
/// # Returns
|
||||
/// EncryptionMetadata parsed from headers
|
||||
///
|
||||
pub fn headers_to_metadata(&self, headers: &HashMap<String, String>) -> Result<EncryptionMetadata> {
|
||||
let algorithm = headers
|
||||
.get("x-amz-server-side-encryption")
|
||||
|
||||
+16
-2
@@ -116,7 +116,7 @@ impl KmsError {
|
||||
Self::BackendError { message: message.into() }
|
||||
}
|
||||
|
||||
/// Create an access denied error
|
||||
/// Create access denied error
|
||||
pub fn access_denied<S: Into<String>>(message: S) -> Self {
|
||||
Self::AccessDenied { message: message.into() }
|
||||
}
|
||||
@@ -184,7 +184,7 @@ impl KmsError {
|
||||
}
|
||||
}
|
||||
|
||||
// Convert from standard library errors
|
||||
/// Convert from standard library errors
|
||||
impl From<std::io::Error> for KmsError {
|
||||
fn from(error: std::io::Error) -> Self {
|
||||
Self::IoError {
|
||||
@@ -206,6 +206,13 @@ impl From<serde_json::Error> for KmsError {
|
||||
|
||||
impl KmsError {
|
||||
/// Create a KMS error from AES-GCM error
|
||||
///
|
||||
/// #Arguments
|
||||
/// * `error` - The AES-GCM error to convert
|
||||
///
|
||||
/// #Returns
|
||||
/// * `KmsError` - The corresponding KMS error
|
||||
///
|
||||
pub fn from_aes_gcm_error(error: aes_gcm::Error) -> Self {
|
||||
Self::CryptographicError {
|
||||
operation: "AES-GCM".to_string(),
|
||||
@@ -214,6 +221,13 @@ impl KmsError {
|
||||
}
|
||||
|
||||
/// Create a KMS error from ChaCha20-Poly1305 error
|
||||
///
|
||||
/// #Arguments
|
||||
/// * `error` - The ChaCha20-Poly1305 error to convert
|
||||
///
|
||||
/// #Returns
|
||||
/// * `KmsError` - The corresponding KMS error
|
||||
///
|
||||
pub fn from_chacha20_error(error: chacha20poly1305::Error) -> Self {
|
||||
Self::CryptographicError {
|
||||
operation: "ChaCha20-Poly1305".to_string(),
|
||||
|
||||
@@ -19,7 +19,7 @@ use crate::config::{BackendConfig, KmsConfig};
|
||||
use crate::encryption::service::ObjectEncryptionService;
|
||||
use crate::error::{KmsError, Result};
|
||||
use crate::manager::KmsManager;
|
||||
use std::sync::Arc;
|
||||
use std::sync::{Arc, OnceLock};
|
||||
use tokio::sync::RwLock;
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
@@ -71,7 +71,7 @@ impl KmsServiceManager {
|
||||
|
||||
/// Configure KMS with new configuration
|
||||
pub async fn configure(&self, new_config: KmsConfig) -> Result<()> {
|
||||
tracing::info!("CLAUDE DEBUG: configure() called with backend: {:?}", new_config.backend);
|
||||
info!("CLAUDE DEBUG: configure() called with backend: {:?}", new_config.backend);
|
||||
info!("Configuring KMS with backend: {:?}", new_config.backend);
|
||||
|
||||
// Update configuration
|
||||
@@ -92,7 +92,7 @@ impl KmsServiceManager {
|
||||
|
||||
/// Start KMS service with current configuration
|
||||
pub async fn start(&self) -> Result<()> {
|
||||
tracing::info!("CLAUDE DEBUG: start() called");
|
||||
info!("CLAUDE DEBUG: start() called");
|
||||
let config = {
|
||||
let config_guard = self.config.read().await;
|
||||
match config_guard.as_ref() {
|
||||
@@ -254,7 +254,7 @@ impl Default for KmsServiceManager {
|
||||
}
|
||||
|
||||
/// Global KMS service manager instance
|
||||
static GLOBAL_KMS_SERVICE_MANAGER: once_cell::sync::OnceCell<Arc<KmsServiceManager>> = once_cell::sync::OnceCell::new();
|
||||
static GLOBAL_KMS_SERVICE_MANAGER: OnceLock<Arc<KmsServiceManager>> = OnceLock::new();
|
||||
|
||||
/// Initialize global KMS service manager
|
||||
pub fn init_global_kms_service_manager() -> Arc<KmsServiceManager> {
|
||||
@@ -270,12 +270,12 @@ pub fn get_global_kms_service_manager() -> Option<Arc<KmsServiceManager>> {
|
||||
|
||||
/// Get global encryption service (if KMS is running)
|
||||
pub async fn get_global_encryption_service() -> Option<Arc<ObjectEncryptionService>> {
|
||||
tracing::info!("CLAUDE DEBUG: get_global_encryption_service called");
|
||||
info!("CLAUDE DEBUG: get_global_encryption_service called");
|
||||
let manager = get_global_kms_service_manager().unwrap_or_else(|| {
|
||||
tracing::warn!("CLAUDE DEBUG: KMS service manager not initialized, initializing now as fallback");
|
||||
warn!("CLAUDE DEBUG: KMS service manager not initialized, initializing now as fallback");
|
||||
init_global_kms_service_manager()
|
||||
});
|
||||
let service = manager.get_encryption_service().await;
|
||||
tracing::info!("CLAUDE DEBUG: get_encryption_service returned: {}", service.is_some());
|
||||
info!("CLAUDE DEBUG: get_encryption_service returned: {}", service.is_some());
|
||||
service
|
||||
}
|
||||
|
||||
@@ -42,6 +42,17 @@ pub struct DataKey {
|
||||
|
||||
impl DataKey {
|
||||
/// Create a new data key
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `key_id` - Unique identifier for the key
|
||||
/// * `version` - Key version number
|
||||
/// * `plaintext` - Optional plaintext key material
|
||||
/// * `ciphertext` - Encrypted key material
|
||||
/// * `key_spec` - Key specification (e.g., "AES_256")
|
||||
///
|
||||
/// # Returns
|
||||
/// A new `DataKey` instance
|
||||
///
|
||||
pub fn new(key_id: String, version: u32, plaintext: Option<Vec<u8>>, ciphertext: Vec<u8>, key_spec: String) -> Self {
|
||||
Self {
|
||||
key_id,
|
||||
@@ -55,6 +66,11 @@ impl DataKey {
|
||||
}
|
||||
|
||||
/// Clear the plaintext key material from memory for security
|
||||
///
|
||||
/// # Security
|
||||
/// This method zeroes out the plaintext key material before dropping it
|
||||
/// to prevent sensitive data from lingering in memory.
|
||||
///
|
||||
pub fn clear_plaintext(&mut self) {
|
||||
if let Some(ref mut plaintext) = self.plaintext {
|
||||
// Zero out the memory before dropping
|
||||
@@ -64,6 +80,14 @@ impl DataKey {
|
||||
}
|
||||
|
||||
/// Add metadata to the data key
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `key` - Metadata key
|
||||
/// * `value` - Metadata value
|
||||
///
|
||||
/// # Returns
|
||||
/// Updated `DataKey` instance with added metadata
|
||||
///
|
||||
pub fn with_metadata(mut self, key: String, value: String) -> Self {
|
||||
self.metadata.insert(key, value);
|
||||
self
|
||||
@@ -97,6 +121,15 @@ pub struct MasterKey {
|
||||
|
||||
impl MasterKey {
|
||||
/// Create a new master key
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `key_id` - Unique identifier for the key
|
||||
/// * `algorithm` - Key algorithm (e.g., "AES-256")
|
||||
/// * `created_by` - Optional creator/owner of the key
|
||||
///
|
||||
/// # Returns
|
||||
/// A new `MasterKey` instance
|
||||
///
|
||||
pub fn new(key_id: String, algorithm: String, created_by: Option<String>) -> Self {
|
||||
Self {
|
||||
key_id,
|
||||
@@ -113,6 +146,16 @@ impl MasterKey {
|
||||
}
|
||||
|
||||
/// Create a new master key with description
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `key_id` - Unique identifier for the key
|
||||
/// * `algorithm` - Key algorithm (e.g., "AES-256")
|
||||
/// * `created_by` - Optional creator/owner of the key
|
||||
/// * `description` - Optional key description
|
||||
///
|
||||
/// # Returns
|
||||
/// A new `MasterKey` instance with description
|
||||
///
|
||||
pub fn new_with_description(
|
||||
key_id: String,
|
||||
algorithm: String,
|
||||
@@ -218,6 +261,14 @@ pub struct GenerateKeyRequest {
|
||||
|
||||
impl GenerateKeyRequest {
|
||||
/// Create a new generate key request
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `master_key_id` - Master key ID to use for encryption
|
||||
/// * `key_spec` - Key specification (e.g., "AES_256")
|
||||
///
|
||||
/// # Returns
|
||||
/// A new `GenerateKeyRequest` instance
|
||||
///
|
||||
pub fn new(master_key_id: String, key_spec: String) -> Self {
|
||||
Self {
|
||||
master_key_id,
|
||||
@@ -229,12 +280,27 @@ impl GenerateKeyRequest {
|
||||
}
|
||||
|
||||
/// Add encryption context
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `key` - Context key
|
||||
/// * `value` - Context value
|
||||
///
|
||||
/// # Returns
|
||||
/// Updated `GenerateKeyRequest` instance with added context
|
||||
///
|
||||
pub fn with_context(mut self, key: String, value: String) -> Self {
|
||||
self.encryption_context.insert(key, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set key length explicitly
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `length` - Key length in bytes
|
||||
///
|
||||
/// # Returns
|
||||
/// Updated `GenerateKeyRequest` instance with specified key length
|
||||
///
|
||||
pub fn with_length(mut self, length: u32) -> Self {
|
||||
self.key_length = Some(length);
|
||||
self
|
||||
@@ -256,6 +322,14 @@ pub struct EncryptRequest {
|
||||
|
||||
impl EncryptRequest {
|
||||
/// Create a new encrypt request
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `key_id` - Key ID to use for encryption
|
||||
/// * `plaintext` - Plaintext data to encrypt
|
||||
///
|
||||
/// # Returns
|
||||
/// A new `EncryptRequest` instance
|
||||
///
|
||||
pub fn new(key_id: String, plaintext: Vec<u8>) -> Self {
|
||||
Self {
|
||||
key_id,
|
||||
@@ -266,6 +340,14 @@ impl EncryptRequest {
|
||||
}
|
||||
|
||||
/// Add encryption context
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `key` - Context key
|
||||
/// * `value` - Context value
|
||||
///
|
||||
/// # Returns
|
||||
/// Updated `EncryptRequest` instance with added context
|
||||
///
|
||||
pub fn with_context(mut self, key: String, value: String) -> Self {
|
||||
self.encryption_context.insert(key, value);
|
||||
self
|
||||
@@ -298,6 +380,13 @@ pub struct DecryptRequest {
|
||||
|
||||
impl DecryptRequest {
|
||||
/// Create a new decrypt request
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `ciphertext` - Ciphertext to decrypt
|
||||
///
|
||||
/// # Returns
|
||||
/// A new `DecryptRequest` instance
|
||||
///
|
||||
pub fn new(ciphertext: Vec<u8>) -> Self {
|
||||
Self {
|
||||
ciphertext,
|
||||
@@ -307,6 +396,14 @@ impl DecryptRequest {
|
||||
}
|
||||
|
||||
/// Add encryption context
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `key` - Context key
|
||||
/// * `value` - Context value
|
||||
///
|
||||
/// # Returns
|
||||
/// Updated `DecryptRequest` instance with added context
|
||||
///
|
||||
pub fn with_context(mut self, key: String, value: String) -> Self {
|
||||
self.encryption_context.insert(key, value);
|
||||
self
|
||||
@@ -365,6 +462,13 @@ pub struct OperationContext {
|
||||
|
||||
impl OperationContext {
|
||||
/// Create a new operation context
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `principal` - User or service performing the operation
|
||||
///
|
||||
/// # Returns
|
||||
/// A new `OperationContext` instance
|
||||
///
|
||||
pub fn new(principal: String) -> Self {
|
||||
Self {
|
||||
operation_id: Uuid::new_v4(),
|
||||
@@ -376,18 +480,40 @@ impl OperationContext {
|
||||
}
|
||||
|
||||
/// Add additional context
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `key` - Context key
|
||||
/// * `value` - Context value
|
||||
///
|
||||
/// # Returns
|
||||
/// Updated `OperationContext` instance with added context
|
||||
///
|
||||
pub fn with_context(mut self, key: String, value: String) -> Self {
|
||||
self.additional_context.insert(key, value);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set source IP
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `ip` - Source IP address
|
||||
///
|
||||
/// # Returns
|
||||
/// Updated `OperationContext` instance with source IP
|
||||
///
|
||||
pub fn with_source_ip(mut self, ip: String) -> Self {
|
||||
self.source_ip = Some(ip);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set user agent
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `agent` - User agent string
|
||||
///
|
||||
/// # Returns
|
||||
/// Updated `OperationContext` instance with user agent
|
||||
///
|
||||
pub fn with_user_agent(mut self, agent: String) -> Self {
|
||||
self.user_agent = Some(agent);
|
||||
self
|
||||
@@ -411,6 +537,14 @@ pub struct ObjectEncryptionContext {
|
||||
|
||||
impl ObjectEncryptionContext {
|
||||
/// Create a new object encryption context
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `bucket` - Bucket name
|
||||
/// * `object_key` - Object key
|
||||
///
|
||||
/// # Returns
|
||||
/// A new `ObjectEncryptionContext` instance
|
||||
///
|
||||
pub fn new(bucket: String, object_key: String) -> Self {
|
||||
Self {
|
||||
bucket,
|
||||
@@ -422,18 +556,40 @@ impl ObjectEncryptionContext {
|
||||
}
|
||||
|
||||
/// Set content type
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `content_type` - Content type string
|
||||
///
|
||||
/// # Returns
|
||||
/// Updated `ObjectEncryptionContext` instance with content type
|
||||
///
|
||||
pub fn with_content_type(mut self, content_type: String) -> Self {
|
||||
self.content_type = Some(content_type);
|
||||
self
|
||||
}
|
||||
|
||||
/// Set object size
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `size` - Object size in bytes
|
||||
///
|
||||
/// # Returns
|
||||
/// Updated `ObjectEncryptionContext` instance with size
|
||||
///
|
||||
pub fn with_size(mut self, size: u64) -> Self {
|
||||
self.size = Some(size);
|
||||
self
|
||||
}
|
||||
|
||||
/// Add encryption context
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `key` - Context key
|
||||
/// * `value` - Context value
|
||||
///
|
||||
/// # Returns
|
||||
/// Updated `ObjectEncryptionContext` instance with added context
|
||||
///
|
||||
pub fn with_encryption_context(mut self, key: String, value: String) -> Self {
|
||||
self.encryption_context.insert(key, value);
|
||||
self
|
||||
@@ -503,6 +659,10 @@ pub enum KeySpec {
|
||||
|
||||
impl KeySpec {
|
||||
/// Get the key size in bytes
|
||||
///
|
||||
/// # Returns
|
||||
/// Key size in bytes
|
||||
///
|
||||
pub fn key_size(&self) -> usize {
|
||||
match self {
|
||||
Self::Aes256 => 32,
|
||||
@@ -512,6 +672,10 @@ impl KeySpec {
|
||||
}
|
||||
|
||||
/// Get the string representation for backends
|
||||
///
|
||||
/// # Returns
|
||||
/// Key specification as a string
|
||||
///
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Aes256 => "AES_256",
|
||||
@@ -636,6 +800,14 @@ pub struct GenerateDataKeyRequest {
|
||||
|
||||
impl GenerateDataKeyRequest {
|
||||
/// Create a new generate data key request
|
||||
///
|
||||
/// # Arguments
|
||||
/// * `key_id` - Key ID to use for encryption
|
||||
/// * `key_spec` - Key specification
|
||||
///
|
||||
/// # Returns
|
||||
/// A new `GenerateDataKeyRequest` instance
|
||||
///
|
||||
pub fn new(key_id: String, key_spec: KeySpec) -> Self {
|
||||
Self {
|
||||
key_id,
|
||||
@@ -658,6 +830,10 @@ pub struct GenerateDataKeyResponse {
|
||||
|
||||
impl EncryptionAlgorithm {
|
||||
/// Get the algorithm name as a string
|
||||
///
|
||||
/// # Returns
|
||||
/// Algorithm name as a string
|
||||
///
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Self::Aes256 => "AES256",
|
||||
|
||||
Reference in New Issue
Block a user