Support busybox user/group modification, more informational errors (#319)

This commit is contained in:
Cole Helbling 2023-03-10 14:00:20 -08:00 committed by GitHub
parent 51056b854a
commit 32dca2e846
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 191 additions and 97 deletions

18
Cargo.lock generated
View file

@ -360,6 +360,12 @@ version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30" checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30"
[[package]]
name = "either"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.32" version = "0.8.32"
@ -980,6 +986,7 @@ dependencies = [
"typetag", "typetag",
"url", "url",
"uuid", "uuid",
"which",
"xz2", "xz2",
] ]
@ -2068,6 +2075,17 @@ dependencies = [
"webpki", "webpki",
] ]
[[package]]
name = "which"
version = "4.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2441c784c52b289a054b7201fc93253e288f094e2f4be9058343127c4226a269"
dependencies = [
"either",
"libc",
"once_cell",
]
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.3.9" version = "0.3.9"

View file

@ -58,6 +58,7 @@ os-release = { version = "0.1.0", default-features = false, optional = true }
is_ci = { version = "1.1.1", default-features = false, optional = true } is_ci = { version = "1.1.1", default-features = false, optional = true }
strum = { version = "0.24.1", features = ["derive"] } strum = { version = "0.24.1", features = ["derive"] }
nix-config-parser = { version = "0.1.2", features = ["serde"] } nix-config-parser = { version = "0.1.2", features = ["serde"] }
which = "4.4.0"
[dev-dependencies] [dev-dependencies]
eyre = { version = "0.6.8", default-features = false, features = [ "track-caller" ] } eyre = { version = "0.6.8", default-features = false, features = [ "track-caller" ] }

View file

@ -202,14 +202,26 @@ impl Action for AddUserToGroup {
.await?; .await?;
}, },
_ => { _ => {
execute_command( if which::which("gpasswd").is_ok() {
Command::new("gpasswd") execute_command(
.process_group(0) Command::new("gpasswd")
.args(["-a"]) .process_group(0)
.args([&name.to_string(), &groupname.to_string()]) .args(["-a"])
.stdin(std::process::Stdio::null()), .args([name, groupname])
) .stdin(std::process::Stdio::null()),
.await?; )
.await?;
} else if which::which("addgroup").is_ok() {
execute_command(
Command::new("addgroup")
.process_group(0)
.args([name, groupname])
.stdin(std::process::Stdio::null()),
)
.await?;
} else {
return Err(ActionError::MissingAddUserToGroupCommand);
}
}, },
} }
@ -255,14 +267,26 @@ impl Action for AddUserToGroup {
.await?; .await?;
}, },
_ => { _ => {
execute_command( if which::which("gpasswd").is_ok() {
Command::new("gpasswd") execute_command(
.process_group(0) Command::new("gpasswd")
.args(["-d"]) .process_group(0)
.args([&name.to_string(), &groupname.to_string()]) .args(["-d"])
.stdin(std::process::Stdio::null()), .args([&name.to_string(), &groupname.to_string()])
) .stdin(std::process::Stdio::null()),
.await?; )
.await?;
} else if which::which("delgroup").is_ok() {
execute_command(
Command::new("delgroup")
.process_group(0)
.args([name, groupname])
.stdin(std::process::Stdio::null()),
)
.await?;
} else {
return Err(ActionError::MissingRemoveUserFromGroupCommand);
}
}, },
}; };

View file

@ -92,20 +92,32 @@ impl Action for CreateGroup {
"Nix build group for nix-daemon", "Nix build group for nix-daemon",
"-i", "-i",
&format!("{gid}"), &format!("{gid}"),
name.as_str(), name,
]) ])
.stdin(std::process::Stdio::null()), .stdin(std::process::Stdio::null()),
) )
.await?; .await?;
}, },
_ => { _ => {
execute_command( if which::which("groupadd").is_ok() {
Command::new("groupadd") execute_command(
.process_group(0) Command::new("groupadd")
.args(["-g", &gid.to_string(), "--system", &name]) .process_group(0)
.stdin(std::process::Stdio::null()), .args(["-g", &gid.to_string(), "--system", name])
) .stdin(std::process::Stdio::null()),
.await?; )
.await?;
} else if which::which("addgroup").is_ok() {
execute_command(
Command::new("addgroup")
.process_group(0)
.args(["-g", &gid.to_string(), "--system", name])
.stdin(std::process::Stdio::null()),
)
.await?;
} else {
return Err(ActionError::MissingGroupCreationCommand);
}
}, },
}; };
@ -143,13 +155,25 @@ impl Action for CreateGroup {
if !output.status.success() {} if !output.status.success() {}
}, },
_ => { _ => {
execute_command( if which::which("groupdel").is_ok() {
Command::new("groupdel") execute_command(
.process_group(0) Command::new("groupdel")
.arg(&name) .process_group(0)
.stdin(std::process::Stdio::null()), .arg(name)
) .stdin(std::process::Stdio::null()),
.await?; )
.await?;
} else if which::which("delgroup").is_ok() {
execute_command(
Command::new("delgroup")
.process_group(0)
.arg(name)
.stdin(std::process::Stdio::null()),
)
.await?;
} else {
return Err(ActionError::MissingGroupDeletionCommand);
}
}, },
}; };

View file

