Enter user namespace in the incus case

This commit is contained in:
Johannes Leupolz 2026-04-16 20:29:48 +00:00
parent 6eb5a2985a
commit 152ffd9711
6 changed files with 88 additions and 54 deletions

View file

@ -83,8 +83,9 @@ impl InjectionStrategy for GenericPlacementInContainer {
minor: minor,
};
let child_pid = process_tools::start_action(mknod_device_action, &requesting_process)
.expect("subprocess should work");
let child_pid =
process_tools::start_action(mknod_device_action, &requesting_process, false)
.expect("subprocess should work");
let _exit_info = process_tools::await_process(Pid::Pid(child_pid))
.await
@ -106,8 +107,9 @@ impl InjectionStrategy for GenericPlacementInContainer {
minor: minor,
};
let child_pid_1 = process_tools::start_action(remove_device_action, &requesting_process)
.expect("subprocess should work");
let child_pid_1 =
process_tools::start_action(remove_device_action, &requesting_process, false)
.expect("subprocess should work");
let _exit_info = process_tools::await_process(Pid::Pid(child_pid_1)).await;
Ok(())
@ -126,8 +128,9 @@ impl InjectionStrategy for GenericPlacementInContainer {
minor: minor,
};
let child_pid = process_tools::start_action(write_udev_runtime_data, &requesting_process)
.expect("subprocess should work");
let child_pid =
process_tools::start_action(write_udev_runtime_data, &requesting_process, false)
.expect("subprocess should work");
let _exit_info = process_tools::await_process(Pid::Pid(child_pid))
.await
@ -148,7 +151,7 @@ impl InjectionStrategy for GenericPlacementInContainer {
};
let child_pid_2 =
process_tools::start_action(write_udev_runtime_data_action, &requesting_process)
process_tools::start_action(write_udev_runtime_data_action, &requesting_process, false)
.expect("subprocess should work");
let _exit_info = process_tools::await_process(Pid::Pid(child_pid_2)).await;
Ok(())
@ -164,8 +167,9 @@ impl InjectionStrategy for GenericPlacementInContainer {
netlink_message: netlink_message,
};
let child_pid = process_tools::start_action(emit_netlink_message, requesting_process)
.expect("subprocess should work");
let child_pid =
process_tools::start_action(emit_netlink_message, requesting_process, false)
.expect("subprocess should work");
let _exit_info = process_tools::await_process(Pid::Pid(child_pid)).await;
Ok(())
@ -235,7 +239,6 @@ impl InjectionStrategy for GenericPlacementOnHost {
Ok(())
}
/// Emit netlink message.
async fn emit_netlink_message(
&self,
@ -382,14 +385,21 @@ impl InjectionStrategy for Incus {
.await
}
/// Emit netlink message.
/// Emit netlink message.
async fn emit_netlink_message(
&self,
requesting_process: &RequestingProcess,
netlink_message: HashMap<String, String>,
) -> anyhow::Result<()> {
PLACEMENT_IN_CONTAINER
.emit_netlink_message(requesting_process, netlink_message)
.await
let emit_netlink_message = Action::EmitNetlinkMessage {
netlink_message: netlink_message,
};
let child_pid = process_tools::start_action(emit_netlink_message, requesting_process, true)
.expect("subprocess should work");
let _exit_info = process_tools::await_process(Pid::Pid(child_pid)).await;
Ok(())
}
}

View file

