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:
唐小鸭
2026-06-08 19:59:14 +08:00
committed by GitHub
parent 9504dff595
commit f7724d223b
47 changed files with 8742 additions and 682 deletions
+59 -8
View File
@@ -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]);
}
}