mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-08 06:13:14 +00:00
feat(kms): add static single-key backend (#5222)
This commit is contained in:
@@ -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
@@ -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))),
|
||||
};
|
||||
|
||||
|
||||
@@ -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"));
|
||||
|
||||
Reference in New Issue
Block a user