@ -33,8 +33,6 @@ pub unsafe extern "C" fn vuinput_poll(
get_vuinput_state(&VuFileHandle::from_fuse_file_info(fi.as_ref().unwrap())).unwrap();
let mut vuinput_state = vuinput_state_mutex.lock().unwrap();
debug!("poll");
match vuinput_state.poll.pollphase {
PollPhase::Empty => {
if ph != std::ptr::null_mut() {

View file

@ -158,7 +158,10 @@ impl EmitUdevEventJob {
.await
.unwrap();
injector.emit_netlink_message(&self.requesting_process, netlink_data).await.unwrap();
injector
.emit_netlink_message(&self.requesting_process, netlink_data)
.await
.unwrap();
self.set_state(&State::Finished);
}

View file

@ -142,16 +142,10 @@ impl RemoveDeviceJob {
.await
.unwrap();
// this is always in the container
let emit_netlink_message = Action::EmitNetlinkMessage {
netlink_message: netlink_data.clone(),
};
let child_pid_netlink =
process_tools::start_action(emit_netlink_message, &self.requesting_process)
.expect("subprocess should work");
let _exit_info = await_process(Pid::Pid(child_pid_netlink)).await;
injector
.emit_netlink_message(&self.requesting_process, netlink_data)
.await
.unwrap();
self.set_state(&State::Finished);
}

View file

@ -82,14 +82,22 @@ struct Args {
#[arg(long = "action-base64", value_name = "BASE64")]
pub action_base64: Option<String>,
/// Process id that is used as the namespace source (e.g. 1234 is used to read the namespaces from /proc/1234/ns).
/// Process id that is used as the namespace source (e.g. 1234 is used to read the namespaces from /proc/1234/ns). Enters net and
/// mnt namespaces by default.
#[arg(
long = "target-pid",
value_name = "PID",
help = "Process id that is used as the namespace source (e.g. 1234 is used to read the namespaces from /proc/1234/ns)."
help = "Process id that is used as the namespace source (e.g. 1234 is used to read the namespaces from /proc/1234/ns). Enters net and mnt namespaces by default."
)]
pub target_pid: Option<String>,
/// Enter also the user namespace. Used together with --target-pid.
#[arg(
long = "enter-user-namespace",
help = "Enter also the user namespace. Used together with --target-pid."
)]
pub enter_user_namespace: bool,
#[arg(
long = "vt-guard",
help = "Prevent all keyboard input from reaching the VT by setting K_OFF on /dev/tty0.",
@ -229,8 +237,12 @@ fn main() -> std::io::Result<()> {
if action.is_some() {
if let Some(target_pid) = args.target_pid {
process_tools::run_in_net_and_mnt_namespace(target_pid.as_str(), &args.device_owner)
.unwrap();
process_tools::run_in_net_and_mnt_namespace(
target_pid.as_str(),
&args.device_owner,
args.enter_user_namespace,
)
.unwrap();
}
let error_code = actions::handle_action::handle_cli_action(action.unwrap());
std::process::exit(error_code);
@ -245,7 +257,7 @@ fn main() -> std::io::Result<()> {
vt_tools::check_vt_status();
let container_runtime = args.resolve_runtime();
let scope= args.get_scope();
let scope = args.get_scope();
global_config::initialize_global_config(
&args.device_policy,

View file

@ -282,46 +282,57 @@ fn print_debug_string(action: &str, ns: &RequestingProcess) {
/// Runs a function inside the given network and mount namespaces.
/// Returns the child PID so the caller can `waitpid` on it.
pub fn start_action(action: Action, ns: &RequestingProcess) -> anyhow::Result<u32> {
pub fn start_action(
action: Action,
ns: &RequestingProcess,
enter_user_ns: bool,
) -> anyhow::Result<u32> {
let action_json = serde_json::to_string(&action).unwrap();
print_debug_string(&action_json, &ns);
let device_owner = get_device_owner().to_string_rep();
let child = unsafe {
Command::new("/proc/self/exe")
.args([
"--action",
action_json.as_str(),
"--target-pid",
ns.pid_requestor_root.to_string_rep().as_str(),
"--device-owner",
device_owner.as_str(),
])
.pre_exec(|| {
// Last resort, if the parent just is killed.
libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL);
Ok(())
})
.spawn()
.expect("failed to start vuinputd")
let mut cmd = Command::new("/proc/self/exe");
cmd.args([
"--action",
action_json.as_str(),
"--target-pid",
ns.pid_requestor_root.to_string_rep().as_str(),
"--device-owner",
device_owner.as_str(),
]);
if enter_user_ns {
cmd.arg("--enter-user-namespace");
}
cmd.pre_exec(|| {
// Last resort, if the parent just is killed.
libc::prctl(libc::PR_SET_PDEATHSIG, libc::SIGKILL);
Ok(())
})
.spawn()
.expect("failed to start vuinputd")
};
Result::Ok(child.id())
}
pub fn run_in_net_and_mnt_namespace(target_pid: &str, device_owner: &DeviceOwner) -> anyhow::Result<()> {
pub fn run_in_net_and_mnt_namespace(
target_pid: &str,
device_owner: &DeviceOwner,
enter_user_ns: bool,
) -> anyhow::Result<()> {
debug!(
"Entering namespaces of process {}. We assume this is the root process of the container.",
target_pid
);
let fs_uid_gid = if *device_owner == DeviceOwner::ContainerDevFolder {
let pid:u32 = target_pid.trim().parse()?;
let pid: u32 = target_pid.trim().parse()?;
let pid = Pid::Pid(pid);
let fs_uid=ns_fscreds::get_uid_in_container(pid, 0)?;
let fs_gid=ns_fscreds::get_gid_in_container(pid, 0)?;
Some((fs_uid,fs_gid))
let fs_uid = ns_fscreds::get_uid_in_container(pid, 0)?;
let fs_gid = ns_fscreds::get_gid_in_container(pid, 0)?;
Some((fs_uid, fs_gid))
} else {
None
};
@ -331,16 +342,22 @@ pub fn run_in_net_and_mnt_namespace(target_pid: &str, device_owner: &DeviceOwner
if !fs::exists(path).unwrap() {
return Err(anyhow!("the root process of the container whose namespaces we want to enter does not exist anymore"));
}
let user = File::open(nspath.to_string() + "/user")?;
let net = File::open(nspath.to_string() + "/net")?;
let mnt = File::open(nspath.to_string() + "/mnt")?;
unsafe {
// enter namespaces
if enter_user_ns {
libc::setns(user.as_raw_fd(), libc::CLONE_NEWUSER);
libc::setresgid(0, 0, 0);
libc::setresuid(0, 0, 0);
}
libc::setns(net.as_raw_fd(), libc::CLONE_NEWNET);
libc::setns(mnt.as_raw_fd(), libc::CLONE_NEWNS);
};
if let Some((fs_uid,fs_gid)) = fs_uid_gid {
if let Some((fs_uid, fs_gid)) = fs_uid_gid {
ns_fscreds::acquire_uid_and_gid(fs_uid, fs_gid)?;
}