mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-20 19:42:17 +00:00
feat(rio): rio_v2 is compatible with minio for storing data. (#3115)
* Set up a compatibility layer for replacing old Rio components with new ones. * fix(rio). compress range * feat(rio). Add the experimental feature rio_v2 to support minio data at the binary level. * feat(rio_v2): add sse-c test * test compression component * simple fix * fix minlz encode * fix metadata * fix kms key cache error * Update launch.json * ci: set nix crate download user agent * fix: gate obs pyroscope backend * ignore minio test * fix encrypt check * fix * fix * fix * Update object_usecase.rs * Update ci.yml * fix * ci add rio-v2 test * fix * ci fix * fix * Reconstructed into a more reasonable compatibility mode * fix * fix --------- Signed-off-by: houseme <housemecn@gmail.com> Signed-off-by: 唐小鸭 <tangtang1251@qq.com> Co-authored-by: houseme <housemecn@gmail.com> Co-authored-by: cxymds <Cxymds@qq.com> Co-authored-by: 安正超 <anzhengchao@gmail.com>
This commit is contained in:
@@ -49,6 +49,26 @@ pub struct ObjectEncryptionService {
|
||||
kms_manager: KmsManager,
|
||||
}
|
||||
|
||||
fn canonical_bucket_path(bucket: &str, object_key: &str) -> String {
|
||||
let bucket = bucket.trim_matches('/');
|
||||
let object_key = object_key.trim_matches('/');
|
||||
if object_key.is_empty() {
|
||||
bucket.to_string()
|
||||
} else if bucket.is_empty() {
|
||||
object_key.to_string()
|
||||
} else {
|
||||
format!("{bucket}/{object_key}")
|
||||
}
|
||||
}
|
||||
|
||||
fn request_encryption_context(context: &ObjectEncryptionContext) -> HashMap<String, String> {
|
||||
let mut enc_context = context.encryption_context.clone();
|
||||
enc_context
|
||||
.entry(context.bucket.clone())
|
||||
.or_insert_with(|| canonical_bucket_path(&context.bucket, &context.object_key));
|
||||
enc_context
|
||||
}
|
||||
|
||||
const INTERNAL_ENCRYPTION_KEY_ID_HEADER: &str = "x-rustfs-encryption-key-id";
|
||||
|
||||
/// Result of object encryption
|
||||
@@ -178,15 +198,10 @@ impl ObjectEncryptionService {
|
||||
.or_else(|| self.kms_manager.get_default_key_id().map(|s| s.as_str()))
|
||||
.ok_or_else(|| KmsError::configuration_error("No KMS key ID specified and no default configured"))?;
|
||||
|
||||
// Build encryption context
|
||||
let mut enc_context = context.encryption_context.clone();
|
||||
enc_context.insert("bucket".to_string(), context.bucket.clone());
|
||||
enc_context.insert("object_key".to_string(), context.object_key.clone());
|
||||
|
||||
let request = GenerateDataKeyRequest {
|
||||
key_id: actual_key_id.to_string(),
|
||||
key_spec: KeySpec::Aes256,
|
||||
encryption_context: enc_context,
|
||||
encryption_context: request_encryption_context(context),
|
||||
};
|
||||
|
||||
let data_key_response = self.kms_manager.generate_data_key(request).await?;
|
||||
@@ -216,10 +231,10 @@ impl ObjectEncryptionService {
|
||||
/// # Returns
|
||||
/// DataKey with decrypted key
|
||||
///
|
||||
pub async fn decrypt_data_key(&self, encrypted_key: &[u8], _context: &ObjectEncryptionContext) -> Result<DataKey> {
|
||||
pub async fn decrypt_data_key(&self, encrypted_key: &[u8], context: &ObjectEncryptionContext) -> Result<DataKey> {
|
||||
let decrypt_request = DecryptRequest {
|
||||
ciphertext: encrypted_key.to_vec(),
|
||||
encryption_context: HashMap::new(),
|
||||
encryption_context: request_encryption_context(context),
|
||||
grant_tokens: Vec::new(),
|
||||
};
|
||||
|
||||
@@ -864,4 +879,40 @@ mod tests {
|
||||
.is_err()
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_decrypt_data_key_uses_object_encryption_context() {
|
||||
let (service, _temp_dir) = create_test_service().await;
|
||||
service
|
||||
.create_key(CreateKeyRequest {
|
||||
key_name: Some("test-key".to_string()),
|
||||
key_usage: KeyUsage::EncryptDecrypt,
|
||||
description: None,
|
||||
policy: None,
|
||||
tags: HashMap::new(),
|
||||
origin: None,
|
||||
})
|
||||
.await
|
||||
.expect("test key should be created");
|
||||
let create_context = ObjectEncryptionContext::new("bucket".to_string(), "dir/object".to_string())
|
||||
.with_encryption_context("tenant".to_string(), "alpha".to_string());
|
||||
let kms_key = Some("test-key".to_string());
|
||||
let (_data_key, encrypted_key) = service
|
||||
.create_data_key(&kms_key, &create_context)
|
||||
.await
|
||||
.expect("create data key should succeed");
|
||||
|
||||
let wrong_context = ObjectEncryptionContext::new("bucket".to_string(), "dir/object".to_string())
|
||||
.with_encryption_context("tenant".to_string(), "beta".to_string());
|
||||
assert!(
|
||||
service.decrypt_data_key(&encrypted_key, &wrong_context).await.is_err(),
|
||||
"decrypt should reject mismatched KMS context"
|
||||
);
|
||||
|
||||
let decrypted = service
|
||||
.decrypt_data_key(&encrypted_key, &create_context)
|
||||
.await
|
||||
.expect("decrypt should accept matching KMS context");
|
||||
assert_ne!(decrypted.plaintext_key, [0u8; 32]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user