feat(kms): add static single-key backend (#5222)

This commit is contained in:
唐小鸭
2026-07-26 04:12:56 +08:00
committed by GitHub
parent c984bc7251
commit 99e1f5fbd2
14 changed files with 972 additions and 22 deletions
+3
View File
@@ -65,6 +65,7 @@ fn existing_vault_auth(config: &KmsConfig) -> Option<rustfs_kms::config::VaultAu
rustfs_kms::config::BackendConfig::VaultKv2(vault) => Some(vault.auth_method.clone()),
rustfs_kms::config::BackendConfig::VaultTransit(vault) => Some(vault.auth_method.clone()),
rustfs_kms::config::BackendConfig::Local(_) => None,
rustfs_kms::config::BackendConfig::Static(_) => None,
}
}
@@ -84,6 +85,7 @@ fn normalize_configure_request_auth(
ConfigureKmsRequest::VaultKv2(req) => token_is_blank(&req.auth_method),
ConfigureKmsRequest::VaultTransit(req) => token_is_blank(&req.auth_method),
ConfigureKmsRequest::Local(_) => false,
ConfigureKmsRequest::Static(_) => false,
};
if !needs_existing_auth {
@@ -98,6 +100,7 @@ fn normalize_configure_request_auth(
ConfigureKmsRequest::VaultKv2(req) => req.auth_method = existing_auth,
ConfigureKmsRequest::VaultTransit(req) => req.auth_method = existing_auth,
ConfigureKmsRequest::Local(_) => {}
ConfigureKmsRequest::Static(_) => {}
}
Ok(())
@@ -49,6 +49,7 @@ fn backend_name(backend: &KmsBackend) -> &'static str {
KmsBackend::Local => "local",
KmsBackend::VaultKv2 => "vault-kv2",
KmsBackend::VaultTransit => "vault-transit",
KmsBackend::Static => "static",
}
}
+70 -6
View File
@@ -283,7 +283,7 @@ fn build_local_kms_config(cfg: &config::Config) -> std::io::Result<rustfs_kms::c
.as_ref()
.ok_or_else(|| Error::other("KMS key directory is required for local backend"))?;
Ok(rustfs_kms::config::KmsConfig {
let kms_config = rustfs_kms::config::KmsConfig {
backend: rustfs_kms::config::KmsBackend::Local,
backend_config: rustfs_kms::config::BackendConfig::Local(rustfs_kms::config::LocalConfig {
key_dir: std::path::PathBuf::from(key_dir),
@@ -296,7 +296,11 @@ fn build_local_kms_config(cfg: &config::Config) -> std::io::Result<rustfs_kms::c
retry_attempts: 3,
enable_cache: true,
cache_config: rustfs_kms::config::CacheConfig::default(),
})
};
kms_config
.validate()
.map_err(|e| Error::other(format!("Local KMS configuration validation failed: {e}")))?;
Ok(kms_config)
}
/// Build KMS configuration for Vault backend
@@ -310,7 +314,7 @@ fn build_vault_kms_config(cfg: &config::Config) -> std::io::Result<rustfs_kms::c
.as_ref()
.ok_or_else(|| Error::other("Vault token is required for vault backend"))?;
Ok(rustfs_kms::config::KmsConfig {
let kms_config = rustfs_kms::config::KmsConfig {
backend: rustfs_kms::config::KmsBackend::VaultKv2,
backend_config: rustfs_kms::config::BackendConfig::VaultKv2(Box::new(rustfs_kms::config::VaultConfig {
address: vault_address.clone(),
@@ -329,7 +333,11 @@ fn build_vault_kms_config(cfg: &config::Config) -> std::io::Result<rustfs_kms::c
retry_attempts: 3,
enable_cache: true,
cache_config: rustfs_kms::config::CacheConfig::default(),
})
};
kms_config
.validate()
.map_err(|e| Error::other(format!("Vault KMS configuration validation failed: {e}")))?;
Ok(kms_config)
}
/// Build KMS configuration for Vault Transit backend
@@ -343,7 +351,7 @@ fn build_vault_transit_kms_config(cfg: &config::Config) -> std::io::Result<rustf
.as_ref()
.ok_or_else(|| Error::other("Vault token is required for vault-transit backend"))?;
Ok(rustfs_kms::config::KmsConfig {
let kms_config = rustfs_kms::config::KmsConfig {
backend: rustfs_kms::config::KmsBackend::VaultTransit,
backend_config: rustfs_kms::config::BackendConfig::VaultTransit(Box::new(rustfs_kms::config::VaultTransitConfig {
address: vault_address.clone(),
@@ -360,7 +368,62 @@ fn build_vault_transit_kms_config(cfg: &config::Config) -> std::io::Result<rustf
retry_attempts: 3,
enable_cache: true,
cache_config: rustfs_kms::config::CacheConfig::default(),
})
};
kms_config
.validate()
.map_err(|e| Error::other(format!("Vault Transit KMS configuration validation failed: {e}")))?;
Ok(kms_config)
}
/// Build KMS configuration for static single-key backend
fn build_static_kms_config(cfg: &config::Config) -> std::io::Result<rustfs_kms::config::KmsConfig> {
use rustfs_kms::config::{ENV_KMS_STATIC_SECRET_KEY, ENV_KMS_STATIC_SECRET_KEY_FILE, StaticConfig};
// Read secret from file first, then fall back to env var
let secret_str = if let Some(file_path) = rustfs_utils::get_env_opt_str(ENV_KMS_STATIC_SECRET_KEY_FILE) {
std::fs::read_to_string(&file_path)
.map_err(|e| Error::other(format!("Failed to read static KMS secret key file {file_path}: {e}")))?
} else {
rustfs_utils::get_env_str(ENV_KMS_STATIC_SECRET_KEY, "")
};
let secret_str = secret_str.trim();
if secret_str.is_empty() {
return Err(Error::other(format!(
"Static KMS requires {ENV_KMS_STATIC_SECRET_KEY} or {ENV_KMS_STATIC_SECRET_KEY_FILE} to be set"
)));
}
let colon_pos = secret_str.find(':').ok_or_else(|| {
Error::other(format!(
"Static KMS secret key must be in format <key-name>:<base64-key>, got: {secret_str}"
))
})?;
let key_id = secret_str[..colon_pos].to_string();
let secret_key = secret_str[colon_pos + 1..].to_string();
if key_id.is_empty() || secret_key.is_empty() {
return Err(Error::other("Static KMS secret key must be in format <key-name>:<base64-key>"));
}
// Base64 decoding and 32-byte key length are validated by KmsConfig::validate() below
let static_config = StaticConfig {
key_id: key_id.clone(),
secret_key,
};
let kms_config = rustfs_kms::config::KmsConfig {
backend: rustfs_kms::config::KmsBackend::Static,
default_key_id: cfg.kms_default_key_id.clone().or(Some(key_id)),
backend_config: rustfs_kms::config::BackendConfig::Static(static_config),
allow_insecure_dev_defaults: cfg.kms_allow_insecure_dev_defaults,
..Default::default()
};
kms_config
.validate()
.map_err(|e| Error::other(format!("Static KMS configuration validation failed: {e}")))?;
Ok(kms_config)
}
/// Configure and start KMS service
@@ -423,6 +486,7 @@ pub async fn init_kms_system(config: &config::Config) -> std::io::Result<()> {
"local" => build_local_kms_config(config)?,
"vault" | "vault-kv2" | "vault_kv2" => build_vault_kms_config(config)?,
"vault-transit" | "vault_transit" => build_vault_transit_kms_config(config)?,
"static" => build_static_kms_config(config)?,
_ => return Err(Error::other(format!("Unsupported KMS backend: {}", config.kms_backend))),
};
+12 -3
View File
@@ -1601,12 +1601,21 @@ async fn apply_managed_encryption_material(
// Try to get default key from KMS service (if available)
if let Some(service) = runtime_sources::current_encryption_service().await {
kms_key_candidate = service.get_default_key_id().cloned();
tracing::debug!(
default_key_id = ?kms_key_candidate,
"SSE-S3: KMS service available, default_key_id from config"
);
} else {
tracing::debug!("SSE-S3: KMS encryption service not available");
}
}
let kms_key_to_use = match (encryption_type, kms_key_candidate.clone()) {
(SSEType::SseS3, Some(kms_key_id)) => kms_key_id,
(SSEType::SseS3, None) => "default".to_string(),
(SSEType::SseS3, None) => {
tracing::debug!("SSE-S3: no KMS key configured, falling back to \"default\" key ID");
"default".to_string()
}
(SSEType::SseKms, Some(kms_key_id)) => kms_key_id,
(SSEType::SseKms, None) => {
return Err(ApiError::from(StorageError::other(
@@ -1635,7 +1644,7 @@ async fn apply_managed_encryption_material(
Ok(EncryptionMaterial {
sse_type: encryption_type,
server_side_encryption,
kms_key_id: matches!(encryption_type, SSEType::SseKms).then_some(kms_key_to_use),
kms_key_id: Some(kms_key_to_use),
algorithm,
key_bytes,
base_nonce,
@@ -3523,7 +3532,7 @@ mod tests {
let material = material.expect("managed sse-s3 encryption should return material");
let metadata = encryption_material_to_metadata(&material).expect("managed SSE-S3 metadata should serialize");
assert_eq!(material.kms_key_id, None);
assert_eq!(material.kms_key_id.as_deref(), Some("default"));
assert_eq!(metadata.get("x-amz-server-side-encryption").map(String::as_str), Some("AES256"));
assert!(!metadata.contains_key("x-amz-server-side-encryption-aws-kms-key-id"));
assert_eq!(metadata.get(INTERNAL_ENCRYPTION_KEY_ID_HEADER).map(String::as_str), Some("default"));