mirror of
https://github.com/containers/youki
synced 2024-05-21 06:56:06 +02:00
Merge pull request #2029 from yihuaf/yihuaf/seccomp
Bump the oci-spec-rs to 0.6.1 to resolve seccomp rule issue
This commit is contained in:
commit
a49d14cca9
|
@ -2335,9 +2335,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "oci-spec"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "214b837f7dde5026f2028ead5ae720073277c19f82ff85623b142c39d4b843e7"
|
||||
checksum = "cf77d2eace1f4909b081d231d1ad3570ba3448ae95290ab701314faaee1b611a"
|
||||
dependencies = [
|
||||
"derive_builder",
|
||||
"getset",
|
||||
|
|
|
@ -22,7 +22,7 @@ cgroupsv2_devices = ["rbpf", "libbpf-sys", "errno", "libc"]
|
|||
[dependencies]
|
||||
nix = "0.26.2"
|
||||
procfs = "0.15.1"
|
||||
oci-spec = { version = "^0.6.0", features = ["runtime"] }
|
||||
oci-spec = { version = "^0.6.1", features = ["runtime"] }
|
||||
dbus = { version = "0.9.7", optional = true }
|
||||
fixedbitset = "0.4.2"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
|
|
@ -28,7 +28,7 @@ fastrand = "^1.7.0"
|
|||
futures = { version = "0.3", features = ["thread-pool"] }
|
||||
libc = "0.2.146"
|
||||
nix = "0.26.2"
|
||||
oci-spec = { version = "^0.6.0", features = ["runtime"] }
|
||||
oci-spec = { version = "^0.6.1", features = ["runtime"] }
|
||||
once_cell = "1.18.0"
|
||||
procfs = "0.15.1"
|
||||
prctl = "1.0.0"
|
||||
|
|
|
@ -22,6 +22,8 @@ pub enum NamespaceError {
|
|||
IO(#[from] std::io::Error),
|
||||
#[error(transparent)]
|
||||
Syscall(#[from] crate::syscall::SyscallError),
|
||||
#[error("Namespace type not supported: {0}")]
|
||||
NotSupported(String),
|
||||
}
|
||||
|
||||
static ORDERED_NAMESPACES: &[CloneFlags] = &[
|
||||
|
@ -40,8 +42,8 @@ pub struct Namespaces {
|
|||
namespace_map: collections::HashMap<CloneFlags, LinuxNamespace>,
|
||||
}
|
||||
|
||||
fn get_clone_flag(namespace_type: LinuxNamespaceType) -> CloneFlags {
|
||||
match namespace_type {
|
||||
fn get_clone_flag(namespace_type: LinuxNamespaceType) -> Result<CloneFlags> {
|
||||
let flag = match namespace_type {
|
||||
LinuxNamespaceType::User => CloneFlags::CLONE_NEWUSER,
|
||||
LinuxNamespaceType::Pid => CloneFlags::CLONE_NEWPID,
|
||||
LinuxNamespaceType::Uts => CloneFlags::CLONE_NEWUTS,
|
||||
|
@ -49,22 +51,32 @@ fn get_clone_flag(namespace_type: LinuxNamespaceType) -> CloneFlags {
|
|||
LinuxNamespaceType::Network => CloneFlags::CLONE_NEWNET,
|
||||
LinuxNamespaceType::Cgroup => CloneFlags::CLONE_NEWCGROUP,
|
||||
LinuxNamespaceType::Mount => CloneFlags::CLONE_NEWNS,
|
||||
}
|
||||
LinuxNamespaceType::Time => return Err(NamespaceError::NotSupported("time".to_string())),
|
||||
};
|
||||
|
||||
Ok(flag)
|
||||
}
|
||||
|
||||
impl From<Option<&Vec<LinuxNamespace>>> for Namespaces {
|
||||
fn from(namespaces: Option<&Vec<LinuxNamespace>>) -> Self {
|
||||
impl TryFrom<Option<&Vec<LinuxNamespace>>> for Namespaces {
|
||||
type Error = NamespaceError;
|
||||
|
||||
fn try_from(namespaces: Option<&Vec<LinuxNamespace>>) -> Result<Self> {
|
||||
let command: Box<dyn Syscall> = create_syscall();
|
||||
let namespace_map: collections::HashMap<CloneFlags, LinuxNamespace> = namespaces
|
||||
.unwrap_or(&vec![])
|
||||
.iter()
|
||||
.map(|ns| (get_clone_flag(ns.typ()), ns.clone()))
|
||||
.map(|ns| match get_clone_flag(ns.typ()) {
|
||||
Ok(flag) => Ok((flag, ns.clone())),
|
||||
Err(err) => Err(err),
|
||||
})
|
||||
.collect::<Result<Vec<(CloneFlags, LinuxNamespace)>>>()?
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
Namespaces {
|
||||
Ok(Namespaces {
|
||||
command,
|
||||
namespace_map,
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,7 +105,7 @@ impl Namespaces {
|
|||
},
|
||||
)?;
|
||||
self.command
|
||||
.set_ns(fd, get_clone_flag(namespace.typ()))
|
||||
.set_ns(fd, get_clone_flag(namespace.typ())?)
|
||||
.map_err(|err| {
|
||||
tracing::error!(?err, ?namespace, "failed to set namespace");
|
||||
err
|
||||
|
@ -105,7 +117,7 @@ impl Namespaces {
|
|||
}
|
||||
None => {
|
||||
self.command
|
||||
.unshare(get_clone_flag(namespace.typ()))
|
||||
.unshare(get_clone_flag(namespace.typ())?)
|
||||
.map_err(|err| {
|
||||
tracing::error!(?err, ?namespace, "failed to unshare namespace");
|
||||
err
|
||||
|
@ -116,8 +128,8 @@ impl Namespaces {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get(&self, k: LinuxNamespaceType) -> Option<&LinuxNamespace> {
|
||||
self.namespace_map.get(&get_clone_flag(k))
|
||||
pub fn get(&self, k: LinuxNamespaceType) -> Result<Option<&LinuxNamespace>> {
|
||||
Ok(self.namespace_map.get(&get_clone_flag(k)?))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -159,7 +171,8 @@ mod tests {
|
|||
#[serial]
|
||||
fn test_apply_namespaces() {
|
||||
let sample_linux_namespaces = gen_sample_linux_namespaces();
|
||||
let namespaces = Namespaces::from(Some(&sample_linux_namespaces));
|
||||
let namespaces = Namespaces::try_from(Some(&sample_linux_namespaces))
|
||||
.expect("create namespace struct should be good");
|
||||
let test_command: &TestHelperSyscall = namespaces.command.as_any().downcast_ref().unwrap();
|
||||
assert!(namespaces
|
||||
.apply_namespaces(|ns_type| { ns_type != CloneFlags::CLONE_NEWIPC })
|
||||
|
|
|
@ -271,7 +271,7 @@ fn apply_rest_namespaces(
|
|||
})?;
|
||||
|
||||
// Only set the host name if entering into a new uts namespace
|
||||
if let Some(uts_namespace) = namespaces.get(LinuxNamespaceType::Uts) {
|
||||
if let Some(uts_namespace) = namespaces.get(LinuxNamespaceType::Uts)? {
|
||||
if uts_namespace.path().is_none() {
|
||||
if let Some(hostname) = spec.hostname() {
|
||||
syscall.set_hostname(hostname).map_err(|err| {
|
||||
|
@ -340,7 +340,7 @@ pub fn container_init_process(
|
|||
let rootfs_path = args.rootfs;
|
||||
let hooks = spec.hooks().as_ref();
|
||||
let container = args.container.as_ref();
|
||||
let namespaces = Namespaces::from(linux.namespaces().as_ref());
|
||||
let namespaces = Namespaces::try_from(linux.namespaces().as_ref())?;
|
||||
|
||||
setsid().map_err(|err| {
|
||||
tracing::error!(?err, "failed to setsid to create a session");
|
||||
|
@ -370,14 +370,14 @@ pub fn container_init_process(
|
|||
})?;
|
||||
}
|
||||
|
||||
let bind_service = namespaces.get(LinuxNamespaceType::User).is_some();
|
||||
let bind_service = namespaces.get(LinuxNamespaceType::User)?.is_some();
|
||||
let rootfs = RootFS::new();
|
||||
rootfs
|
||||
.prepare_rootfs(
|
||||
spec,
|
||||
rootfs_path,
|
||||
bind_service,
|
||||
namespaces.get(LinuxNamespaceType::Cgroup).is_some(),
|
||||
namespaces.get(LinuxNamespaceType::Cgroup)?.is_some(),
|
||||
)
|
||||
.map_err(|err| {
|
||||
tracing::error!(?err, "failed to prepare rootfs");
|
||||
|
@ -388,7 +388,7 @@ pub fn container_init_process(
|
|||
// we use pivot_root, but if we are on the host mount namespace, we will
|
||||
// use simple chroot. Scary things will happen if you try to pivot_root
|
||||
// in the host mount namespace...
|
||||
if namespaces.get(LinuxNamespaceType::Mount).is_some() {
|
||||
if namespaces.get(LinuxNamespaceType::Mount)?.is_some() {
|
||||
// change the root of filesystem of the process to the rootfs
|
||||
syscall.pivot_rootfs(rootfs_path).map_err(|err| {
|
||||
tracing::error!(?err, ?rootfs_path, "failed to pivot root");
|
||||
|
@ -848,7 +848,7 @@ mod tests {
|
|||
.typ(LinuxNamespaceType::Pid)
|
||||
.build()?,
|
||||
];
|
||||
let namespaces = Namespaces::from(Some(&linux_spaces));
|
||||
let namespaces = Namespaces::try_from(Some(&linux_spaces))?;
|
||||
|
||||
apply_rest_namespaces(&namespaces, &spec, syscall.as_ref())?;
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ use nix::unistd::{close, write};
|
|||
use nix::unistd::{Gid, Pid, Uid};
|
||||
use oci_spec::runtime::{LinuxNamespaceType, LinuxResources};
|
||||
use procfs::process::Process;
|
||||
use std::convert::From;
|
||||
|
||||
use super::args::{ContainerArgs, ContainerType};
|
||||
use super::container_init_process::container_init_process;
|
||||
|
@ -43,7 +42,7 @@ pub fn container_intermediate_process(
|
|||
let command = &args.syscall;
|
||||
let spec = &args.spec;
|
||||
let linux = spec.linux().as_ref().ok_or(MissingSpecError::Linux)?;
|
||||
let namespaces = Namespaces::from(linux.namespaces().as_ref());
|
||||
let namespaces = Namespaces::try_from(linux.namespaces().as_ref())?;
|
||||
|
||||
// this needs to be done before we create the init process, so that the init
|
||||
// process will already be captured by the cgroup. It also needs to be done
|
||||
|
@ -65,7 +64,7 @@ pub fn container_intermediate_process(
|
|||
// namespace will be created, check
|
||||
// https://man7.org/linux/man-pages/man7/user_namespaces.7.html for more
|
||||
// information
|
||||
if let Some(user_namespace) = namespaces.get(LinuxNamespaceType::User) {
|
||||
if let Some(user_namespace) = namespaces.get(LinuxNamespaceType::User)? {
|
||||
namespaces.unshare_or_setns(user_namespace)?;
|
||||
if user_namespace.path().is_none() {
|
||||
tracing::debug!("creating new user namespace");
|
||||
|
@ -104,7 +103,7 @@ pub fn container_intermediate_process(
|
|||
}
|
||||
|
||||
// Pid namespace requires an extra fork to enter, so we enter pid namespace now.
|
||||
if let Some(pid_namespace) = namespaces.get(LinuxNamespaceType::Pid) {
|
||||
if let Some(pid_namespace) = namespaces.get(LinuxNamespaceType::Pid)? {
|
||||
namespaces.unshare_or_setns(pid_namespace)?;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use crate::error::MissingSpecError;
|
||||
use crate::namespaces::Namespaces;
|
||||
use crate::namespaces::{NamespaceError, Namespaces};
|
||||
use nix::unistd::Pid;
|
||||
use oci_spec::runtime::{Linux, LinuxIdMapping, LinuxNamespace, LinuxNamespaceType, Mount, Spec};
|
||||
use std::fs;
|
||||
|
@ -103,6 +103,8 @@ pub enum ValidateSpecError {
|
|||
MountGidMapping(u32),
|
||||
#[error("mount options require mapping gid inside the rootless container")]
|
||||
MountUidMapping(u32),
|
||||
#[error(transparent)]
|
||||
Namespaces(#[from] NamespaceError),
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
|
@ -140,8 +142,11 @@ pub struct Rootless<'a> {
|
|||
impl<'a> Rootless<'a> {
|
||||
pub fn new(spec: &'a Spec) -> Result<Option<Rootless<'a>>> {
|
||||
let linux = spec.linux().as_ref().ok_or(MissingSpecError::Linux)?;
|
||||
let namespaces = Namespaces::from(linux.namespaces().as_ref());
|
||||
let user_namespace = namespaces.get(LinuxNamespaceType::User);
|
||||
let namespaces = Namespaces::try_from(linux.namespaces().as_ref())
|
||||
.map_err(ValidateSpecError::Namespaces)?;
|
||||
let user_namespace = namespaces
|
||||
.get(LinuxNamespaceType::User)
|
||||
.map_err(ValidateSpecError::Namespaces)?;
|
||||
|
||||
// If conditions requires us to use rootless, we must either create a new
|
||||
// user namespace or enter an existing.
|
||||
|
@ -156,7 +161,7 @@ impl<'a> Rootless<'a> {
|
|||
tracing::error!("failed to validate spec for rootless container: {}", err);
|
||||
err
|
||||
})?;
|
||||
let mut rootless = Rootless::from(linux);
|
||||
let mut rootless = Rootless::try_from(linux)?;
|
||||
if let Some((uid_binary, gid_binary)) = lookup_map_binaries(linux)? {
|
||||
rootless.newuidmap = Some(uid_binary);
|
||||
rootless.newgidmap = Some(gid_binary);
|
||||
|
@ -200,11 +205,16 @@ impl<'a> Rootless<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> From<&'a Linux> for Rootless<'a> {
|
||||
fn from(linux: &'a Linux) -> Self {
|
||||
let namespaces = Namespaces::from(linux.namespaces().as_ref());
|
||||
let user_namespace = namespaces.get(LinuxNamespaceType::User);
|
||||
Self {
|
||||
impl<'a> TryFrom<&'a Linux> for Rootless<'a> {
|
||||
type Error = RootlessError;
|
||||
|
||||
fn try_from(linux: &'a Linux) -> Result<Self> {
|
||||
let namespaces = Namespaces::try_from(linux.namespaces().as_ref())
|
||||
.map_err(ValidateSpecError::Namespaces)?;
|
||||
let user_namespace = namespaces
|
||||
.get(LinuxNamespaceType::User)
|
||||
.map_err(ValidateSpecError::Namespaces)?;
|
||||
Ok(Self {
|
||||
newuidmap: None,
|
||||
newgidmap: None,
|
||||
uid_mappings: linux.uid_mappings().as_ref(),
|
||||
|
@ -212,7 +222,7 @@ impl<'a> From<&'a Linux> for Rootless<'a> {
|
|||
user_namespace: user_namespace.cloned(),
|
||||
privileged: nix::unistd::geteuid().is_root(),
|
||||
rootless_id_mapper: RootlessIDMapper::new(),
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,8 +260,8 @@ pub fn unprivileged_user_ns_enabled() -> Result<bool> {
|
|||
fn validate_spec_for_rootless(spec: &Spec) -> std::result::Result<(), ValidateSpecError> {
|
||||
tracing::debug!(?spec, "validating spec for rootless container");
|
||||
let linux = spec.linux().as_ref().ok_or(MissingSpecError::Linux)?;
|
||||
let namespaces = Namespaces::from(linux.namespaces().as_ref());
|
||||
if namespaces.get(LinuxNamespaceType::User).is_none() {
|
||||
let namespaces = Namespaces::try_from(linux.namespaces().as_ref())?;
|
||||
if namespaces.get(LinuxNamespaceType::User)?.is_none() {
|
||||
return Err(ValidateSpecError::NoUserNamespace);
|
||||
}
|
||||
|
||||
|
|
|
@ -78,6 +78,7 @@ fn translate_arch(arch: Arch) -> ScmpArch {
|
|||
}
|
||||
|
||||
fn translate_action(action: LinuxSeccompAction, errno: Option<u32>) -> Result<ScmpAction> {
|
||||
tracing::trace!(?action, ?errno, "translating action");
|
||||
let errno = errno.map(|e| e as i32).unwrap_or(libc::EPERM);
|
||||
let action = match action {
|
||||
LinuxSeccompAction::ScmpActKill => ScmpAction::KillThread,
|
||||
|
@ -94,6 +95,7 @@ fn translate_action(action: LinuxSeccompAction, errno: Option<u32>) -> Result<Sc
|
|||
LinuxSeccompAction::ScmpActLog => ScmpAction::Log,
|
||||
};
|
||||
|
||||
tracing::trace!(?action, "translated action");
|
||||
Ok(action)
|
||||
}
|
||||
|
||||
|
@ -139,9 +141,11 @@ fn check_seccomp(seccomp: &LinuxSeccomp) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(level = "trace", skip(seccomp))]
|
||||
pub fn initialize_seccomp(seccomp: &LinuxSeccomp) -> Result<Option<io::RawFd>> {
|
||||
check_seccomp(seccomp)?;
|
||||
|
||||
tracing::trace!(default_action = ?seccomp.default_action(), errno = ?seccomp.default_errno_ret(), "initializing seccomp");
|
||||
let default_action = translate_action(seccomp.default_action(), seccomp.default_errno_ret())?;
|
||||
let mut ctx =
|
||||
ScmpFilterContext::new_filter(default_action).map_err(|err| SeccompError::NewFilter {
|
||||
|
@ -165,6 +169,7 @@ pub fn initialize_seccomp(seccomp: &LinuxSeccomp) -> Result<Option<io::RawFd>> {
|
|||
|
||||
if let Some(architectures) = seccomp.architectures() {
|
||||
for &arch in architectures {
|
||||
tracing::trace!(?arch, "adding architecture");
|
||||
ctx.add_arch(translate_arch(arch))
|
||||
.map_err(|err| SeccompError::AddArch { source: err, arch })?;
|
||||
}
|
||||
|
@ -226,6 +231,7 @@ pub fn initialize_seccomp(seccomp: &LinuxSeccomp) -> Result<Option<io::RawFd>> {
|
|||
translate_op(arg.op(), arg.value_two()),
|
||||
arg.value(),
|
||||
);
|
||||
tracing::trace!(?name, ?action, ?arg, "add seccomp conditional rule");
|
||||
ctx.add_rule_conditional(action, sc, &[cmp])
|
||||
.map_err(|err| {
|
||||
tracing::error!(
|
||||
|
@ -238,6 +244,7 @@ pub fn initialize_seccomp(seccomp: &LinuxSeccomp) -> Result<Option<io::RawFd>> {
|
|||
}
|
||||
}
|
||||
None => {
|
||||
tracing::trace!(?name, ?action, "add seccomp rule");
|
||||
ctx.add_rule(action, sc).map_err(|err| {
|
||||
tracing::error!(
|
||||
"failed to add seccomp rule: {:?}. Syscall: {name}",
|
||||
|
|
|
@ -70,9 +70,10 @@ pub struct GlobalOpts {
|
|||
// Example in future : '--debug change log level to debug. (default: "warn")'
|
||||
#[clap(long)]
|
||||
pub debug: bool,
|
||||
// Set a consistent behavior like in runc and crun: set log to the last given value
|
||||
/// set the log file to write youki logs to (default is '/dev/stderr')
|
||||
#[clap(short, long, overrides_with("log"))]
|
||||
pub log: Option<PathBuf>,
|
||||
/// set the log format ('text' (default), or 'json') (default: "text")
|
||||
#[clap(long)]
|
||||
pub log_format: Option<String>,
|
||||
/// root directory to store container state
|
||||
|
|
|
@ -32,7 +32,7 @@ libcgroups = { version = "0.0.5", path = "../libcgroups", default-features = fal
|
|||
libcontainer = { version = "0.0.5", path = "../libcontainer", default-features = false }
|
||||
liboci-cli = { version = "0.0.5", path = "../liboci-cli" }
|
||||
nix = "0.26.2"
|
||||
oci-spec = { version = "^0.6.0", features = ["runtime"] }
|
||||
oci-spec = { version = "^0.6.1", features = ["runtime"] }
|
||||
once_cell = "1.18.0"
|
||||
pentacle = "1.0.0"
|
||||
procfs = "0.15.1"
|
||||
|
|
|
@ -34,7 +34,7 @@ This will start the daemon and hang up the console. You can either start this as
|
|||
|
||||
In case you don't stop the original daemon, you can get an error message after previous command
|
||||
|
||||
```
|
||||
```console
|
||||
failed to start daemon: pid file found, ensure docker is not running or delete /var/run/docker.pid
|
||||
```
|
||||
|
||||
|
@ -63,19 +63,29 @@ let docker know youki
|
|||
([source](https://docs.docker.com/engine/reference/commandline/dockerd/#on-linux)).
|
||||
You may need to create this file, if it does not yet exist. A sample content of it:
|
||||
|
||||
```
|
||||
```json
|
||||
{
|
||||
"default-runtime": "runc",
|
||||
"runtimes": {
|
||||
"youki": {
|
||||
"path": "/path/to/youki/youki"
|
||||
"path": "/path/to/youki/youki",
|
||||
"runtimeArgs": [
|
||||
"--debug",
|
||||
"--systemd-log"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
After this (need to restart docker at the first time), you can use youki
|
||||
with docker: `docker run --runtime youki ...`.
|
||||
with docker: `docker run --runtime youki ...`. You can verify the runtime includes `youki`:
|
||||
|
||||
```console
|
||||
$ docker info|grep -i runtime
|
||||
Runtimes: youki runc
|
||||
Default Runtime: runc
|
||||
```
|
||||
|
||||
#### Using Youki Standalone
|
||||
|
||||
|
|
2
justfile
2
justfile
|
@ -24,7 +24,6 @@ runtimetest:
|
|||
rust-oci-tests-bin:
|
||||
./scripts/build.sh -o {{ ROOT }} -r -c integration-test
|
||||
|
||||
|
||||
# Tests
|
||||
|
||||
# run oci tests
|
||||
|
@ -86,6 +85,7 @@ clean:
|
|||
dev-prepare:
|
||||
cargo install typos-cli
|
||||
|
||||
# setup dependencies in CI
|
||||
ci-prepare:
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
|
|
@ -11,7 +11,7 @@ libcgroups = { path = "../../../crates/libcgroups" }
|
|||
libcontainer = { path = "../../../crates/libcontainer" }
|
||||
nix = "0.26.2"
|
||||
num_cpus = "1.15"
|
||||
oci-spec = "0.6.0"
|
||||
oci-spec = { version = "0.6.1", features = ["runtime"] }
|
||||
once_cell = "1.18.0"
|
||||
pnet_datalink = "0.33.0"
|
||||
procfs = "0.15.1"
|
||||
|
|
|
@ -4,7 +4,7 @@ version = "0.0.1"
|
|||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
oci-spec = { version = "0.6.0", features = ["runtime"] }
|
||||
oci-spec = { version = "0.6.1", features = ["runtime"] }
|
||||
nix = "0.26.2"
|
||||
anyhow = "1.0"
|
||||
|
||||
|
|
Loading…
Reference in New Issue