1
0
mirror of https://github.com/containers/youki synced 2024-11-26 06:08:07 +01:00

Rename the rootless struct and correspondigs to UserNamespaceConfig and according.

This commit is contained in:
Yashodhan Joshi 2023-08-12 19:28:25 +05:30
parent e5232ceb17
commit 4e26799e15
15 changed files with 142 additions and 118 deletions

14
MirgationGuide.md Normal file

@ -0,0 +1,14 @@
This contains information for migrating library versions.
## V0.1.0 -> v0.2.0
### libcontainer
- The `Rootless` struct has been re-named as `UserNamespaceConfig` , `RootlessIDMapper` has been re-named to `UserNamespaceIDMapper` , and correspondingly the `RootlessError` has been re-named to `UserNamespaceError` . This is due to the fact that the structure was to be used for containers when a new user namespace is to be created, and that is not strictly only for rootless uses. Accordingly, the fields of various structs has been updated to reflect this change :
- rootless (module name) -> user_ns
- Rootless::rootless_id_mapper -> UserNamespaceConfig::id_mapper
- LibcontainerError::Rootless -> LibcontainerError::UserNamespace
- ContainerBuilderImpl::rootless -> ContainerBuilderImpl::user_ns_config
- ContainerArgs::rootless -> ContainerArgs::user_ns_config
- Changes that will occur for properly running in rootless mode : TODO (@YJDoc2)

