mirror of
https://github.com/telemt/telemt.git
synced 2026-11-04 06:42:00 +03:00
Runtime Paths Checks as opt-in #935
Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
@@ -15,20 +15,32 @@ use super::helpers::{
|
||||
use super::runtime_tasks;
|
||||
use super::validate_synlimit_privilege_drop;
|
||||
|
||||
/// Process-level configuration and logging resources retained across runtime generations.
|
||||
pub(super) struct BootstrapState {
|
||||
/// Monotonic process startup time.
|
||||
pub(super) process_started_at: Instant,
|
||||
/// Process startup time as Unix epoch seconds.
|
||||
pub(super) process_started_at_epoch_secs: u64,
|
||||
/// Startup component progress shared with the control plane.
|
||||
pub(super) startup_tracker: Arc<StartupTracker>,
|
||||
/// Validated initial runtime configuration.
|
||||
pub(super) config: ProxyConfig,
|
||||
/// Resolved source path used by reload operations.
|
||||
pub(super) config_path: PathBuf,
|
||||
/// Whether the environment owns the log filter policy.
|
||||
pub(super) has_rust_log: bool,
|
||||
/// Initial verbosity after CLI overrides.
|
||||
pub(super) effective_log_level: LogLevel,
|
||||
/// Process-owned dynamic tracing filter.
|
||||
pub(super) runtime_log_filter: runtime_tasks::RuntimeLogFilter,
|
||||
/// Keeps the file logging worker alive until process shutdown.
|
||||
pub(super) logging_guard: Option<crate::logging::LoggingGuard>,
|
||||
}
|
||||
|
||||
/// Loads configuration and initializes process logging with the startup parent-path policy.
|
||||
pub(super) async fn bootstrap(
|
||||
privilege_drop_requested: bool,
|
||||
strict_runtime_paths: bool,
|
||||
) -> std::result::Result<BootstrapState, Box<dyn std::error::Error>> {
|
||||
let process_started_at = Instant::now();
|
||||
let process_started_at_epoch_secs = SystemTime::now()
|
||||
@@ -268,6 +280,7 @@ pub(super) async fn bootstrap(
|
||||
let logging_opts = crate::logging::LoggingOptions {
|
||||
destination: log_destination,
|
||||
disable_colors: true,
|
||||
strict_runtime_paths,
|
||||
};
|
||||
let (_, guard) = crate::logging::init_logging(&logging_opts, &initial_filter_spec);
|
||||
logging_guard = Some(guard);
|
||||
@@ -276,6 +289,7 @@ pub(super) async fn bootstrap(
|
||||
let logging_opts = crate::logging::LoggingOptions {
|
||||
destination: log_destination,
|
||||
disable_colors: true,
|
||||
strict_runtime_paths,
|
||||
};
|
||||
let (_, guard) = crate::logging::init_logging(&logging_opts, &initial_filter_spec);
|
||||
logging_guard = Some(guard);
|
||||
|
||||
@@ -30,6 +30,7 @@ pub(crate) fn print_maestro_line(message: impl AsRef<str>) {
|
||||
);
|
||||
}
|
||||
|
||||
/// Resolves the config source against startup cwd while retaining symlink components.
|
||||
pub(crate) fn resolve_runtime_config_path(
|
||||
config_path_cli: &str,
|
||||
startup_cwd: &Path,
|
||||
@@ -75,6 +76,7 @@ pub(crate) fn resolve_runtime_config_path(
|
||||
startup_cwd.join("config.toml")
|
||||
}
|
||||
|
||||
/// Selects the runtime directory from CLI, startup cwd, and explicit config location.
|
||||
pub(crate) fn resolve_runtime_base_dir(
|
||||
config_path: &Path,
|
||||
startup_cwd: &Path,
|
||||
@@ -120,14 +122,21 @@ fn normalize_runtime_dir(path: &Path, startup_cwd: &Path) -> PathBuf {
|
||||
|
||||
/// Parsed CLI arguments.
|
||||
pub(crate) struct CliArgs {
|
||||
/// Config source selected by positional arguments.
|
||||
pub config_path: String,
|
||||
/// Whether the config source was explicitly provided.
|
||||
pub config_path_explicit: bool,
|
||||
/// Runtime directory override from CLI.
|
||||
pub data_path: Option<PathBuf>,
|
||||
/// Whether CLI requests minimal logging output.
|
||||
pub silent: bool,
|
||||
/// Verbosity override from CLI.
|
||||
pub log_level: Option<String>,
|
||||
/// Logging destination, rotation, and retention overrides.
|
||||
pub log_cli_options: LogCliOptions,
|
||||
}
|
||||
|
||||
/// Parses runtime arguments after early daemon and control-command handling.
|
||||
pub(crate) fn parse_cli() -> CliArgs {
|
||||
let mut config_path = "config.toml".to_string();
|
||||
let mut config_path_explicit = false;
|
||||
@@ -222,6 +231,8 @@ pub(crate) fn parse_cli() -> CliArgs {
|
||||
}
|
||||
// Skip daemon-related flags (already parsed)
|
||||
"--daemon" | "-d" | "--foreground" | "-f" => {}
|
||||
#[cfg(unix)]
|
||||
"--strict-runtime-paths" => {}
|
||||
s if s.starts_with("--pid-file") => {
|
||||
if !s.contains('=') {
|
||||
// Skip the pid-file value consumed by daemon argument parsing.
|
||||
@@ -300,6 +311,15 @@ fn print_help() {
|
||||
eprintln!(" --daemon, -d Fork to background (daemonize)");
|
||||
eprintln!(" --foreground, -f Explicit foreground mode (for systemd)");
|
||||
eprintln!(" --pid-file <PATH> PID file path (default: /var/run/telemt.pid)");
|
||||
eprintln!(
|
||||
" --strict-runtime-paths Require trusted, symlink-free PID/log parents (default: off)"
|
||||
);
|
||||
eprintln!(
|
||||
" Applies to run/start/stop/reload/status; parent symlinks and"
|
||||
);
|
||||
eprintln!(
|
||||
" writable directories are allowed when this flag is absent"
|
||||
);
|
||||
eprintln!(" --run-as-user <USER> Drop privileges to this user after binding");
|
||||
eprintln!(" --run-as-group <GROUP> Drop privileges to this group after binding");
|
||||
eprintln!(" --working-dir <DIR> Working directory for daemon mode");
|
||||
|
||||
+18
-10
@@ -96,7 +96,10 @@ async fn run_inner(
|
||||
// Acquire PID file if daemonizing or if explicitly requested.
|
||||
// Keep it alive until shutdown for RAII cleanup.
|
||||
let _pid_file = if daemon_opts.daemonize || daemon_opts.pid_file.is_some() {
|
||||
let mut pf = PidFile::new(daemon_opts.pid_file_path());
|
||||
let mut pf = PidFile::new(
|
||||
daemon_opts.pid_file_path(),
|
||||
daemon_opts.strict_runtime_paths,
|
||||
);
|
||||
if let Err(e) = pf.acquire() {
|
||||
eprintln!("[telemt] {}", e);
|
||||
std::process::exit(1);
|
||||
@@ -109,20 +112,25 @@ async fn run_inner(
|
||||
let user = daemon_opts.user.clone();
|
||||
let group = daemon_opts.group.clone();
|
||||
|
||||
orchestrator::run_telemt_core(user.is_some() || group.is_some(), || {
|
||||
if (user.is_some() || group.is_some())
|
||||
&& let Err(e) = drop_privileges(user.as_deref(), group.as_deref(), _pid_file.as_ref())
|
||||
{
|
||||
error!(error = %e, "Failed to drop privileges");
|
||||
std::process::exit(1);
|
||||
}
|
||||
})
|
||||
orchestrator::run_telemt_core(
|
||||
user.is_some() || group.is_some(),
|
||||
daemon_opts.strict_runtime_paths,
|
||||
|| {
|
||||
if (user.is_some() || group.is_some())
|
||||
&& let Err(e) =
|
||||
drop_privileges(user.as_deref(), group.as_deref(), _pid_file.as_ref())
|
||||
{
|
||||
error!(error = %e, "Failed to drop privileges");
|
||||
std::process::exit(1);
|
||||
}
|
||||
},
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
#[cfg(not(unix))]
|
||||
async fn run_inner() -> std::result::Result<(), Box<dyn std::error::Error>> {
|
||||
orchestrator::run_telemt_core(false, || {}).await
|
||||
orchestrator::run_telemt_core(false, false, || {}).await
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -30,10 +30,11 @@ use super::{
|
||||
runtime_tasks, shutdown, tls_bootstrap,
|
||||
};
|
||||
|
||||
// Shared maestro startup and main loop. `drop_after_bind` runs on Unix after listeners are bound
|
||||
// and privileged firewall setup completes; it is a no-op on other platforms.
|
||||
/// Runs startup and the main loop with explicit runtime-path policy.
|
||||
/// `drop_after_bind` runs after listeners and privileged firewall setup are ready.
|
||||
pub(super) async fn run_telemt_core(
|
||||
privilege_drop_requested: bool,
|
||||
strict_runtime_paths: bool,
|
||||
drop_after_bind: impl FnOnce(),
|
||||
) -> std::result::Result<(), Box<dyn std::error::Error>> {
|
||||
let bootstrap::BootstrapState {
|
||||
@@ -46,7 +47,7 @@ pub(super) async fn run_telemt_core(
|
||||
effective_log_level,
|
||||
runtime_log_filter,
|
||||
logging_guard: _logging_guard,
|
||||
} = bootstrap::bootstrap(privilege_drop_requested).await?;
|
||||
} = bootstrap::bootstrap(privilege_drop_requested, strict_runtime_paths).await?;
|
||||
|
||||
if privilege_drop_requested && config.server.conntrack_control.inline_conntrack_control {
|
||||
warn!("Inline conntrack control is disabled when process privileges are dropped");
|
||||
|
||||
Reference in New Issue
Block a user