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:
parent
e5232ceb17
commit
4e26799e15
14
MirgationGuide.md
Normal file
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.
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user