Privileges fix

Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
Alexey 2026-04-06 11:10:41 +03:00
parent 4d87a790cc
commit fa4e2000a8
No known key found for this signature in database
2 changed files with 48 additions and 10 deletions

View File

@ -339,31 +339,35 @@ fn is_process_running(pid: i32) -> bool {
/// Drops privileges to the specified user and group. /// Drops privileges to the specified user and group.
/// ///
/// This should be called after binding privileged ports but before /// This should be called after binding privileged ports but before entering
/// entering the main event loop. /// the main event loop.
pub fn drop_privileges(user: Option<&str>, group: Option<&str>) -> Result<(), DaemonError> { pub fn drop_privileges(
// Look up group first (need to do this while still root) user: Option<&str>,
group: Option<&str>,
pid_file: Option<&PidFile>,
) -> Result<(), DaemonError> {
let target_gid = if let Some(group_name) = group { let target_gid = if let Some(group_name) = group {
Some(lookup_group(group_name)?) Some(lookup_group(group_name)?)
} else if let Some(user_name) = user { } else if let Some(user_name) = user {
// If no group specified but user is, use user's primary group
Some(lookup_user_primary_gid(user_name)?) Some(lookup_user_primary_gid(user_name)?)
} else { } else {
None None
}; };
// Look up user
let target_uid = if let Some(user_name) = user { let target_uid = if let Some(user_name) = user {
Some(lookup_user(user_name)?) Some(lookup_user(user_name)?)
} else { } else {
None None
}; };
// Drop privileges: set GID first, then UID if (target_uid.is_some() || target_gid.is_some())
// (Setting UID first would prevent us from setting GID) && let Some(file) = pid_file.and_then(|pid| pid.file.as_ref())
{
unistd::fchown(file, target_uid, target_gid).map_err(DaemonError::PrivilegeDrop)?;
}
if let Some(gid) = target_gid { if let Some(gid) = target_gid {
unistd::setgid(gid).map_err(DaemonError::PrivilegeDrop)?; unistd::setgid(gid).map_err(DaemonError::PrivilegeDrop)?;
// Also set supplementary groups to just this one
unistd::setgroups(&[gid]).map_err(DaemonError::PrivilegeDrop)?; unistd::setgroups(&[gid]).map_err(DaemonError::PrivilegeDrop)?;
info!(gid = gid.as_raw(), "Dropped group privileges"); info!(gid = gid.as_raw(), "Dropped group privileges");
} }
@ -371,6 +375,36 @@ pub fn drop_privileges(user: Option<&str>, group: Option<&str>) -> Result<(), Da
if let Some(uid) = target_uid { if let Some(uid) = target_uid {
unistd::setuid(uid).map_err(DaemonError::PrivilegeDrop)?; unistd::setuid(uid).map_err(DaemonError::PrivilegeDrop)?;
info!(uid = uid.as_raw(), "Dropped user privileges"); info!(uid = uid.as_raw(), "Dropped user privileges");
if uid.as_raw() != 0 && let Some(pid) = pid_file {
let parent = pid.path.parent().unwrap_or(Path::new("."));
let probe_path = parent.join(format!(
".telemt_pid_probe_{}_{}",
std::process::id(),
getpid().as_raw()
));
OpenOptions::new()
.write(true)
.create_new(true)
.mode(0o600)
.open(&probe_path)
.map_err(|e| {
DaemonError::PidFile(format!(
"cannot create probe in PID directory {} as uid {} (pid cleanup will fail): {}",
parent.display(),
uid.as_raw(),
e
))
})?;
fs::remove_file(&probe_path).map_err(|e| {
DaemonError::PidFile(format!(
"cannot remove probe in PID directory {} as uid {} (pid cleanup will fail): {}",
parent.display(),
uid.as_raw(),
e
))
})?;
}
} }
Ok(()) Ok(())

View File

@ -763,7 +763,11 @@ async fn run_inner(
// Drop privileges after binding sockets (which may require root for port < 1024) // Drop privileges after binding sockets (which may require root for port < 1024)
if daemon_opts.user.is_some() || daemon_opts.group.is_some() { if daemon_opts.user.is_some() || daemon_opts.group.is_some() {
if let Err(e) = drop_privileges(daemon_opts.user.as_deref(), daemon_opts.group.as_deref()) { if let Err(e) = drop_privileges(
daemon_opts.user.as_deref(),
daemon_opts.group.as_deref(),
_pid_file.as_ref(),
) {
error!(error = %e, "Failed to drop privileges"); error!(error = %e, "Failed to drop privileges");
std::process::exit(1); std::process::exit(1);
} }