@ -47,7 +47,7 @@ pub struct YoukiConfig {
}
impl<'a> YoukiConfig {
pub fn from_spec(spec: &'a Spec, container_id: &str, rootless: bool) -> Result<Self> {
pub fn from_spec(spec: &'a Spec, container_id: &str, new_user_ns: bool) -> Result<Self> {
Ok(YoukiConfig {
hooks: spec.hooks().clone(),
cgroup_path: utils::get_cgroup_path(
@ -56,7 +56,7 @@ impl<'a> YoukiConfig {
.ok_or(ConfigError::MissingLinux)?
.cgroups_path(),
container_id,
rootless,
new_user_ns,
),
})
}

@ -8,8 +8,8 @@ use crate::{
args::{ContainerArgs, ContainerType},
intel_rdt::delete_resctrl_subdirectory,
},
rootless::Rootless,
syscall::syscall::SyscallType,
user_ns::UserNamespaceConfig,
utils,
workload::Executor,
};
@ -36,8 +36,8 @@ pub(super) struct ContainerBuilderImpl {
pub pid_file: Option<PathBuf>,
/// Socket to communicate the file descriptor of the ptty
pub console_socket: Option<RawFd>,
/// Options for rootless containers
pub rootless: Option<Rootless>,
/// Options for new user namespace
pub user_ns_config: Option<UserNamespaceConfig>,
/// Path to the Unix Domain Socket to communicate container start
pub notify_path: PathBuf,
/// Container state
@ -71,11 +71,11 @@ impl ContainerBuilderImpl {
let cgroups_path = utils::get_cgroup_path(
linux.cgroups_path(),
&self.container_id,
self.rootless.is_some(),
self.user_ns_config.is_some(),
);
let cgroup_config = libcgroups::common::CgroupConfig {
cgroup_path: cgroups_path,
systemd_cgroup: self.use_systemd || self.rootless.is_some(),
systemd_cgroup: self.use_systemd || self.user_ns_config.is_some(),
container_name: self.container_id.to_owned(),
};
let process = self
@ -144,7 +144,7 @@ impl ContainerBuilderImpl {
notify_listener,
preserve_fds: self.preserve_fds,
container: self.container.to_owned(),
rootless: self.rootless.to_owned(),
user_ns_config: self.user_ns_config.to_owned(),
cgroup_config,
detached: self.detached,
executor: self.executor.clone(),
@ -184,12 +184,12 @@ impl ContainerBuilderImpl {
let cgroups_path = utils::get_cgroup_path(
linux.cgroups_path(),
&self.container_id,
self.rootless.is_some(),
self.user_ns_config.is_some(),
);
let cmanager =
libcgroups::common::create_cgroup_manager(libcgroups::common::CgroupConfig {
cgroup_path: cgroups_path,
systemd_cgroup: self.use_systemd || self.rootless.is_some(),
systemd_cgroup: self.use_systemd || self.user_ns_config.is_some(),
container_name: self.container_id.to_string(),
})?;

@ -1,11 +1,11 @@
use nix::unistd;
use oci_spec::runtime::Spec;
use rootless::Rootless;
use std::{
fs,
path::{Path, PathBuf},
rc::Rc,
};
use user_ns::UserNamespaceConfig;
use crate::{
apparmor,
@ -13,7 +13,7 @@ use crate::{
error::{ErrInvalidSpec, LibcontainerError, MissingSpecError},
notify_socket::NOTIFY_FILE,
process::args::ContainerType,
rootless, tty,
tty, user_ns,
};
use super::{
@ -86,8 +86,8 @@ impl InitContainerBuilder {
None
};
let rootless = Rootless::new(&spec)?;
let config = YoukiConfig::from_spec(&spec, container.id(), rootless.is_some())?;
let user_ns_config = UserNamespaceConfig::new(&spec)?;
let config = YoukiConfig::from_spec(&spec, container.id(), user_ns_config.is_some())?;
config.save(&container_dir).map_err(|err| {
tracing::error!(?container_dir, "failed to save config: {}", err);
err
@ -102,7 +102,7 @@ impl InitContainerBuilder {
use_systemd: self.use_systemd,
spec: Rc::new(spec),
rootfs,
rootless,
user_ns_config,
notify_path,
container: Some(container.clone()),
preserve_fds: self.base.preserve_fds,

@ -23,7 +23,7 @@ use std::{
use crate::error::{ErrInvalidSpec, LibcontainerError, MissingSpecError};
use crate::process::args::ContainerType;
use crate::{capabilities::CapabilityExt, container::builder_impl::ContainerBuilderImpl};
use crate::{notify_socket::NotifySocket, rootless::Rootless, tty, utils};
use crate::{notify_socket::NotifySocket, tty, user_ns::UserNamespaceConfig, utils};
use super::{builder::ContainerBuilder, Container};
@ -119,7 +119,7 @@ impl TenantContainerBuilder {
let csocketfd = self.setup_tty_socket(&container_dir)?;
let use_systemd = self.should_use_systemd(&container);
let rootless = Rootless::new(&spec)?;
let user_ns_config = UserNamespaceConfig::new(&spec)?;
let (read_end, write_end) =
pipe2(OFlag::O_CLOEXEC).map_err(LibcontainerError::OtherSyscall)?;
@ -135,7 +135,7 @@ impl TenantContainerBuilder {
use_systemd,
spec: Rc::new(spec),
rootfs,
rootless,
user_ns_config,
notify_path: notify_path.clone(),
container: None,
preserve_fds: self.base.preserve_fds,

@ -35,7 +35,7 @@ pub enum LibcontainerError {
#[error(transparent)]
Tty(#[from] crate::tty::TTYError),
#[error(transparent)]
Rootless(#[from] crate::rootless::RootlessError),
UserNamespace(#[from] crate::user_ns::UserNamespaceError),
#[error(transparent)]
NotifyListener(#[from] crate::notify_socket::NotifyListenerError),
#[error(transparent)]

@ -9,13 +9,13 @@ pub mod namespaces;
pub mod notify_socket;
pub mod process;
pub mod rootfs;
pub mod rootless;
#[cfg(feature = "libseccomp")]
pub mod seccomp;
pub mod signal;
pub mod syscall;
pub mod test_utils;
pub mod tty;
pub mod user_ns;
pub mod utils;
pub mod workload;

@ -6,8 +6,8 @@ use std::rc::Rc;
use crate::container::Container;
use crate::notify_socket::NotifyListener;
use crate::rootless::Rootless;
use crate::syscall::syscall::SyscallType;
use crate::user_ns::UserNamespaceConfig;
use crate::workload::Executor;
#[derive(Debug, Copy, Clone)]
pub enum ContainerType {
@ -33,8 +33,8 @@ pub struct ContainerArgs {
pub preserve_fds: i32,
/// Container state
pub container: Option<Container>,
/// Options for rootless containers
pub rootless: Option<Rootless>,
/// Options for new namespace creation
pub user_ns_config: Option<UserNamespaceConfig>,
/// Cgroup Manager Config
pub cgroup_config: CgroupConfig,
/// If the container is to be run in detached mode

@ -4,8 +4,8 @@ use crate::namespaces::NamespaceError;
use crate::syscall::{Syscall, SyscallError};
use crate::{apparmor, notify_socket, rootfs, workload};
use crate::{
capabilities, hooks, namespaces::Namespaces, process::channel, rootfs::RootFS,
rootless::Rootless, tty, utils,
capabilities, hooks, namespaces::Namespaces, process::channel, rootfs::RootFS, tty,
user_ns::UserNamespaceConfig, utils,
};
use nix::mount::MsFlags;
use nix::sched::CloneFlags;
@ -492,7 +492,7 @@ pub fn container_init_process(
}
};
set_supplementary_gids(proc.user(), &args.rootless, syscall.as_ref()).map_err(|err| {
set_supplementary_gids(proc.user(), &args.user_ns_config, syscall.as_ref()).map_err(|err| {
tracing::error!(?err, "failed to set supplementary gids");
err
})?;
@ -720,7 +720,7 @@ pub fn container_init_process(
//
fn set_supplementary_gids(
user: &User,
rootless: &Option<Rootless>,
user_ns_config: &Option<UserNamespaceConfig>,
syscall: &dyn Syscall,
) -> Result<()> {
if let Some(additional_gids) = user.additional_gids() {
@ -742,7 +742,7 @@ fn set_supplementary_gids(
.map(|gid| Gid::from_raw(*gid))
.collect();
match rootless {
match user_ns_config {
Some(r) if r.privileged => {
syscall.set_groups(&gids).map_err(|err| {
tracing::error!(?err, ?gids, "failed to set privileged supplementary gids");
@ -757,7 +757,7 @@ fn set_supplementary_gids(
}
// this should have been detected during validation
_ => unreachable!(
"unprivileged users cannot set supplementary gids in rootless container"
"unprivileged users cannot set supplementary gids in containers with new user namespace"
),
}
}
@ -931,20 +931,20 @@ mod tests {
UserBuilder::default()
.additional_gids(vec![33, 34])
.build()?,
None::<Rootless>,
None::<UserNamespaceConfig>,
vec![vec![Gid::from_raw(33), Gid::from_raw(34)]],
),
// unreachable case
(
UserBuilder::default().build()?,
Some(Rootless::default()),
Some(UserNamespaceConfig::default()),
vec![],
),
(
UserBuilder::default()
.additional_gids(vec![37, 38])
.build()?,
Some(Rootless {
Some(UserNamespaceConfig {
privileged: true,
gid_mappings: None,
newgidmap: None,
@ -956,9 +956,9 @@ mod tests {
vec![vec![Gid::from_raw(37), Gid::from_raw(38)]],
),
];
for (user, rootless, want) in tests.into_iter() {
for (user, ns_config, want) in tests.into_iter() {
let syscall = create_syscall();
let result = set_supplementary_gids(&user, &rootless, syscall.as_ref());
let result = set_supplementary_gids(&user, &ns_config, syscall.as_ref());
match fs::read_to_string("/proc/self/setgroups")?.trim() {
"deny" => {
assert!(result.is_err());

@ -5,7 +5,7 @@ use crate::{
fork::{self, CloneCb},
intel_rdt::setup_intel_rdt,
},
rootless::Rootless,
user_ns::UserNamespaceConfig,
};
use nix::sys::wait::{waitpid, WaitStatus};
use nix::unistd::Pid;
@ -17,7 +17,7 @@ pub enum ProcessError {
#[error("failed to write deny to setgroups")]
SetGroupsDeny(#[source] std::io::Error),
#[error(transparent)]
Rootless(#[from] crate::rootless::RootlessError),
UserNamespace(#[from] crate::user_ns::UserNamespaceError),
#[error("container state is required")]
ContainerStateRequired,
#[error("failed to wait for intermediate process")]
@ -87,12 +87,12 @@ pub fn container_main_process(container_args: &ContainerArgs) -> Result<(Pid, bo
#[cfg(not(feature = "libseccomp"))]
let (init_sender, init_receiver) = init_chan;
// If creating a rootless container, the intermediate process will ask
// If creating a container with new user namespace, the intermediate process will ask
// the main process to set up uid and gid mapping, once the intermediate
// process enters into a new user namespace.
if let Some(rootless) = &container_args.rootless {
if let Some(config) = &container_args.user_ns_config {
main_receiver.wait_for_mapping_request()?;
setup_mapping(rootless, intermediate_pid)?;
setup_mapping(config, intermediate_pid)?;
inter_sender.mapping_written()?;
}
@ -197,20 +197,20 @@ pub fn container_main_process(container_args: &ContainerArgs) -> Result<(Pid, bo
Ok((init_pid, need_to_clean_up_intel_rdt_subdirectory))
}
fn setup_mapping(rootless: &Rootless, pid: Pid) -> Result<()> {
fn setup_mapping(config: &UserNamespaceConfig, pid: Pid) -> Result<()> {
tracing::debug!("write mapping for pid {:?}", pid);
if !rootless.privileged {
if !config.privileged {
// The main process is running as an unprivileged user and cannot write the mapping
// until "deny" has been written to setgroups. See CVE-2014-8989.
std::fs::write(format!("/proc/{pid}/setgroups"), "deny")
.map_err(ProcessError::SetGroupsDeny)?;
}
rootless.write_uid_mapping(pid).map_err(|err| {
config.write_uid_mapping(pid).map_err(|err| {
tracing::error!("failed to write uid mapping for pid {:?}: {}", pid, err);
err
})?;
rootless.write_gid_mapping(pid).map_err(|err| {
config.write_gid_mapping(pid).map_err(|err| {
tracing::error!("failed to write gid mapping for pid {:?}: {}", pid, err);
err
})?;
@ -221,7 +221,7 @@ fn setup_mapping(rootless: &Rootless, pid: Pid) -> Result<()> {
mod tests {
use super::*;
use crate::process::channel::{intermediate_channel, main_channel};
use crate::rootless::RootlessIDMapper;
use crate::user_ns::UserNamespaceIDMapper;
use anyhow::Result;
use nix::{
sched::{unshare, CloneFlags},
@ -241,11 +241,11 @@ mod tests {
.build()?;
let uid_mappings = vec![uid_mapping];
let tmp = tempfile::tempdir()?;
let id_mapper = RootlessIDMapper::new_test(tmp.path().to_path_buf());
let rootless = Rootless {
let id_mapper = UserNamespaceIDMapper::new_test(tmp.path().to_path_buf());
let ns_config = UserNamespaceConfig {
uid_mappings: Some(uid_mappings),
privileged: true,
rootless_id_mapper: id_mapper.clone(),
id_mapper: id_mapper.clone(),
..Default::default()
};
let (mut parent_sender, mut parent_receiver) = main_channel()?;
@ -260,7 +260,7 @@ mod tests {
// The path requires the pid we use, so we can only do do after
// obtaining the child pid here.
id_mapper.ensure_uid_path(&child)?;
setup_mapping(&rootless, child)?;
setup_mapping(&ns_config, child)?;
let line = fs::read_to_string(id_mapper.get_uid_path(&child))?;
let line_splited = line.split_whitespace();
for (act, expect) in line_splited.zip([
@ -296,10 +296,10 @@ mod tests {
.build()?;
let gid_mappings = vec![gid_mapping];
let tmp = tempfile::tempdir()?;
let id_mapper = RootlessIDMapper::new_test(tmp.path().to_path_buf());
let rootless = Rootless {
let id_mapper = UserNamespaceIDMapper::new_test(tmp.path().to_path_buf());
let ns_config = UserNamespaceConfig {
gid_mappings: Some(gid_mappings),
rootless_id_mapper: id_mapper.clone(),
id_mapper: id_mapper.clone(),
..Default::default()
};
let (mut parent_sender, mut parent_receiver) = main_channel()?;
@ -314,7 +314,7 @@ mod tests {
// The path requires the pid we use, so we can only do do after
// obtaining the child pid here.
id_mapper.ensure_gid_path(&child)?;
setup_mapping(&rootless, child)?;
setup_mapping(&ns_config, child)?;
let line = fs::read_to_string(id_mapper.get_gid_path(&child))?;
let line_splited = line.split_whitespace();
for (act, expect) in line_splited.zip([

@ -11,11 +11,11 @@ use std::{env, path::PathBuf};
// allows us to mock the id mapping logic in unit tests by using a different
// base path other than `/proc`.
#[derive(Debug, Clone)]
pub struct RootlessIDMapper {
pub struct UserNamespaceIDMapper {
base_path: PathBuf,
}
impl Default for RootlessIDMapper {
impl Default for UserNamespaceIDMapper {
fn default() -> Self {
Self {
// By default, the `uid_map` and `gid_map` files are located in the
@ -26,8 +26,8 @@ impl Default for RootlessIDMapper {
}
}
impl RootlessIDMapper {
// In production code, we can direclt use the `new` function without the
impl UserNamespaceIDMapper {
// In production code, we can direct use the `new` function without the
// need to worry about the default.
pub fn new() -> Self {
Default::default()
@ -62,12 +62,12 @@ impl RootlessIDMapper {
}
#[derive(Debug, thiserror::Error)]
pub enum RootlessError {
pub enum UserNamespaceError {
#[error(transparent)]
MissingSpec(#[from] crate::error::MissingSpecError),
#[error("rootless container requires valid user namespace definition")]
#[error("user namespace definition is invalid")]
NoUserNamespace,
#[error("invalid spec for rootless container")]
#[error("invalid spec for new user namespace container")]
InvalidSpec(#[from] ValidateSpecError),
#[error("failed to read unprivileged userns clone")]
ReadUnprivilegedUsernsClone(#[source] std::io::Error),
@ -79,17 +79,17 @@ pub enum RootlessError {
IDMapping(#[from] MappingError),
}
type Result<T> = std::result::Result<T, RootlessError>;
type Result<T> = std::result::Result<T, UserNamespaceError>;
#[derive(Debug, thiserror::Error)]
pub enum ValidateSpecError {
#[error(transparent)]
MissingSpec(#[from] crate::error::MissingSpecError),
#[error("rootless container requires valid user namespace definition")]
NoUserNamespace,
#[error("rootless container requires valid uid mappings")]
NoUserNamespace, // TODO fix this while fixing podman
#[error("new user namespace requires valid uid mappings")]
NoUIDMappings,
#[error("rootless container requires valid gid mappings")]
#[error("new user namespace requires valid gid mappings")]
NoGIDMapping,
#[error("no mount in spec")]
NoMountSpec,
@ -99,9 +99,13 @@ pub enum ValidateSpecError {
GidNotMapped(u32),
#[error("failed to parse ID")]
ParseID(#[source] std::num::ParseIntError),
#[error("mount options require mapping uid inside the rootless container")]
#[error(
"mount options require mapping valid uid inside the container with new user namespace"
)]
MountGidMapping(u32),
#[error("mount options require mapping gid inside the rootless container")]
#[error(
"mount options require mapping valid gid inside the container with new user namespace"
)]
MountUidMapping(u32),
#[error(transparent)]
Namespaces(#[from] NamespaceError),
@ -122,7 +126,7 @@ pub enum MappingError {
}
#[derive(Debug, Clone, Default)]
pub struct Rootless {
pub struct UserNamespaceConfig {
/// Location of the newuidmap binary
pub newuidmap: Option<PathBuf>,
/// Location of the newgidmap binary
@ -133,14 +137,14 @@ pub struct Rootless {
pub(crate) gid_mappings: Option<Vec<LinuxIdMapping>>,
/// Info on the user namespaces
pub user_namespace: Option<LinuxNamespace>,
/// Is rootless container requested by a privileged user
/// Is the container requested by a privileged user
pub privileged: bool,
/// Path to the id mappings
pub rootless_id_mapper: RootlessIDMapper,
pub id_mapper: UserNamespaceIDMapper,
}
impl Rootless {
pub fn new(spec: &Spec) -> Result<Option<Rootless>> {
impl UserNamespaceConfig {
pub fn new(spec: &Spec) -> Result<Option<Self>> {
let linux = spec.linux().as_ref().ok_or(MissingSpecError::Linux)?;
let namespaces = Namespaces::try_from(linux.namespaces().as_ref())
.map_err(ValidateSpecError::Namespaces)?;
@ -150,26 +154,27 @@ impl Rootless {
// If conditions requires us to use rootless, we must either create a new
// user namespace or enter an existing.
// TODO FIX THIS FOR ROOTLESS
if rootless_required() && user_namespace.is_none() {
return Err(RootlessError::NoUserNamespace);
return Err(UserNamespaceError::NoUserNamespace);
}
if user_namespace.is_some() && user_namespace.unwrap().path().is_none() {
tracing::debug!("rootless container should be created");
tracing::debug!("container with new user namespace should be created");
validate_spec_for_rootless(spec).map_err(|err| {
tracing::error!("failed to validate spec for rootless container: {}", err);
validate_spec_for_new_user_ns(spec).map_err(|err| {
tracing::error!("failed to validate spec for new user namespace: {}", err);
err
})?;
let mut rootless = Rootless::try_from(linux)?;
let mut user_ns_config = UserNamespaceConfig::try_from(linux)?;
if let Some((uid_binary, gid_binary)) = lookup_map_binaries(linux)? {
rootless.newuidmap = Some(uid_binary);
rootless.newgidmap = Some(gid_binary);
user_ns_config.newuidmap = Some(uid_binary);
user_ns_config.newgidmap = Some(gid_binary);
}
Ok(Some(rootless))
Ok(Some(user_ns_config))
} else {
tracing::debug!("this is NOT a rootless container");
tracing::debug!("this is container does NOT create a new user namespace");
Ok(None)
}
}
@ -179,7 +184,7 @@ impl Rootless {
if let Some(uid_mappings) = self.uid_mappings.as_ref() {
write_id_mapping(
target_pid,
self.rootless_id_mapper.get_uid_path(&target_pid).as_path(),
self.id_mapper.get_uid_path(&target_pid).as_path(),
uid_mappings,
self.newuidmap.as_deref(),
)?;
@ -192,7 +197,7 @@ impl Rootless {
if let Some(gid_mappings) = self.gid_mappings.as_ref() {
write_id_mapping(
target_pid,
self.rootless_id_mapper.get_gid_path(&target_pid).as_path(),
self.id_mapper.get_gid_path(&target_pid).as_path(),
gid_mappings,
self.newgidmap.as_deref(),
)?;
@ -200,13 +205,13 @@ impl Rootless {
Ok(())
}
pub fn with_id_mapper(&mut self, mapper: RootlessIDMapper) {
self.rootless_id_mapper = mapper
pub fn with_id_mapper(&mut self, mapper: UserNamespaceIDMapper) {
self.id_mapper = mapper
}
}
impl TryFrom<&Linux> for Rootless {
type Error = RootlessError;
impl TryFrom<&Linux> for UserNamespaceConfig {
type Error = UserNamespaceError;
fn try_from(linux: &Linux) -> Result<Self> {
let namespaces = Namespaces::try_from(linux.namespaces().as_ref())
@ -221,12 +226,13 @@ impl TryFrom<&Linux> for Rootless {
gid_mappings: linux.gid_mappings().to_owned(),
user_namespace: user_namespace.cloned(),
privileged: nix::unistd::geteuid().is_root(),
rootless_id_mapper: RootlessIDMapper::new(),
id_mapper: UserNamespaceIDMapper::new(),
})
}
}
/// Checks if rootless mode should be used
// TODO fix this along with podman
pub fn rootless_required() -> bool {
if !nix::unistd::geteuid().is_root() {
return true;
@ -241,24 +247,27 @@ pub fn unprivileged_user_ns_enabled() -> Result<bool> {
return Ok(true);
}
let content =
fs::read_to_string(user_ns_sysctl).map_err(RootlessError::ReadUnprivilegedUsernsClone)?;
let content = fs::read_to_string(user_ns_sysctl)
.map_err(UserNamespaceError::ReadUnprivilegedUsernsClone)?;
match content
.trim()
.parse::<u8>()
.map_err(RootlessError::ParseUnprivilegedUsernsClone)?
.map_err(UserNamespaceError::ParseUnprivilegedUsernsClone)?
{
0 => Ok(false),
1 => Ok(true),
v => Err(RootlessError::UnknownUnprivilegedUsernsClone(v)),
v => Err(UserNamespaceError::UnknownUnprivilegedUsernsClone(v)),
}
}
/// Validates that the spec contains the required information for
/// running in rootless mode
fn validate_spec_for_rootless(spec: &Spec) -> std::result::Result<(), ValidateSpecError> {
tracing::debug!(?spec, "validating spec for rootless container");
/// creating a new user namespace
fn validate_spec_for_new_user_ns(spec: &Spec) -> std::result::Result<(), ValidateSpecError> {
tracing::debug!(
?spec,
"validating spec for container with new user namespace"
);
let linux = spec.linux().as_ref().ok_or(MissingSpecError::Linux)?;
let namespaces = Namespaces::try_from(linux.namespaces().as_ref())?;
if namespaces.get(LinuxNamespaceType::User)?.is_none() {
@ -281,7 +290,7 @@ fn validate_spec_for_rootless(spec: &Spec) -> std::result::Result<(), ValidateSp
return Err(ValidateSpecError::NoGIDMapping);
}
validate_mounts_for_rootless(
validate_mounts_for_new_user_ns(
spec.mounts()
.as_ref()
.ok_or(ValidateSpecError::NoMountSpec)?,
@ -296,6 +305,7 @@ fn validate_spec_for_rootless(spec: &Spec) -> std::result::Result<(), ValidateSp
{
let privileged = nix::unistd::geteuid().is_root();
// TODO fix this along with fixes for podman
match (privileged, additional_gids.is_empty()) {
(true, false) => {
for gid in additional_gids {
@ -320,7 +330,7 @@ fn validate_spec_for_rootless(spec: &Spec) -> std::result::Result<(), ValidateSp
Ok(())
}
fn validate_mounts_for_rootless(
fn validate_mounts_for_new_user_ns(
mounts: &[Mount],
uid_mappings: &[LinuxIdMapping],
gid_mappings: &[LinuxIdMapping],
@ -337,7 +347,7 @@ fn validate_mounts_for_rootless(
tracing::error!(
?mount,
?opt,
"mount specifies option which is not mapped inside the rootless container"
"mount specifies option which is not mapped inside the container with new user namespace"
);
return Err(ValidateSpecError::MountUidMapping(
opt[4..].parse().map_err(ValidateSpecError::ParseID)?,
@ -353,7 +363,7 @@ fn validate_mounts_for_rootless(
tracing::error!(
?mount,
?opt,
"mount specifies option which is not mapped inside the rootless container"
"mount specifies option which is not mapped inside the container with new user namespace"
);
return Err(ValidateSpecError::MountGidMapping(
opt[4..].parse().map_err(ValidateSpecError::ParseID)?,
@ -486,7 +496,7 @@ mod tests {
.gid_mappings(gid_mappings)
.build()?;
let spec = SpecBuilder::default().linux(linux).build()?;
assert!(validate_spec_for_rootless(&spec).is_ok());
assert!(validate_spec_for_new_user_ns(&spec).is_ok());
Ok(())
}
@ -511,7 +521,7 @@ mod tests {
.uid_mappings(uid_mappings.clone())
.gid_mappings(gid_mappings.clone())
.build()?;
assert!(validate_spec_for_rootless(
assert!(validate_spec_for_new_user_ns(
&SpecBuilder::default()
.linux(linux_no_userns)
.build()
@ -524,7 +534,7 @@ mod tests {
.uid_mappings(vec![])
.gid_mappings(gid_mappings.clone())
.build()?;
assert!(validate_spec_for_rootless(
assert!(validate_spec_for_new_user_ns(
&SpecBuilder::default()
.linux(linux_uid_empty)
.build()
@ -537,7 +547,7 @@ mod tests {
.uid_mappings(uid_mappings.clone())
.gid_mappings(vec![])
.build()?;
assert!(validate_spec_for_rootless(
assert!(validate_spec_for_new_user_ns(
&SpecBuilder::default()
.linux(linux_gid_empty)
.build()
@ -549,7 +559,7 @@ mod tests {
.namespaces(vec![userns.clone()])
.gid_mappings(gid_mappings)
.build()?;
assert!(validate_spec_for_rootless(
assert!(validate_spec_for_new_user_ns(
&SpecBuilder::default()
.linux(linux_uid_none)
.build()
@ -561,7 +571,7 @@ mod tests {
.namespaces(vec![userns])
.uid_mappings(uid_mappings)
.build()?;
assert!(validate_spec_for_rootless(
assert!(validate_spec_for_new_user_ns(
&SpecBuilder::default()
.linux(linux_gid_none)
.build()
@ -601,19 +611,19 @@ mod tests {
let pid = getpid();
let tmp = tempfile::tempdir()?;
let id_mapper = RootlessIDMapper {
let id_mapper = UserNamespaceIDMapper {
base_path: tmp.path().to_path_buf(),
};
id_mapper.ensure_uid_path(&pid)?;
let mut rootless = Rootless::new(&spec)?.unwrap();
rootless.with_id_mapper(id_mapper.clone());
rootless.write_uid_mapping(pid)?;
let mut config = UserNamespaceConfig::new(&spec)?.unwrap();
config.with_id_mapper(id_mapper.clone());
config.write_uid_mapping(pid)?;
assert_eq!(
format!("{container_id} {host_uid} {size}"),
fs::read_to_string(id_mapper.get_uid_path(&pid))?
);
rootless.write_gid_mapping(pid)?;
config.write_gid_mapping(pid)?;
Ok(())
}
@ -646,14 +656,14 @@ mod tests {
let pid = getpid();
let tmp = tempfile::tempdir()?;
let id_mapper = RootlessIDMapper {
let id_mapper = UserNamespaceIDMapper {
base_path: tmp.path().to_path_buf(),
};
id_mapper.ensure_gid_path(&pid)?;
let mut rootless = Rootless::new(&spec)?.unwrap();
rootless.with_id_mapper(id_mapper.clone());
rootless.write_gid_mapping(pid)?;
let mut config = UserNamespaceConfig::new(&spec)?.unwrap();
config.with_id_mapper(id_mapper.clone());
config.write_gid_mapping(pid)?;
assert_eq!(
format!("{container_id} {host_gid} {size}"),
fs::read_to_string(id_mapper.get_gid_path(&pid))?

@ -147,11 +147,11 @@ pub fn get_user_home(uid: u32) -> Option<PathBuf> {
pub fn get_cgroup_path(
cgroups_path: &Option<PathBuf>,
container_id: &str,
rootless: bool,
new_user_ns: bool,
) -> PathBuf {
match cgroups_path {
Some(cpath) => cpath.clone(),
None => match rootless {
None => match new_user_ns {
false => PathBuf::from(container_id),
true => PathBuf::from(format!(":youki:{container_id}")),
},

@ -5,7 +5,7 @@ use std::{fs, path::Path};
use anyhow::Result;
use clap::Parser;
use libcontainer::rootless;
use libcontainer::user_ns;
use procfs::{CpuInfo, Meminfo};
#[cfg(feature = "v2")]
@ -211,7 +211,7 @@ pub fn print_namespaces() {
print_feature_status(&content, "CONFIG_UTS_NS", FeatureDisplay::new("uts"));
print_feature_status(&content, "CONFIG_IPC_NS", FeatureDisplay::new("ipc"));
let user_display = match rootless::unprivileged_user_ns_enabled() {
let user_display = match user_ns::unprivileged_user_ns_enabled() {
Ok(false) => FeatureDisplay::with_status("user", "enabled (root only)", "disabled"),
_ => FeatureDisplay::new("user"),
};

@ -1,5 +1,5 @@
use anyhow::{bail, Result};
use libcontainer::rootless::rootless_required;
use libcontainer::user_ns::rootless_required;
use libcontainer::utils::create_dir_all_with_mode;
use nix::libc;
use nix::sys::stat::Mode;

@ -22,7 +22,7 @@ This exposes several modules, each dealing with a specific aspect of working wit
- `rootfs` : this contains modules which deal with rootfs, which is minimal filesystem that is provided to the container.
- `rootless` : this deals with running containers in a rootless configuration, that is running containers without needing root permissions.
- `user_ns` : this deals with running containers in with new user namespace, usually rootless containers will use this, that is running containers without needing root permissions.
- `seccomp` : this deals with setting up seccomp for container process. It uses libseccomp crate in order to do that.