mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-11 07:36:53 +00:00
feat(kms): add vault transit engine
This commit is contained in:
@@ -42,6 +42,48 @@ fn kms_service_manager_from_context() -> std::sync::Arc<rustfs_kms::KmsServiceMa
|
||||
})
|
||||
}
|
||||
|
||||
fn token_is_blank(auth_method: &rustfs_kms::config::VaultAuthMethod) -> bool {
|
||||
matches!(
|
||||
auth_method,
|
||||
rustfs_kms::config::VaultAuthMethod::Token { token } if token.trim().is_empty()
|
||||
)
|
||||
}
|
||||
|
||||
fn existing_vault_auth(config: &KmsConfig) -> Option<rustfs_kms::config::VaultAuthMethod> {
|
||||
match &config.backend_config {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
fn normalize_configure_request_auth(
|
||||
request: &mut ConfigureKmsRequest,
|
||||
existing_config: Option<&KmsConfig>,
|
||||
) -> Result<(), String> {
|
||||
let needs_existing_auth = match request {
|
||||
ConfigureKmsRequest::VaultKv2(req) => token_is_blank(&req.auth_method),
|
||||
ConfigureKmsRequest::VaultTransit(req) => token_is_blank(&req.auth_method),
|
||||
ConfigureKmsRequest::Local(_) => false,
|
||||
};
|
||||
|
||||
if !needs_existing_auth {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let existing_auth = existing_config
|
||||
.and_then(existing_vault_auth)
|
||||
.ok_or_else(|| "Vault token is required when no existing KMS credentials are available".to_string())?;
|
||||
|
||||
match request {
|
||||
ConfigureKmsRequest::VaultKv2(req) => req.auth_method = existing_auth,
|
||||
ConfigureKmsRequest::VaultTransit(req) => req.auth_method = existing_auth,
|
||||
ConfigureKmsRequest::Local(_) => {}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Save KMS configuration to cluster storage
|
||||
#[instrument(skip(config))]
|
||||
async fn save_kms_config(config: &KmsConfig) -> Result<(), String> {
|
||||
@@ -153,7 +195,7 @@ impl Operation for ConfigureKmsHandler {
|
||||
.await
|
||||
.map_err(|e| s3_error!(InvalidRequest, "failed to read request body: {}", e))?;
|
||||
|
||||
let configure_request: ConfigureKmsRequest = if body.is_empty() {
|
||||
let mut configure_request: ConfigureKmsRequest = if body.is_empty() {
|
||||
return Ok(S3Response::new((
|
||||
StatusCode::BAD_REQUEST,
|
||||
Body::from("Request body is required".to_string()),
|
||||
@@ -171,6 +213,11 @@ impl Operation for ConfigureKmsHandler {
|
||||
info!("Configuring KMS with request: {:?}", configure_request);
|
||||
|
||||
let service_manager = kms_service_manager_from_context();
|
||||
let existing_config = service_manager.get_config().await;
|
||||
|
||||
if let Err(e) = normalize_configure_request_auth(&mut configure_request, existing_config.as_ref()) {
|
||||
return Ok(S3Response::new((StatusCode::BAD_REQUEST, Body::from(e))));
|
||||
}
|
||||
|
||||
// Convert request to KmsConfig
|
||||
let kms_config = configure_request.to_kms_config();
|
||||
@@ -508,7 +555,7 @@ impl Operation for ReconfigureKmsHandler {
|
||||
.await
|
||||
.map_err(|e| s3_error!(InvalidRequest, "failed to read request body: {}", e))?;
|
||||
|
||||
let configure_request: ConfigureKmsRequest = if body.is_empty() {
|
||||
let mut configure_request: ConfigureKmsRequest = if body.is_empty() {
|
||||
return Ok(S3Response::new((
|
||||
StatusCode::BAD_REQUEST,
|
||||
Body::from("Request body is required".to_string()),
|
||||
@@ -526,6 +573,11 @@ impl Operation for ReconfigureKmsHandler {
|
||||
info!("Reconfiguring KMS with request: {:?}", configure_request);
|
||||
|
||||
let service_manager = kms_service_manager_from_context();
|
||||
let existing_config = service_manager.get_config().await;
|
||||
|
||||
if let Err(e) = normalize_configure_request_auth(&mut configure_request, existing_config.as_ref()) {
|
||||
return Ok(S3Response::new((StatusCode::BAD_REQUEST, Body::from(e))));
|
||||
}
|
||||
|
||||
// Convert request to KmsConfig
|
||||
let kms_config = configure_request.to_kms_config();
|
||||
|
||||
@@ -22,7 +22,7 @@ use crate::auth::{check_key_valid, get_session_token};
|
||||
use crate::server::{ADMIN_PREFIX, RemoteAddr};
|
||||
use hyper::{HeaderMap, Method, StatusCode};
|
||||
use matchit::Params;
|
||||
use rustfs_kms::init_global_kms_service_manager;
|
||||
use rustfs_kms::{KmsBackend, init_global_kms_service_manager};
|
||||
use rustfs_policy::policy::action::{Action, AdminAction};
|
||||
use s3s::header::CONTENT_TYPE;
|
||||
use s3s::{Body, S3Request, S3Response, S3Result, s3_error};
|
||||
@@ -30,14 +30,26 @@ use serde::{Deserialize, Serialize};
|
||||
use tracing::{error, info, warn};
|
||||
|
||||
async fn kms_encryption_service_from_context() -> Option<std::sync::Arc<rustfs_kms::ObjectEncryptionService>> {
|
||||
let manager = match resolve_kms_runtime_service_manager() {
|
||||
let manager = kms_service_manager_from_context();
|
||||
manager.get_encryption_service().await
|
||||
}
|
||||
|
||||
fn kms_service_manager_from_context() -> std::sync::Arc<rustfs_kms::KmsServiceManager> {
|
||||
match resolve_kms_runtime_service_manager() {
|
||||
Some(manager) => manager,
|
||||
None => {
|
||||
warn!("KMS service manager not initialized, initializing now as fallback");
|
||||
init_global_kms_service_manager()
|
||||
}
|
||||
};
|
||||
manager.get_encryption_service().await
|
||||
}
|
||||
}
|
||||
|
||||
fn backend_name(backend: &KmsBackend) -> &'static str {
|
||||
match backend {
|
||||
KmsBackend::Local => "local",
|
||||
KmsBackend::VaultKv2 => "vault-kv2",
|
||||
KmsBackend::VaultTransit => "vault-transit",
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@@ -168,11 +180,15 @@ impl Operation for KmsStatusHandler {
|
||||
hit_count: hits,
|
||||
miss_count: misses,
|
||||
});
|
||||
let config = kms_service_manager_from_context().get_config().await;
|
||||
|
||||
let response = KmsStatusResponse {
|
||||
backend_type: "vault".to_string(), // TODO: Get from config
|
||||
backend_type: config
|
||||
.as_ref()
|
||||
.map(|cfg| backend_name(&cfg.backend).to_string())
|
||||
.unwrap_or_else(|| "unknown".to_string()),
|
||||
backend_status,
|
||||
cache_enabled: cache_stats.is_some(),
|
||||
cache_enabled: config.as_ref().is_some_and(|cfg| cfg.enable_cache),
|
||||
cache_stats,
|
||||
default_key_id: service.get_default_key_id().cloned(),
|
||||
};
|
||||
@@ -213,12 +229,16 @@ impl Operation for KmsConfigHandler {
|
||||
return Err(s3_error!(InternalError, "KMS service not initialized"));
|
||||
};
|
||||
|
||||
// TODO: Get actual config from service
|
||||
let config = kms_service_manager_from_context()
|
||||
.get_config()
|
||||
.await
|
||||
.ok_or_else(|| s3_error!(InternalError, "KMS config not available"))?;
|
||||
|
||||
let response = KmsConfigResponse {
|
||||
backend: "vault".to_string(),
|
||||
cache_enabled: true,
|
||||
cache_max_keys: 1000,
|
||||
cache_ttl_seconds: 300,
|
||||
backend: backend_name(&config.backend).to_string(),
|
||||
cache_enabled: config.enable_cache,
|
||||
cache_max_keys: config.cache_config.max_keys,
|
||||
cache_ttl_seconds: config.cache_config.ttl.as_secs(),
|
||||
default_key_id: service.get_default_key_id().cloned(),
|
||||
};
|
||||
|
||||
|
||||
@@ -218,7 +218,7 @@ pub struct ServerOpts {
|
||||
#[arg(long, default_value_t = false, env = "RUSTFS_KMS_ENABLE")]
|
||||
pub kms_enable: bool,
|
||||
|
||||
/// KMS backend type (local or vault)
|
||||
/// KMS backend type: local, vault or vault-kv2 (Vault KV2+Transit), vault-transit
|
||||
#[arg(long, default_value_t = rustfs_config::DEFAULT_KMS_BACKEND.to_string(), env = "RUSTFS_KMS_BACKEND")]
|
||||
pub kms_backend: String,
|
||||
|
||||
@@ -234,6 +234,10 @@ pub struct ServerOpts {
|
||||
#[arg(long, env = "RUSTFS_KMS_VAULT_TOKEN")]
|
||||
pub kms_vault_token: Option<String>,
|
||||
|
||||
/// Vault mount path for vault or vault-transit backend
|
||||
#[arg(long, env = "RUSTFS_KMS_VAULT_MOUNT_PATH")]
|
||||
pub kms_vault_mount_path: Option<String>,
|
||||
|
||||
/// Default KMS key ID for encryption
|
||||
#[arg(long, env = "RUSTFS_KMS_DEFAULT_KEY_ID")]
|
||||
pub kms_default_key_id: Option<String>,
|
||||
@@ -284,6 +288,7 @@ pub fn default_server_opts() -> ServerOpts {
|
||||
kms_key_dir: None,
|
||||
kms_vault_address: None,
|
||||
kms_vault_token: None,
|
||||
kms_vault_mount_path: None,
|
||||
kms_default_key_id: None,
|
||||
buffer_profile_disable: false,
|
||||
buffer_profile: "GeneralPurpose".to_string(),
|
||||
|
||||
@@ -97,6 +97,9 @@ pub struct Config {
|
||||
/// Vault token for vault backend
|
||||
pub kms_vault_token: Option<String>,
|
||||
|
||||
/// Vault mount path for vault or vault-transit backend
|
||||
pub kms_vault_mount_path: Option<String>,
|
||||
|
||||
/// Default KMS key ID for encryption
|
||||
pub kms_default_key_id: Option<String>,
|
||||
|
||||
@@ -129,6 +132,7 @@ impl Config {
|
||||
kms_key_dir,
|
||||
kms_vault_address,
|
||||
kms_vault_token,
|
||||
kms_vault_mount_path,
|
||||
kms_default_key_id,
|
||||
buffer_profile_disable,
|
||||
buffer_profile,
|
||||
@@ -157,6 +161,7 @@ impl Config {
|
||||
kms_key_dir,
|
||||
kms_vault_address,
|
||||
kms_vault_token,
|
||||
kms_vault_mount_path,
|
||||
kms_default_key_id,
|
||||
buffer_profile_disable,
|
||||
buffer_profile,
|
||||
@@ -197,6 +202,7 @@ impl std::fmt::Debug for Config {
|
||||
.field("kms_key_dir", &self.kms_key_dir)
|
||||
.field("kms_vault_address", &self.kms_vault_address)
|
||||
.field("kms_vault_token", &Masked(self.kms_vault_token.as_deref()))
|
||||
.field("kms_vault_mount_path", &self.kms_vault_mount_path)
|
||||
.field("kms_default_key_id", &self.kms_default_key_id)
|
||||
.field("buffer_profile_disable", &self.buffer_profile_disable)
|
||||
.field("buffer_profile", &self.buffer_profile)
|
||||
|
||||
@@ -46,6 +46,7 @@ pub struct Opt {
|
||||
pub kms_key_dir: Option<String>,
|
||||
pub kms_vault_address: Option<String>,
|
||||
pub kms_vault_token: Option<String>,
|
||||
pub kms_vault_mount_path: Option<String>,
|
||||
pub kms_default_key_id: Option<String>,
|
||||
pub buffer_profile_disable: bool,
|
||||
pub buffer_profile: String,
|
||||
@@ -73,6 +74,7 @@ impl Opt {
|
||||
kms_key_dir: o.kms_key_dir,
|
||||
kms_vault_address: o.kms_vault_address,
|
||||
kms_vault_token: o.kms_vault_token,
|
||||
kms_vault_mount_path: o.kms_vault_mount_path,
|
||||
kms_default_key_id: o.kms_default_key_id,
|
||||
buffer_profile_disable: o.buffer_profile_disable,
|
||||
buffer_profile: o.buffer_profile,
|
||||
|
||||
+35
-4
@@ -196,14 +196,14 @@ fn build_vault_kms_config(cfg: &config::Config) -> std::io::Result<rustfs_kms::c
|
||||
.ok_or_else(|| Error::other("Vault token is required for vault backend"))?;
|
||||
|
||||
Ok(rustfs_kms::config::KmsConfig {
|
||||
backend: rustfs_kms::config::KmsBackend::Vault,
|
||||
backend_config: rustfs_kms::config::BackendConfig::Vault(Box::new(rustfs_kms::config::VaultConfig {
|
||||
backend: rustfs_kms::config::KmsBackend::VaultKv2,
|
||||
backend_config: rustfs_kms::config::BackendConfig::VaultKv2(Box::new(rustfs_kms::config::VaultConfig {
|
||||
address: vault_address.clone(),
|
||||
auth_method: rustfs_kms::config::VaultAuthMethod::Token {
|
||||
token: vault_token.clone(),
|
||||
},
|
||||
namespace: None,
|
||||
mount_path: "transit".to_string(),
|
||||
mount_path: cfg.kms_vault_mount_path.clone().unwrap_or_else(|| "transit".to_string()),
|
||||
kv_mount: "secret".to_string(),
|
||||
key_path_prefix: "rustfs/kms/keys".to_string(),
|
||||
tls: None,
|
||||
@@ -216,6 +216,36 @@ fn build_vault_kms_config(cfg: &config::Config) -> std::io::Result<rustfs_kms::c
|
||||
})
|
||||
}
|
||||
|
||||
/// Build KMS configuration for Vault Transit backend
|
||||
fn build_vault_transit_kms_config(cfg: &config::Config) -> std::io::Result<rustfs_kms::config::KmsConfig> {
|
||||
let vault_address = cfg
|
||||
.kms_vault_address
|
||||
.as_ref()
|
||||
.ok_or_else(|| Error::other("Vault address is required for vault-transit backend"))?;
|
||||
let vault_token = cfg
|
||||
.kms_vault_token
|
||||
.as_ref()
|
||||
.ok_or_else(|| Error::other("Vault token is required for vault-transit backend"))?;
|
||||
|
||||
Ok(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(),
|
||||
auth_method: rustfs_kms::config::VaultAuthMethod::Token {
|
||||
token: vault_token.clone(),
|
||||
},
|
||||
namespace: None,
|
||||
mount_path: cfg.kms_vault_mount_path.clone().unwrap_or_else(|| "transit".to_string()),
|
||||
tls: None,
|
||||
})),
|
||||
default_key_id: cfg.kms_default_key_id.clone(),
|
||||
timeout: std::time::Duration::from_secs(30),
|
||||
retry_attempts: 3,
|
||||
enable_cache: true,
|
||||
cache_config: rustfs_kms::config::CacheConfig::default(),
|
||||
})
|
||||
}
|
||||
|
||||
/// Configure and start KMS service
|
||||
async fn configure_and_start_kms(
|
||||
service_manager: &std::sync::Arc<rustfs_kms::KmsServiceManager>,
|
||||
@@ -258,7 +288,8 @@ pub(crate) async fn init_kms_system(config: &config::Config) -> std::io::Result<
|
||||
// Create KMS configuration from command line options
|
||||
let kms_config = match config.kms_backend.as_str() {
|
||||
"local" => build_local_kms_config(config)?,
|
||||
"vault" => build_vault_kms_config(config)?,
|
||||
"vault" | "vault-kv2" | "vault_kv2" => build_vault_kms_config(config)?,
|
||||
"vault-transit" | "vault_transit" => build_vault_transit_kms_config(config)?,
|
||||
_ => return Err(Error::other(format!("Unsupported KMS backend: {}", config.kms_backend))),
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user