@ -98,7 +98,7 @@ impl Action for CreateUser {
let Self { let Self {
name, name,
uid, uid,
groupname: _, groupname,
gid, gid,
} = self; } = self;
@ -178,31 +178,57 @@ impl Action for CreateUser {
.await?; .await?;
}, },
_ => { _ => {
execute_command( if which::which("useradd").is_ok() {
Command::new("useradd") execute_command(
.process_group(0) Command::new("useradd")
.args([ .process_group(0)
"--home-dir", .args([
"/var/empty", "--home-dir",
"--comment", "/var/empty",
&format!("\"Nix build user\""), "--comment",
"--gid", "Nix build user",
&gid.to_string(), "--gid",
"--groups", &gid.to_string(),
&gid.to_string(), "--groups",
"--no-user-group", &gid.to_string(),
"--system", "--no-user-group",
"--shell", "--system",
"/sbin/nologin", "--shell",
"--uid", "/sbin/nologin",
&uid.to_string(), "--uid",
"--password", &uid.to_string(),
"\"!\"", "--password",
&name.to_string(), "!",
]) name,
.stdin(std::process::Stdio::null()), ])
) .stdin(std::process::Stdio::null()),
.await?; )
.await?;
} else if which::which("adduser").is_ok() {
execute_command(
Command::new("adduser")
.process_group(0)
.args([
"--home",
"/var/empty",
"--gecos",
"Nix build user",
"--ingroup",
groupname,
"--system",
"--shell",
"/sbin/nologin",
"--uid",
&uid.to_string(),
"--disabled-password",
name,
])
.stdin(std::process::Stdio::null()),
)
.await?;
} else {
return Err(ActionError::MissingUserCreationCommand);
}
}, },
} }
@ -266,13 +292,25 @@ impl Action for CreateUser {
} }
}, },
_ => { _ => {
execute_command( if which::which("userdel").is_ok() {
Command::new("userdel") execute_command(
.process_group(0) Command::new("userdel")
.args([&name.to_string()]) .process_group(0)
.stdin(std::process::Stdio::null()), .arg(name)
) .stdin(std::process::Stdio::null()),
.await?; )
.await?;
} else if which::which("deluser").is_ok() {
execute_command(
Command::new("deluser")
.process_group(0)
.arg(name)
.stdin(std::process::Stdio::null()),
)
.await?;
} else {
return Err(ActionError::MissingUserDeletionCommand);
}
}, },
}; };

View file

@ -5,8 +5,7 @@ use crate::{
}, },
settings::CommonSettings, settings::CommonSettings,
}; };
use tokio::task::JoinSet; use tracing::{span, Span};
use tracing::{span, Instrument, Span};
#[derive(Debug, serde::Deserialize, serde::Serialize, Clone)] #[derive(Debug, serde::Deserialize, serde::Serialize, Clone)]
pub struct CreateUsersAndGroups { pub struct CreateUsersAndGroups {
@ -266,37 +265,11 @@ impl Action for CreateUsersAndGroups {
nix_build_user_prefix: _, nix_build_user_prefix: _,
nix_build_user_id_base: _, nix_build_user_id_base: _,
} = self; } = self;
let mut set = JoinSet::new(); for create_user in create_users.iter_mut() {
create_user
let mut errors = Vec::default(); .try_revert()
.await
for (idx, create_user) in create_users.iter().enumerate() { .map_err(|e| ActionError::Child(create_user.action_tag(), Box::new(e)))?;
let span = tracing::Span::current().clone();
let mut create_user_clone = create_user.clone();
let _abort_handle = set.spawn(async move {
create_user_clone
.try_revert()
.instrument(span)
.await
.map_err(|e| ActionError::Child(create_user_clone.action_tag(), Box::new(e)))?;
Result::<_, ActionError>::Ok((idx, create_user_clone))
});
}
while let Some(result) = set.join_next().await {
match result {
Ok(Ok((idx, success))) => create_users[idx] = success,
Ok(Err(e)) => errors.push(Box::new(e)),
Err(e) => return Err(ActionError::Join(e))?,
};
}
if !errors.is_empty() {
if errors.len() == 1 {
return Err(*errors.into_iter().next().unwrap());
} else {
return Err(ActionError::Children(errors));
}
} }
// We don't actually need to do this, when a user is deleted they are removed from groups // We don't actually need to do this, when a user is deleted they are removed from groups

View file

@ -427,6 +427,22 @@ pub enum ActionError {
Plist(#[from] plist::Error), Plist(#[from] plist::Error),
#[error("Unexpected binary tarball contents found, the build result from `https://releases.nixos.org/?prefix=nix/` or `nix build nix#hydraJobs.binaryTarball.$SYSTEM` is expected")] #[error("Unexpected binary tarball contents found, the build result from `https://releases.nixos.org/?prefix=nix/` or `nix build nix#hydraJobs.binaryTarball.$SYSTEM` is expected")]
MalformedBinaryTarball, MalformedBinaryTarball,
#[error(
"Could not find a supported command to create users in PATH; please install `useradd` or `adduser`"
)]
MissingUserCreationCommand,
#[error("Could not find a supported command to create groups in PATH; please install `groupadd` or `addgroup`")]
MissingGroupCreationCommand,
#[error("Could not find a supported command to add users to groups in PATH; please install `gpasswd` or `addgroup`")]
MissingAddUserToGroupCommand,
#[error(
"Could not find a supported command to delete users in PATH; please install `userdel` or `deluser`"
)]
MissingUserDeletionCommand,
#[error("Could not find a supported command to delete groups in PATH; please install `groupdel` or `delgroup`")]
MissingGroupDeletionCommand,
#[error("Could not find a supported command to remove users from groups in PATH; please install `gpasswd` or `deluser`")]
MissingRemoveUserFromGroupCommand,
} }
impl ActionError { impl ActionError {