fix(kms): fail closed on missing or corrupt key material (#5475)

This commit is contained in:
Zhengchao An
2026-07-30 22:56:30 +08:00
committed by GitHub
parent 6e5f330ff5
commit 19cdd806a2
6 changed files with 370 additions and 58 deletions
+14
View File
@@ -851,6 +851,13 @@ impl Operation for DeleteKmsKeyHandler {
let status = match &e {
KmsError::KeyNotFound { .. } => StatusCode::NOT_FOUND,
KmsError::InvalidOperation { .. } | KmsError::ValidationError { .. } => StatusCode::BAD_REQUEST,
// Damaged or missing key material is an integrity fault of an existing
// key: it must surface as a server error, never as NOT_FOUND (the key
// exists) and never as a retryable backend outage.
KmsError::MaterialMissing { .. }
| KmsError::MaterialCorrupt { .. }
| KmsError::MaterialAuthenticationFailed { .. }
| KmsError::UnsupportedFormatVersion { .. } => StatusCode::INTERNAL_SERVER_ERROR,
_ => StatusCode::INTERNAL_SERVER_ERROR,
};
let response = DeleteKmsKeyResponse {
@@ -1265,6 +1272,13 @@ impl Operation for DescribeKmsKeyHandler {
let status = match &e {
KmsError::KeyNotFound { .. } => StatusCode::NOT_FOUND,
KmsError::InvalidOperation { .. } => StatusCode::BAD_REQUEST,
// Damaged or missing key material is an integrity fault of an existing
// key: it must surface as a server error, never as NOT_FOUND (the key
// exists) and never as a retryable backend outage.
KmsError::MaterialMissing { .. }
| KmsError::MaterialCorrupt { .. }
| KmsError::MaterialAuthenticationFailed { .. }
| KmsError::UnsupportedFormatVersion { .. } => StatusCode::INTERNAL_SERVER_ERROR,
_ => StatusCode::INTERNAL_SERVER_ERROR,
};
+23
View File
@@ -538,6 +538,29 @@ mod tests {
assert_eq!(api_error.code, S3ErrorCode::InternalError);
}
#[test]
fn test_kms_material_faults_map_to_internal_error_not_retryable_or_not_found() {
// Missing/corrupt key material is a persistent integrity fault of an existing key.
// It must not be disguised as a retryable backend outage (503 invites pointless
// retries) nor as NoSuchKey/404 (which suggests the key can be recreated —
// recreating it would orphan every DEK wrapped by the original material).
let material_faults = [
rustfs_kms::KmsError::material_missing("key-a"),
rustfs_kms::KmsError::material_corrupt("key-a", "truncated record"),
rustfs_kms::KmsError::material_authentication_failed("key-a"),
rustfs_kms::KmsError::unsupported_format_version("key-a", "v99"),
];
for fault in material_faults {
let description = fault.to_string();
let api_error = ApiError::from(StorageError::other(fault));
assert_eq!(api_error.code, S3ErrorCode::InternalError, "wrong code for: {description}");
assert_ne!(api_error.code, S3ErrorCode::ServiceUnavailable, "must not be retryable: {description}");
assert_ne!(api_error.code, S3ErrorCode::NoSuchKey, "must not report key-not-found: {description}");
}
}
#[test]
fn test_api_error_from_storage_error_mappings() {
let test_cases = vec![