mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-30 16:59:52 +00:00
feat(kms): surface non-production backend positioning at runtime (#6633)
This commit is contained in:
@@ -821,6 +821,7 @@ impl KmsBackend for AwsKmsBackend {
|
||||
.with_schedule_deletion(true)
|
||||
.with_versioning(true)
|
||||
.with_physical_delete(false)
|
||||
.with_production_supported(true)
|
||||
}
|
||||
|
||||
/// Observe, never destroy.
|
||||
|
||||
@@ -2279,7 +2279,10 @@ impl KmsBackend for LocalKmsBackend {
|
||||
fn capabilities(&self) -> BackendCapabilities {
|
||||
// Rotation stays unadvertised until historical key versions can be
|
||||
// retained (see LocalKmsClient::rotate_key); without version history
|
||||
// there is also no versioning capability.
|
||||
// there is also no versioning capability. `production_supported` stays
|
||||
// false by positioning decision: the Local backend keeps its
|
||||
// cryptographic root on the host filesystem and exists for
|
||||
// development, testing and demos only.
|
||||
BackendCapabilities::minimal()
|
||||
.with_enable_disable(true)
|
||||
.with_schedule_deletion(true)
|
||||
|
||||
@@ -615,6 +615,14 @@ pub struct BackendCapabilities {
|
||||
pub update_key_metadata: bool,
|
||||
/// Re-wrapping an existing data key envelope onto the key's current version
|
||||
pub rewrap: bool,
|
||||
/// Whether this backend is positioned for production use.
|
||||
///
|
||||
/// Backends that keep their cryptographic root on the local host (Local,
|
||||
/// Static) are for development, testing and demos only; this flag is how
|
||||
/// that positioning reaches logs, the status API and the console without
|
||||
/// each consumer matching on backend names.
|
||||
#[serde(default)]
|
||||
pub production_supported: bool,
|
||||
}
|
||||
|
||||
impl BackendCapabilities {
|
||||
@@ -633,6 +641,7 @@ impl BackendCapabilities {
|
||||
physical_delete: false,
|
||||
update_key_metadata: false,
|
||||
rewrap: false,
|
||||
production_supported: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -695,6 +704,12 @@ impl BackendCapabilities {
|
||||
self.rewrap = rewrap;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set whether the backend is positioned for production use
|
||||
pub const fn with_production_supported(mut self, production_supported: bool) -> Self {
|
||||
self.production_supported = production_supported;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for BackendCapabilities {
|
||||
@@ -775,6 +790,30 @@ mod tests {
|
||||
assert!(!capabilities.physical_delete);
|
||||
assert!(!capabilities.update_key_metadata);
|
||||
assert!(!capabilities.rewrap);
|
||||
// Production positioning is an explicit claim, never inherited.
|
||||
assert!(!capabilities.production_supported);
|
||||
}
|
||||
|
||||
/// Older peers and consoles serialize capabilities without the
|
||||
/// positioning flag; deserializing their payloads must not fail and must
|
||||
/// default to the conservative claim.
|
||||
#[test]
|
||||
fn capabilities_without_positioning_field_deserialize_as_non_production() {
|
||||
let legacy = serde_json::json!({
|
||||
"encrypt": true,
|
||||
"decrypt": true,
|
||||
"generate_data_key": true,
|
||||
"rotate": true,
|
||||
"enable_disable": true,
|
||||
"schedule_deletion": true,
|
||||
"versioning": true,
|
||||
"physical_delete": true,
|
||||
"update_key_metadata": true,
|
||||
"rewrap": true,
|
||||
});
|
||||
let capabilities: BackendCapabilities =
|
||||
serde_json::from_value(legacy).expect("legacy capability payloads must stay deserializable");
|
||||
assert!(!capabilities.production_supported);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
+1
@@ -8,6 +8,7 @@ expression: capabilities_snapshot(backend.capabilities())
|
||||
"encrypt": true,
|
||||
"generate_data_key": true,
|
||||
"physical_delete": false,
|
||||
"production_supported": true,
|
||||
"rewrap": false,
|
||||
"rotate": true,
|
||||
"schedule_deletion": true,
|
||||
|
||||
+1
@@ -8,6 +8,7 @@ expression: capabilities_snapshot(backend.capabilities())
|
||||
"encrypt": true,
|
||||
"generate_data_key": true,
|
||||
"physical_delete": true,
|
||||
"production_supported": false,
|
||||
"rewrap": false,
|
||||
"rotate": false,
|
||||
"schedule_deletion": true,
|
||||
|
||||
+1
@@ -8,6 +8,7 @@ expression: capabilities_snapshot(backend.capabilities())
|
||||
"encrypt": true,
|
||||
"generate_data_key": true,
|
||||
"physical_delete": false,
|
||||
"production_supported": false,
|
||||
"rewrap": false,
|
||||
"rotate": false,
|
||||
"schedule_deletion": false,
|
||||
|
||||
+1
@@ -8,6 +8,7 @@ expression: capabilities_snapshot(backend.capabilities())
|
||||
"encrypt": true,
|
||||
"generate_data_key": true,
|
||||
"physical_delete": true,
|
||||
"production_supported": true,
|
||||
"rewrap": true,
|
||||
"rotate": true,
|
||||
"schedule_deletion": true,
|
||||
|
||||
+1
@@ -8,6 +8,7 @@ expression: capabilities_snapshot(backend.capabilities())
|
||||
"encrypt": true,
|
||||
"generate_data_key": true,
|
||||
"physical_delete": true,
|
||||
"production_supported": true,
|
||||
"rewrap": true,
|
||||
"rotate": true,
|
||||
"schedule_deletion": true,
|
||||
|
||||
@@ -413,7 +413,9 @@ impl KmsBackend for StaticKmsBackend {
|
||||
|
||||
fn capabilities(&self) -> BackendCapabilities {
|
||||
// Static KMS is a read-only single-key backend: it only performs
|
||||
// cryptographic operations and rejects every lifecycle mutation.
|
||||
// cryptographic operations and rejects every lifecycle mutation. Its
|
||||
// key material comes straight from configuration, so like Local it is
|
||||
// a development/testing backend and never production_supported.
|
||||
BackendCapabilities::minimal()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2257,6 +2257,7 @@ impl KmsBackend for VaultKmsBackend {
|
||||
.with_physical_delete(true)
|
||||
.with_update_key_metadata(true)
|
||||
.with_rewrap(true)
|
||||
.with_production_supported(true)
|
||||
}
|
||||
|
||||
async fn remove_expired_key(&self, key_id: &str, now: &Zoned) -> Result<ExpiredKeyRemoval> {
|
||||
|
||||
@@ -1767,6 +1767,7 @@ impl KmsBackend for VaultTransitKmsBackend {
|
||||
.with_physical_delete(true)
|
||||
.with_update_key_metadata(true)
|
||||
.with_rewrap(true)
|
||||
.with_production_supported(true)
|
||||
}
|
||||
|
||||
async fn remove_expired_key(&self, key_id: &str, now: &Zoned) -> Result<ExpiredKeyRemoval> {
|
||||
|
||||
Reference in New Issue
Block a user