From 5cb12300bc96fc5eca3d5a1e10f05ca2c7432033 Mon Sep 17 00:00:00 2001 From: Zhengchao An Date: Wed, 19 Aug 2026 14:30:04 +0800 Subject: [PATCH] refactor(sse): keep one bucket-default algorithm mapping for every writer (#6251) --- rustfs/src/app/object_usecase.rs | 29 +++++------------------------ rustfs/src/app/storage_api.rs | 5 +++-- rustfs/src/storage/sse.rs | 26 ++++++++++++++++++++------ rustfs/src/storage/storage_api.rs | 6 +++--- 4 files changed, 31 insertions(+), 35 deletions(-) diff --git a/rustfs/src/app/object_usecase.rs b/rustfs/src/app/object_usecase.rs index e541a5b3e..2c7405c62 100644 --- a/rustfs/src/app/object_usecase.rs +++ b/rustfs/src/app/object_usecase.rs @@ -97,7 +97,7 @@ use super::storage_api::object_usecase::set_disk::{ }; use super::storage_api::object_usecase::sse::{ DecryptionRequest, EncryptionRequest, SSEType, SseKmsPrincipal, apply_bucket_default_lock_retention, - authorize_sse_kms_object_read, build_ssec_read_headers, encryption_material_to_metadata, + authorize_sse_kms_object_read, bucket_default_write_sse, build_ssec_read_headers, encryption_material_to_metadata, extract_server_side_encryption_from_headers, extract_ssec_params_from_headers, extract_ssekms_context_from_headers, get_buffer_size_opt_in, load_bucket_object_lock_config_state, map_get_object_reader_error, sse_decryption, sse_encryption, validate_bucket_object_lock_enabled_state, @@ -169,8 +169,8 @@ use s3s::dto::{ ObjectLockLegalHoldStatus, ObjectLockMode, ObjectLockRetention, ObjectLockRetentionMode, ObjectPart, PutObjectInput, PutObjectOutput, Range, RequestCharged, RestoreObjectInput, RestoreObjectOutput, RestoreStatus, SSECustomerAlgorithm, SSECustomerKeyMD5, SSEKMSKeyId, SelectObjectContentInput, SelectObjectContentOutput, ServerSideEncryption, - ServerSideEncryptionByDefault, ServerSideEncryptionConfiguration, StorageClass, StreamingBlob, TaggingDirective, - TaggingHeader, Timestamp, TimestampFormat, WebsiteRedirectLocation, + ServerSideEncryptionConfiguration, StorageClass, StreamingBlob, TaggingDirective, TaggingHeader, Timestamp, TimestampFormat, + WebsiteRedirectLocation, }; use s3s::header::{X_AMZ_RESTORE, X_AMZ_RESTORE_OUTPUT_PATH}; use s3s::stream::{ByteStream, DynByteStream, RemainingLength}; @@ -2676,25 +2676,6 @@ fn has_put_sse_request_headers(headers: &HeaderMap) -> bool { || headers.get(AMZ_SERVER_SIDE_ENCRYPTION_KMS_ID).is_some() } -/// Managed SSE resolved from a bucket default encryption rule on the copy path. -/// -/// Unknown algorithms fall back to AES256, the same total mapping as the PUT and -/// extract paths and the storage-layer resolver (`prepare_sse_configuration`), which -/// `sse_encryption` re-runs when it mints the destination DEK. Resolving `None` here -/// instead lets a same-name copy under a malformed bucket default pass the -/// `copy_changes_encryption` guard and take the metadata-only shortcut while the -/// storage layer still encrypts: fresh DEK metadata is committed beside the untouched -/// plaintext blocks and the object becomes unreadable. Reachable only via corrupt or -/// hand-edited bucket metadata — PutBucketEncryption rejects unknown algorithms -/// (backlog#1826). -fn bucket_default_write_sse(sse: &ServerSideEncryptionByDefault) -> ServerSideEncryption { - match sse.sse_algorithm.as_str() { - "AES256" => ServerSideEncryption::from_static(ServerSideEncryption::AES256), - "aws:kms" => ServerSideEncryption::from_static(ServerSideEncryption::AWS_KMS), - _ => ServerSideEncryption::from_static(ServerSideEncryption::AES256), - } -} - /// Resolve the effective server-side encryption for a write against the bucket's /// default encryption configuration. /// @@ -10115,8 +10096,8 @@ mod tests { DefaultRetention, Delete, DeleteMarkerReplication, DeleteMarkerReplicationStatus, DeleteReplication, DeleteReplicationStatus, Destination, ExistingObjectReplication, ExistingObjectReplicationStatus, ObjectIdentifier, ObjectLockConfiguration, ObjectLockEnabled, ObjectLockRule, ReplicaModifications, ReplicaModificationsStatus, - ReplicationConfiguration, ReplicationRule, ReplicationRuleStatus, RestoreRequest, ServerSideEncryptionConfiguration, - ServerSideEncryptionRule, SourceSelectionCriteria, + ReplicationConfiguration, ReplicationRule, ReplicationRuleStatus, RestoreRequest, ServerSideEncryptionByDefault, + ServerSideEncryptionConfiguration, ServerSideEncryptionRule, SourceSelectionCriteria, }; use std::pin::Pin; use std::sync::Arc; diff --git a/rustfs/src/app/storage_api.rs b/rustfs/src/app/storage_api.rs index 0c2b3d4d4..d90cfb6a8 100644 --- a/rustfs/src/app/storage_api.rs +++ b/rustfs/src/app/storage_api.rs @@ -1032,8 +1032,9 @@ pub(crate) mod sse { validate_bucket_object_lock_enabled_state, }; pub(crate) use crate::storage::storage_api::sse_consumer::{ - EncryptionKeyKind, SSEType, build_ssec_read_headers, encryption_material_to_metadata, extract_ssec_params_from_headers, - extract_ssekms_context_from_headers, map_get_object_reader_error, mark_encrypted_multipart_metadata, + EncryptionKeyKind, SSEType, bucket_default_write_sse, build_ssec_read_headers, encryption_material_to_metadata, + extract_ssec_params_from_headers, extract_ssekms_context_from_headers, map_get_object_reader_error, + mark_encrypted_multipart_metadata, }; } diff --git a/rustfs/src/storage/sse.rs b/rustfs/src/storage/sse.rs index 33b03f8c7..b3e4f839f 100644 --- a/rustfs/src/storage/sse.rs +++ b/rustfs/src/storage/sse.rs @@ -164,7 +164,7 @@ use rustfs_utils::http::headers::{ AMZ_SERVER_SIDE_ENCRYPTION_CUSTOMER_KEY_MD5, AMZ_SERVER_SIDE_ENCRYPTION_KMS_CONTEXT, }; use rustfs_utils::path::path_join_buf; -use s3s::dto::{SSECustomerAlgorithm, SSECustomerKey, SSECustomerKeyMD5, SSEKMSKeyId}; +use s3s::dto::{SSECustomerAlgorithm, SSECustomerKey, SSECustomerKeyMD5, SSEKMSKeyId, ServerSideEncryptionByDefault}; use std::borrow::Cow; // ============================================================================ @@ -203,6 +203,24 @@ pub struct SseConfiguration { /// Effective KMS key ID (after considering bucket defaults) pub effective_kms_key_id: Option, } +/// Managed SSE resolved from a bucket default encryption rule on a write path. +/// +/// The single mapping shared by every writer: this resolver, and the PUT, COPY +/// and extract paths in `app::object_usecase`, which reach it through +/// `resolve_bucket_default_sse`. Unknown algorithms fall back to AES256 rather +/// than to `None`. Resolving `None` instead lets a same-name copy under a +/// malformed bucket default pass the `copy_changes_encryption` guard and take +/// the metadata-only shortcut while this layer still encrypts: fresh DEK +/// metadata is committed beside the untouched plaintext blocks and the object +/// becomes unreadable. Reachable only via corrupt or hand-edited bucket +/// metadata — PutBucketEncryption rejects unknown algorithms (backlog#1826). +pub(crate) fn bucket_default_write_sse(sse: &ServerSideEncryptionByDefault) -> ServerSideEncryption { + match sse.sse_algorithm.as_str() { + "AES256" => ServerSideEncryption::from_static(ServerSideEncryption::AES256), + "aws:kms" => ServerSideEncryption::from_static(ServerSideEncryption::AWS_KMS), + _ => ServerSideEncryption::from_static(ServerSideEncryption::AES256), + } +} /// Prepare SSE configuration by resolving request parameters with bucket defaults /// @@ -266,11 +284,7 @@ async fn prepare_sse_configuration( has_kms_key_id = sse.kms_master_key_id.is_some(), "Bucket SSE default resolved" ); - match sse.sse_algorithm.as_str() { - "AES256" => ServerSideEncryption::from_static(ServerSideEncryption::AES256), - "aws:kms" => ServerSideEncryption::from_static(ServerSideEncryption::AWS_KMS), - _ => ServerSideEncryption::from_static(ServerSideEncryption::AES256), // fallback - } + bucket_default_write_sse(sse) }) }) }); diff --git a/rustfs/src/storage/storage_api.rs b/rustfs/src/storage/storage_api.rs index ef9a92414..31668783a 100644 --- a/rustfs/src/storage/storage_api.rs +++ b/rustfs/src/storage/storage_api.rs @@ -344,9 +344,9 @@ pub(crate) mod s3_api_consumer { pub(crate) mod sse_consumer { pub(crate) use super::super::sse::{ - EncryptionKeyKind, SSEType, build_ssec_read_headers, encryption_material_to_metadata, extract_ssec_params_from_headers, - extract_ssekms_context_from_headers, log_sse_kms_key_policy_mode, map_get_object_reader_error, - mark_encrypted_multipart_metadata, + EncryptionKeyKind, SSEType, bucket_default_write_sse, build_ssec_read_headers, encryption_material_to_metadata, + extract_ssec_params_from_headers, extract_ssekms_context_from_headers, log_sse_kms_key_policy_mode, + map_get_object_reader_error, mark_encrypted_multipart_metadata, }; pub(crate) use super::{ DecryptionRequest, EncryptionRequest, PrepareEncryptionRequest, SseKmsPrincipal, apply_bucket_default_lock_retention,