mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-18 10:43:15 +00:00
refactor(logging): standardize protocol and observability events (#3419)
* refactor(logging): standardize object capacity events * refactor(logging): standardize protocol server events * refactor(logging): standardize swift protocol events * refactor(logging): standardize observability events * refactor(logging): move masking helper and extend guardrails
This commit is contained in:
@@ -17,6 +17,7 @@ use crate::common::gateway::S3Action;
|
||||
use crate::common::gateway::authorize_operation;
|
||||
use async_trait::async_trait;
|
||||
use futures_util::stream;
|
||||
use rustfs_utils::MaskedAccessKey;
|
||||
use rustfs_utils::path;
|
||||
use s3s::dto::*;
|
||||
use std::fmt::Debug;
|
||||
@@ -25,6 +26,19 @@ use tokio::io::AsyncRead;
|
||||
use tracing::{debug, error};
|
||||
use unftp_core::storage::{Error, ErrorKind, Fileinfo, Metadata, Result, StorageBackend};
|
||||
|
||||
const LOG_COMPONENT_PROTOCOLS: &str = "protocols";
|
||||
const LOG_SUBSYSTEM_FTPS_DRIVER: &str = "ftps_driver";
|
||||
const EVENT_FTPS_BUCKET_DELETE_FAILED: &str = "ftps_bucket_delete_failed";
|
||||
const EVENT_FTPS_METADATA_FAILED: &str = "ftps_metadata_failed";
|
||||
const EVENT_FTPS_LIST_FAILED: &str = "ftps_list_failed";
|
||||
const EVENT_FTPS_STREAM_READ_FAILED: &str = "ftps_stream_read_failed";
|
||||
const EVENT_FTPS_OBJECT_GET_FAILED: &str = "ftps_object_get_failed";
|
||||
const EVENT_FTPS_OBJECT_PUT_FAILED: &str = "ftps_object_put_failed";
|
||||
const EVENT_FTPS_OBJECT_DELETE_STATE: &str = "ftps_object_delete_state";
|
||||
const EVENT_FTPS_DIRECTORY_STATE: &str = "ftps_directory_state";
|
||||
const EVENT_FTPS_CWD_FAILED: &str = "ftps_cwd_failed";
|
||||
const EVENT_FTPS_RENAME_STATE: &str = "ftps_rename_state";
|
||||
|
||||
/// FTPS metadata implementation
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FtpsMetadata {
|
||||
@@ -210,7 +224,14 @@ where
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) if e.to_string().contains("NoSuchBucket") => Ok(()),
|
||||
Err(e) => {
|
||||
error!("Failed to delete bucket '{}': {}", bucket, e);
|
||||
error!(
|
||||
event = EVENT_FTPS_BUCKET_DELETE_FAILED,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_DRIVER,
|
||||
bucket = %bucket,
|
||||
error = %e,
|
||||
"ftps bucket delete failed"
|
||||
);
|
||||
Err(Error::new(ErrorKind::PermanentFileNotAvailable, format!("Delete bucket failed: {}", e)))
|
||||
}
|
||||
}
|
||||
@@ -263,7 +284,16 @@ where
|
||||
})
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to get metadata for '{}': {}", path_str, e);
|
||||
error!(
|
||||
event = EVENT_FTPS_METADATA_FAILED,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_DRIVER,
|
||||
path = %path_str,
|
||||
bucket = %bucket,
|
||||
object = %key,
|
||||
error = %e,
|
||||
"ftps metadata failed"
|
||||
);
|
||||
Err(Error::new(ErrorKind::PermanentFileNotAvailable, format!("{}: {}", "Metadata failed", e)))
|
||||
}
|
||||
}
|
||||
@@ -290,7 +320,15 @@ where
|
||||
is_dir: true,
|
||||
}),
|
||||
Err(e) => {
|
||||
error!("Failed to get bucket metadata for '{}': {}", bucket_clone, e);
|
||||
error!(
|
||||
event = EVENT_FTPS_METADATA_FAILED,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_DRIVER,
|
||||
path = %path_str,
|
||||
bucket = %bucket_clone,
|
||||
error = %e,
|
||||
"ftps metadata failed"
|
||||
);
|
||||
Err(Error::new(
|
||||
ErrorKind::PermanentFileNotAvailable,
|
||||
format!("{}: {}", "Bucket metadata failed", e),
|
||||
@@ -423,7 +461,15 @@ where
|
||||
Ok(fileinfos)
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to list '{}': {}", path_str, e);
|
||||
error!(
|
||||
event = EVENT_FTPS_LIST_FAILED,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_DRIVER,
|
||||
path = %path_str,
|
||||
bucket = %prefix_with_slash.unwrap_or_default(),
|
||||
error = %e,
|
||||
"ftps list failed"
|
||||
);
|
||||
Err(Error::new(ErrorKind::PermanentFileNotAvailable, format!("{}: {}", "List failed", e)))
|
||||
}
|
||||
}
|
||||
@@ -437,6 +483,7 @@ where
|
||||
) -> Result<Box<dyn AsyncRead + Send + Sync + Unpin>> {
|
||||
let path_str = path.as_ref().to_string_lossy();
|
||||
let session_context = &user.session_context;
|
||||
let masked_username = MaskedAccessKey(&user.username);
|
||||
|
||||
let (bucket, key) = self
|
||||
.parse_s3_path(&path_str)
|
||||
@@ -472,7 +519,17 @@ where
|
||||
match chunk_result {
|
||||
Ok(bytes) => data.extend_from_slice(&bytes),
|
||||
Err(e) => {
|
||||
error!("Error reading stream: {}", e);
|
||||
error!(
|
||||
event = EVENT_FTPS_STREAM_READ_FAILED,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_DRIVER,
|
||||
username = %masked_username,
|
||||
path = %path_str,
|
||||
bucket = %bucket,
|
||||
object = %key,
|
||||
error = %e,
|
||||
"ftps stream read failed"
|
||||
);
|
||||
return Err(Error::new(ErrorKind::PermanentFileNotAvailable, format!("Stream error: {}", e)));
|
||||
}
|
||||
}
|
||||
@@ -481,7 +538,18 @@ where
|
||||
Ok(Box::new(std::io::Cursor::new(data)))
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to get '{}': {}", path_str, e);
|
||||
error!(
|
||||
event = EVENT_FTPS_OBJECT_GET_FAILED,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_DRIVER,
|
||||
username = %masked_username,
|
||||
path = %path_str,
|
||||
bucket = %bucket,
|
||||
object = %key,
|
||||
start_pos,
|
||||
error = %e,
|
||||
"ftps object get failed"
|
||||
);
|
||||
Err(Error::new(ErrorKind::PermanentFileNotAvailable, format!("{}: {}", "Get failed", e)))
|
||||
}
|
||||
}
|
||||
@@ -496,6 +564,7 @@ where
|
||||
) -> Result<u64> {
|
||||
let path_str = path.as_ref().to_string_lossy();
|
||||
let session_context = &user.session_context;
|
||||
let masked_username = MaskedAccessKey(&user.username);
|
||||
|
||||
let (bucket, key) = self
|
||||
.parse_s3_path(&path_str)
|
||||
@@ -555,7 +624,18 @@ where
|
||||
Ok(file_size as u64) // Return the size of the uploaded object
|
||||
}
|
||||
Err(e) => {
|
||||
error!("FTPS put - S3 error details: {:?}", e);
|
||||
error!(
|
||||
event = EVENT_FTPS_OBJECT_PUT_FAILED,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_DRIVER,
|
||||
username = %masked_username,
|
||||
path = %path_str,
|
||||
bucket = %bucket,
|
||||
object = %key,
|
||||
file_size,
|
||||
error = ?e,
|
||||
"ftps object put failed"
|
||||
);
|
||||
Err(Error::new(
|
||||
ErrorKind::PermanentFileNotAvailable,
|
||||
format!("Failed to upload object: {:?}", e),
|
||||
@@ -567,7 +647,16 @@ where
|
||||
async fn del<P: AsRef<Path> + Send>(&self, user: &super::server::FtpsUser, path: P) -> Result<()> {
|
||||
let path_str = path.as_ref().to_string_lossy();
|
||||
let session_context = &user.session_context;
|
||||
debug!("FTPS delete request for user '{}' path '{}'", user.username, path_str);
|
||||
let masked_username = MaskedAccessKey(&user.username);
|
||||
debug!(
|
||||
event = EVENT_FTPS_OBJECT_DELETE_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_DRIVER,
|
||||
state = "requested",
|
||||
username = %masked_username,
|
||||
path = %path_str,
|
||||
"ftps object delete state changed"
|
||||
);
|
||||
|
||||
let (bucket, key) = self
|
||||
.parse_s3_path(&path_str)
|
||||
@@ -592,7 +681,18 @@ where
|
||||
{
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
error!("Failed to delete file '{}': {}", path_str, e);
|
||||
error!(
|
||||
event = EVENT_FTPS_OBJECT_DELETE_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_DRIVER,
|
||||
state = "delete_failed",
|
||||
username = %masked_username,
|
||||
path = %path_str,
|
||||
bucket = %bucket,
|
||||
object = %key,
|
||||
error = %e,
|
||||
"ftps object delete state changed"
|
||||
);
|
||||
Err(Error::new(ErrorKind::PermanentFileNotAvailable, format!("Delete failed: {}", e)))
|
||||
}
|
||||
}
|
||||
@@ -615,7 +715,16 @@ where
|
||||
async fn mkd<P: AsRef<Path> + Send>(&self, user: &super::server::FtpsUser, path: P) -> Result<()> {
|
||||
let path_str = path.as_ref().to_string_lossy();
|
||||
let session_context = &user.session_context;
|
||||
debug!("FTPS mkdir request for user '{}' path '{}'", user.username, path_str);
|
||||
let masked_username = MaskedAccessKey(&user.username);
|
||||
debug!(
|
||||
event = EVENT_FTPS_DIRECTORY_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_DRIVER,
|
||||
state = "create_requested",
|
||||
username = %masked_username,
|
||||
path = %path_str,
|
||||
"ftps directory state changed"
|
||||
);
|
||||
|
||||
let (bucket, _key) = self
|
||||
.parse_s3_path(&path_str)
|
||||
@@ -632,11 +741,30 @@ where
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
debug!("Successfully created directory/bucket '{}'", path_str);
|
||||
debug!(
|
||||
event = EVENT_FTPS_DIRECTORY_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_DRIVER,
|
||||
state = "created",
|
||||
username = %masked_username,
|
||||
path = %path_str,
|
||||
bucket = %bucket,
|
||||
"ftps directory state changed"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Failed to create directory/bucket '{}': {}", path_str, e);
|
||||
error!(
|
||||
event = EVENT_FTPS_DIRECTORY_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_DRIVER,
|
||||
state = "create_failed",
|
||||
username = %masked_username,
|
||||
path = %path_str,
|
||||
bucket = %bucket,
|
||||
error = %e,
|
||||
"ftps directory state changed"
|
||||
);
|
||||
Err(Error::new(ErrorKind::PermanentFileNotAvailable, format!("Mkdir failed: {}", e)))
|
||||
}
|
||||
}
|
||||
@@ -658,17 +786,41 @@ where
|
||||
// Try to delete bucket recursively
|
||||
match self.delete_bucket_recursively(&bucket, session_context).await {
|
||||
Ok(_) => {
|
||||
debug!("Successfully removed directory/bucket '{}'", path_str);
|
||||
debug!(
|
||||
event = EVENT_FTPS_DIRECTORY_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_DRIVER,
|
||||
state = "removed",
|
||||
path = %path_str,
|
||||
bucket = %bucket,
|
||||
"ftps directory state changed"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
// Check if error is NoSuchBucket - treat as success (idempotent)
|
||||
let error_msg = e.to_string();
|
||||
if error_msg.contains("NoSuchBucket") || error_msg.contains("does not exist") {
|
||||
debug!("Bucket '{}' already deleted", bucket);
|
||||
debug!(
|
||||
event = EVENT_FTPS_DIRECTORY_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_DRIVER,
|
||||
state = "already_removed",
|
||||
bucket = %bucket,
|
||||
"ftps directory state changed"
|
||||
);
|
||||
Ok(())
|
||||
} else {
|
||||
error!("Failed to remove directory/bucket '{}': {}", path_str, e);
|
||||
error!(
|
||||
event = EVENT_FTPS_DIRECTORY_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_DRIVER,
|
||||
state = "remove_failed",
|
||||
path = %path_str,
|
||||
bucket = %bucket,
|
||||
error = %e,
|
||||
"ftps directory state changed"
|
||||
);
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
@@ -700,7 +852,15 @@ where
|
||||
{
|
||||
Ok(_) => Ok(()),
|
||||
Err(e) => {
|
||||
error!("CWD to '{}' failed: {}", path_str, e);
|
||||
error!(
|
||||
event = EVENT_FTPS_CWD_FAILED,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_DRIVER,
|
||||
path = %path_str,
|
||||
bucket = %bucket,
|
||||
error = %e,
|
||||
"ftps cwd failed"
|
||||
);
|
||||
Err(Error::new(ErrorKind::PermanentFileNotAvailable, format!("CWD failed: {}", e)))
|
||||
}
|
||||
}
|
||||
@@ -709,7 +869,16 @@ where
|
||||
async fn rename<P: AsRef<Path> + Send>(&self, user: &super::server::FtpsUser, from: P, to: P) -> Result<()> {
|
||||
let from_str = from.as_ref().to_string_lossy();
|
||||
let to_str = to.as_ref().to_string_lossy();
|
||||
debug!("FTPS rename request for user '{}' from '{}' to '{}'", user.username, from_str, to_str);
|
||||
debug!(
|
||||
event = EVENT_FTPS_RENAME_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_DRIVER,
|
||||
state = "unsupported",
|
||||
username = %MaskedAccessKey(&user.username),
|
||||
from = %from_str,
|
||||
to = %to_str,
|
||||
"ftps rename state changed"
|
||||
);
|
||||
|
||||
Err(Error::new(
|
||||
ErrorKind::CommandNotImplemented,
|
||||
|
||||
@@ -20,6 +20,7 @@ use crate::constants::{network::DEFAULT_SOURCE_IP, paths::ROOT_PATH};
|
||||
use libunftp::options::FtpsRequired;
|
||||
use rustfs_config::{DEFAULT_TLS_RELOAD_ENABLE, DEFAULT_TLS_RELOAD_INTERVAL, ENV_TLS_RELOAD_ENABLE, ENV_TLS_RELOAD_INTERVAL};
|
||||
use rustfs_tls_runtime::{ReloadableServerCertResolver, TlsReloadOptions, spawn_server_cert_reload_loop};
|
||||
use rustfs_utils::MaskedAccessKey;
|
||||
use std::fmt::{Debug, Display, Formatter};
|
||||
use std::net::IpAddr;
|
||||
use std::path::Path;
|
||||
@@ -32,6 +33,15 @@ use unftp_core::auth::{
|
||||
AuthenticationError, Authenticator, Credentials, Principal, UserDetail, UserDetailError, UserDetailProvider,
|
||||
};
|
||||
|
||||
const LOG_COMPONENT_PROTOCOLS: &str = "protocols";
|
||||
const LOG_SUBSYSTEM_FTPS_SERVER: &str = "ftps_server";
|
||||
const LOG_SUBSYSTEM_FTPS_AUTH: &str = "ftps_auth";
|
||||
const EVENT_FTPS_SERVER_STATE: &str = "ftps_server_state";
|
||||
const EVENT_FTPS_TLS_STATE: &str = "ftps_tls_state";
|
||||
const EVENT_FTPS_CONFIG_STATE: &str = "ftps_config_state";
|
||||
const EVENT_FTPS_RUNTIME_STATE: &str = "ftps_runtime_state";
|
||||
const EVENT_FTPS_AUTH_STATE: &str = "ftps_auth_state";
|
||||
|
||||
/// FTPS user implementation
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct FtpsUser {
|
||||
@@ -89,7 +99,16 @@ where
|
||||
/// This method binds the listener first to ensure the port is available,
|
||||
/// then spawns the server loop in a background task.
|
||||
pub async fn start(&self, mut shutdown_rx: broadcast::Receiver<()>) -> Result<(), FtpsInitError> {
|
||||
info!("Initializing FTPS server on {}", self.config.bind_addr);
|
||||
info!(
|
||||
event = EVENT_FTPS_SERVER_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_SERVER,
|
||||
state = "starting",
|
||||
bind_addr = %self.config.bind_addr,
|
||||
tls_enabled = self.config.tls_enabled,
|
||||
ftps_required = self.config.ftps_required,
|
||||
"ftps server state changed"
|
||||
);
|
||||
let (reload_shutdown_tx, reload_shutdown_rx) = watch::channel(false);
|
||||
|
||||
let storage_clone = self.storage.clone();
|
||||
@@ -102,27 +121,62 @@ where
|
||||
// Configure passive ports for data connections
|
||||
if let Some(passive_ports) = &self.config.passive_ports {
|
||||
let range = self.config.parse_passive_ports()?;
|
||||
info!("Configuring FTPS passive ports range: {:?} ({})", range, passive_ports);
|
||||
info!(
|
||||
event = EVENT_FTPS_CONFIG_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_SERVER,
|
||||
state = "passive_ports_configured",
|
||||
passive_ports = %passive_ports,
|
||||
passive_port_range = ?range,
|
||||
"ftps config state changed"
|
||||
);
|
||||
server_builder = server_builder.passive_ports(range);
|
||||
} else {
|
||||
warn!("No passive ports configured, using system-assigned ports");
|
||||
warn!(
|
||||
event = EVENT_FTPS_CONFIG_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_SERVER,
|
||||
result = "system_assigned_passive_ports",
|
||||
"ftps config state changed"
|
||||
);
|
||||
}
|
||||
|
||||
// Configure external IP address for passive mode
|
||||
if let Some(ref external_ip) = self.config.external_ip {
|
||||
info!("Configuring FTPS external IP for passive mode: {}", external_ip);
|
||||
info!(
|
||||
event = EVENT_FTPS_CONFIG_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_SERVER,
|
||||
state = "external_ip_configured",
|
||||
external_ip = %external_ip,
|
||||
"ftps config state changed"
|
||||
);
|
||||
server_builder = server_builder.passive_host(external_ip.as_str());
|
||||
}
|
||||
|
||||
// Configure both active and passive mode support
|
||||
use libunftp::options::ActivePassiveMode;
|
||||
server_builder = server_builder.active_passive_mode(ActivePassiveMode::ActiveAndPassive);
|
||||
info!("FTPS server configured for both active and passive mode support");
|
||||
info!(
|
||||
event = EVENT_FTPS_CONFIG_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_SERVER,
|
||||
state = "active_passive_mode_enabled",
|
||||
mode = "active_and_passive",
|
||||
"ftps config state changed"
|
||||
);
|
||||
|
||||
// Configure FTPS / TLS
|
||||
if self.config.tls_enabled {
|
||||
if let Some(cert_dir) = &self.config.cert_dir {
|
||||
debug!("Enabling FTPS with multi-certificate support from directory: {}", cert_dir);
|
||||
debug!(
|
||||
event = EVENT_FTPS_TLS_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_SERVER,
|
||||
state = "enabled",
|
||||
cert_dir = %cert_dir,
|
||||
"ftps tls state changed"
|
||||
);
|
||||
|
||||
let resolver = ReloadableServerCertResolver::load_from_directory(cert_dir)
|
||||
.map_err(|e| FtpsInitError::InvalidConfig(format!("Failed to create certificate resolver: {}", e)))?;
|
||||
@@ -143,7 +197,13 @@ where
|
||||
server_builder = server_builder.ftps_manual::<std::path::PathBuf>(Arc::new(server_config));
|
||||
|
||||
if self.config.ftps_required {
|
||||
info!("FTPS is explicitly required for all connections");
|
||||
info!(
|
||||
event = EVENT_FTPS_TLS_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_SERVER,
|
||||
state = "required",
|
||||
"ftps tls state changed"
|
||||
);
|
||||
server_builder = server_builder.ftps_required(FtpsRequired::All, FtpsRequired::All);
|
||||
}
|
||||
} else if self.config.ftps_required {
|
||||
@@ -152,7 +212,14 @@ where
|
||||
));
|
||||
}
|
||||
} else {
|
||||
info!("TLS disabled, running in plain FTP mode");
|
||||
info!(
|
||||
event = EVENT_FTPS_TLS_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_SERVER,
|
||||
state = "disabled",
|
||||
mode = "plain_ftp",
|
||||
"ftps tls state changed"
|
||||
);
|
||||
}
|
||||
|
||||
// Build the server instance
|
||||
@@ -162,7 +229,14 @@ where
|
||||
let bind_addr = self.config.bind_addr.to_string();
|
||||
let server_handle = tokio::spawn(async move {
|
||||
if let Err(e) = server.listen(bind_addr).await {
|
||||
error!("FTPS server runtime error: {}", e);
|
||||
error!(
|
||||
event = EVENT_FTPS_RUNTIME_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_SERVER,
|
||||
result = "runtime_error",
|
||||
error = %e,
|
||||
"ftps runtime state changed"
|
||||
);
|
||||
return Err(FtpsInitError::Server(e));
|
||||
}
|
||||
Ok(())
|
||||
@@ -174,21 +248,48 @@ where
|
||||
let _ = reload_shutdown_tx.send(true);
|
||||
match result {
|
||||
Ok(Ok(())) => {
|
||||
info!("FTPS server stopped normally");
|
||||
info!(
|
||||
event = EVENT_FTPS_SERVER_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_SERVER,
|
||||
state = "stopped",
|
||||
result = "ok",
|
||||
"ftps server state changed"
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
Ok(Err(e)) => {
|
||||
error!("FTPS server internal error: {}", e);
|
||||
error!(
|
||||
event = EVENT_FTPS_RUNTIME_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_SERVER,
|
||||
result = "internal_error",
|
||||
error = %e,
|
||||
"ftps runtime state changed"
|
||||
);
|
||||
Err(e)
|
||||
}
|
||||
Err(e) => {
|
||||
error!("FTPS server panic or task cancellation: {}", e);
|
||||
error!(
|
||||
event = EVENT_FTPS_RUNTIME_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_SERVER,
|
||||
result = "task_failed",
|
||||
error = %e,
|
||||
"ftps runtime state changed"
|
||||
);
|
||||
Err(FtpsInitError::Bind(std::io::Error::other(e.to_string())))
|
||||
}
|
||||
}
|
||||
}
|
||||
_ = shutdown_rx.recv() => {
|
||||
info!("FTPS server received shutdown signal");
|
||||
info!(
|
||||
event = EVENT_FTPS_SERVER_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_SERVER,
|
||||
state = "shutdown_requested",
|
||||
"ftps server state changed"
|
||||
);
|
||||
let _ = reload_shutdown_tx.send(true);
|
||||
// libunftp listen() is not easily cancellable gracefully without dropping the future.
|
||||
// The select! dropping server_handle will close the listener.
|
||||
@@ -218,20 +319,46 @@ impl UserDetailProvider for FtpsUserDetailProvider {
|
||||
|
||||
async fn provide_user_detail(&self, principal: &Principal) -> Result<Self::User, UserDetailError> {
|
||||
use rustfs_iam::get;
|
||||
let masked_username = MaskedAccessKey(&principal.username);
|
||||
|
||||
// Access IAM system
|
||||
let iam_sys = get().map_err(|e| {
|
||||
error!("IAM system unavailable during FTPS user detail fetch: {}", e);
|
||||
error!(
|
||||
event = EVENT_FTPS_AUTH_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_AUTH,
|
||||
result = "iam_unavailable",
|
||||
phase = "user_detail",
|
||||
error = %e,
|
||||
"ftps auth state changed"
|
||||
);
|
||||
UserDetailError::ImplPropagated("Internal authentication service unavailable".to_string(), Some(Box::new(e)))
|
||||
})?;
|
||||
|
||||
let (user_identity, _is_valid) = iam_sys.check_key(&principal.username).await.map_err(|e| {
|
||||
error!("IAM check_key failed for {}: {}", principal.username, e);
|
||||
error!(
|
||||
event = EVENT_FTPS_AUTH_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_AUTH,
|
||||
result = "check_key_failed",
|
||||
phase = "user_detail",
|
||||
username = %masked_username,
|
||||
error = %e,
|
||||
"ftps auth state changed"
|
||||
);
|
||||
UserDetailError::ImplPropagated("Authentication verification failed".to_string(), Some(Box::new(e)))
|
||||
})?;
|
||||
|
||||
let identity = user_identity.ok_or_else(|| {
|
||||
error!("User identity missing for {}", principal.username);
|
||||
error!(
|
||||
event = EVENT_FTPS_AUTH_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_AUTH,
|
||||
result = "identity_missing",
|
||||
phase = "user_detail",
|
||||
username = %masked_username,
|
||||
"ftps auth state changed"
|
||||
);
|
||||
UserDetailError::UserNotFound {
|
||||
username: principal.username.clone(),
|
||||
}
|
||||
@@ -268,10 +395,19 @@ impl Authenticator for FtpsAuthenticator {
|
||||
async fn authenticate(&self, username: &str, creds: &Credentials) -> Result<Principal, AuthenticationError> {
|
||||
use rustfs_credentials::Credentials as S3Credentials;
|
||||
use rustfs_iam::get;
|
||||
let masked_username = MaskedAccessKey(username);
|
||||
|
||||
// Access IAM system
|
||||
let iam_sys = get().map_err(|e| {
|
||||
error!("IAM system unavailable during FTPS auth: {}", e);
|
||||
error!(
|
||||
event = EVENT_FTPS_AUTH_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_AUTH,
|
||||
result = "iam_unavailable",
|
||||
phase = "authenticate",
|
||||
error = %e,
|
||||
"ftps auth state changed"
|
||||
);
|
||||
AuthenticationError::ImplPropagated("Internal authentication service unavailable".to_string(), Some(Box::new(e)))
|
||||
})?;
|
||||
|
||||
@@ -289,26 +425,67 @@ impl Authenticator for FtpsAuthenticator {
|
||||
};
|
||||
|
||||
let (user_identity, is_valid) = iam_sys.check_key(&s3_creds.access_key).await.map_err(|e| {
|
||||
error!("IAM check_key failed for {}: {}", username, e);
|
||||
error!(
|
||||
event = EVENT_FTPS_AUTH_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_AUTH,
|
||||
result = "check_key_failed",
|
||||
phase = "authenticate",
|
||||
username = %masked_username,
|
||||
error = %e,
|
||||
"ftps auth state changed"
|
||||
);
|
||||
AuthenticationError::ImplPropagated("Authentication verification failed".to_string(), Some(Box::new(e)))
|
||||
})?;
|
||||
|
||||
if !is_valid {
|
||||
warn!("FTPS login failed: Invalid access key '{}'", username);
|
||||
warn!(
|
||||
event = EVENT_FTPS_AUTH_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_AUTH,
|
||||
result = "invalid_access_key",
|
||||
phase = "authenticate",
|
||||
username = %masked_username,
|
||||
"ftps auth state changed"
|
||||
);
|
||||
return Err(AuthenticationError::BadUser);
|
||||
}
|
||||
|
||||
let identity = user_identity.ok_or_else(|| {
|
||||
error!("User identity missing despite valid key for {}", username);
|
||||
error!(
|
||||
event = EVENT_FTPS_AUTH_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_AUTH,
|
||||
result = "identity_missing",
|
||||
phase = "authenticate",
|
||||
username = %masked_username,
|
||||
"ftps auth state changed"
|
||||
);
|
||||
AuthenticationError::BadUser
|
||||
})?;
|
||||
|
||||
if !identity.credentials.secret_key.eq(&s3_creds.secret_key) {
|
||||
warn!("FTPS login failed: Invalid secret key for '{}'", username);
|
||||
warn!(
|
||||
event = EVENT_FTPS_AUTH_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_AUTH,
|
||||
result = "invalid_secret_key",
|
||||
phase = "authenticate",
|
||||
username = %masked_username,
|
||||
"ftps auth state changed"
|
||||
);
|
||||
return Err(AuthenticationError::BadPassword);
|
||||
}
|
||||
|
||||
info!("FTPS user '{}' authenticated successfully", username);
|
||||
debug!(
|
||||
event = EVENT_FTPS_AUTH_STATE,
|
||||
component = LOG_COMPONENT_PROTOCOLS,
|
||||
subsystem = LOG_SUBSYSTEM_FTPS_AUTH,
|
||||
result = "authenticated",
|
||||
phase = "authenticate",
|
||||
username = %masked_username,
|
||||
"ftps auth state changed"
|
||||
);
|
||||
Ok(Principal {
|
||||
username: username.to_string(),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user