Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions tests/rust-integration-tests/integration_test/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::tests::hooks::get_hooks_tests;
use crate::tests::hostname::get_hostname_test;
use crate::tests::lifecycle::{ContainerCreate, ContainerLifecycle};
use crate::tests::linux_ns_itype::get_ns_itype_tests;
use crate::tests::mounts_recursive::get_mounts_recursive_test;
use crate::tests::pidfile::get_pidfile_test;
use crate::tests::readonly_paths::get_ro_paths_test;
use crate::tests::seccomp_notify::get_seccomp_notify_test;
Expand Down Expand Up @@ -90,6 +91,7 @@ fn main() -> Result<()> {
let seccomp_notify = get_seccomp_notify_test();
let ro_paths = get_ro_paths_test();
let hostname = get_hostname_test();
let mounts_recursive = get_mounts_recursive_test();

tm.add_test_group(Box::new(cl));
tm.add_test_group(Box::new(cc));
Expand All @@ -106,6 +108,7 @@ fn main() -> Result<()> {
tm.add_test_group(Box::new(seccomp_notify));
tm.add_test_group(Box::new(ro_paths));
tm.add_test_group(Box::new(hostname));
tm.add_test_group(Box::new(mounts_recursive));

tm.add_cleanup(Box::new(cgroups::cleanup_v1));
tm.add_cleanup(Box::new(cgroups::cleanup_v2));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fn create_spec(hostname: &str) -> Spec {
)
.process(
ProcessBuilder::default()
.args(vec!["runtimetest".to_string()])
.args(vec!["runtimetest".to_string(), "set_host_name".to_string()])
.build()
.expect("error in creating process config"),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod hooks;
pub mod hostname;
pub mod lifecycle;
pub mod linux_ns_itype;
pub mod mounts_recursive;
pub mod pidfile;
pub mod readonly_paths;
pub mod seccomp_notify;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
use crate::utils::test_inside_container;
use nix::mount::{mount, umount, MsFlags};
use nix::sys::stat::Mode;
use nix::unistd::{chown, Uid};
use oci_spec::runtime::{
get_default_mounts, Capability, LinuxBuilder, LinuxCapabilitiesBuilder, Mount, ProcessBuilder,
Spec, SpecBuilder,
};
use std::collections::hash_set::HashSet;
use std::os::unix::prelude::PermissionsExt;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use std::fs;
use test_framework::{Test, TestGroup, TestResult};

fn get_spec(added_mounts: Vec<Mount>, process_args: Vec<String>) -> Spec {
let mut mounts = get_default_mounts();
for mount in added_mounts {
mounts.push(mount);
}

let caps = vec![
Capability::Chown,
Capability::DacOverride,
Capability::Fsetid,
Capability::Fowner,
Capability::Mknod,
Capability::NetRaw,
Capability::Setgid,
Capability::Setuid,
Capability::Setfcap,
Capability::Setpcap,
Capability::NetBindService,
Capability::SysChroot,
Capability::Kill,
Capability::AuditWrite,
];
let mut cap_bounding = HashSet::new();
let mut cap_effective = HashSet::new();
let mut cap_permitted = HashSet::new();

for cap in caps {
cap_bounding.insert(cap);
cap_effective.insert(cap);
cap_permitted.insert(cap);
}

SpecBuilder::default()
.mounts(mounts)
.linux(
// Need to reset the read-only paths
LinuxBuilder::default()
.readonly_paths(vec![])
.build()
.expect("error in building linux config"),
)
.process(
ProcessBuilder::default()
.args(process_args)
.capabilities(
LinuxCapabilitiesBuilder::default()
.bounding(cap_bounding)
.effective(cap_effective)
.permitted(cap_permitted)
.build()
.unwrap(),
)
.rlimits(vec![])
.no_new_privileges(false)
.build()
.unwrap(),
)
.build()
.unwrap()
}

fn setup_mount(mount_dir: &Path, sub_mount_dir: &Path) {
fs::create_dir(mount_dir).unwrap();
mount::<Path, Path, str, str>(None, mount_dir, Some("tmpfs"), MsFlags::empty(), None).unwrap();
fs::create_dir(sub_mount_dir).unwrap();
mount::<Path, Path, str, str>(None, sub_mount_dir, Some("tmpfs"), MsFlags::empty(), None)
.unwrap();
}

fn clean_mount(mount_dir: &Path, sub_mount_dir: &Path) {
umount(sub_mount_dir).unwrap();
umount(mount_dir).unwrap();
fs::remove_dir_all(mount_dir).unwrap();
}

fn check_recursive_readonly() -> TestResult {
let rro_test_base_dir = PathBuf::from_str("/tmp").unwrap();
let rro_dir_path = rro_test_base_dir.join("rro_dir");
let rro_subdir_path = rro_dir_path.join("rro_subdir");
let mount_dest_path = PathBuf::from_str("/mnt").unwrap();

let mount_options = vec!["rbind".to_string(), "rro".to_string()];
let mut mount_spec = Mount::default();
mount_spec
.set_destination(mount_dest_path.clone())
.set_typ(None)
.set_source(Some(rro_dir_path.clone()))
.set_options(Some(mount_options.clone()));
let spec = get_spec(
vec![mount_spec],
vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
);

let result = test_inside_container(spec, &|_| {
setup_mount(&rro_dir_path, &rro_subdir_path);
Ok(())
});

clean_mount(&rro_dir_path, &rro_subdir_path);

result
}

fn check_recursive_nosuid() -> TestResult {
let rnosuid_test_base_dir = PathBuf::from_str("/tmp").unwrap();
let rnosuid_dir_path = rnosuid_test_base_dir.join("rnosuid_dir");
let rnosuid_subdir_path = rnosuid_dir_path.join("rnosuid_subdir");
let mount_dest_path = PathBuf::from_str("/mnt").unwrap();
let executable_file_name = "whoami";

let mount_options = vec!["rbind".to_string(), "rnosuid".to_string()];
let mut mount_spec = Mount::default();
mount_spec
.set_destination(mount_dest_path.clone())
.set_typ(None)
.set_source(Some(rnosuid_dir_path.clone()))
.set_options(Some(mount_options.clone()));
let spec = get_spec(
vec![mount_spec],
vec![
"sh".to_string(),
"-c".to_string(),
format!(
"{}; {}",
mount_dest_path.join(executable_file_name).to_str().unwrap(),
mount_dest_path
.join("rnosuid_subdir/whoami")
.to_str()
.unwrap()
),
],
);

let result = test_inside_container(spec, &|bundle_path| {
setup_mount(&rnosuid_dir_path, &rnosuid_subdir_path);

let executable_file_path = bundle_path.join("bin").join(executable_file_name);
let in_container_executable_file_path = rnosuid_dir_path.join(executable_file_name);
let in_container_executable_subdir_file_path =
rnosuid_subdir_path.join(executable_file_name);

fs::copy(&executable_file_path, &in_container_executable_file_path)?;
fs::copy(
&executable_file_path,
&in_container_executable_subdir_file_path,
)?;

let in_container_executable_file = fs::File::open(&in_container_executable_file_path)?;
let in_container_executable_subdir_file =
fs::File::open(&in_container_executable_subdir_file_path)?;

let mut in_container_executable_file_perm =
in_container_executable_file.metadata()?.permissions();
let mut in_container_executable_subdir_file_perm = in_container_executable_subdir_file
.metadata()?
.permissions();

// Change file user to nonexistent uid and set suid.
// if rnosuid is applied, whoami command is executed as root.
// but if not adapted, whoami command is executed as uid 1200 and make an error.
chown(
&in_container_executable_file_path,
Some(Uid::from_raw(1200)),
None,
)
.unwrap();
chown(
&in_container_executable_subdir_file_path,
Some(Uid::from_raw(1200)),
None,
)
.unwrap();
in_container_executable_file_perm
.set_mode(in_container_executable_file_perm.mode() | Mode::S_ISUID.bits());
in_container_executable_subdir_file_perm
.set_mode(in_container_executable_subdir_file_perm.mode() | Mode::S_ISUID.bits());

in_container_executable_file.set_permissions(in_container_executable_file_perm.clone())?;
in_container_executable_subdir_file
.set_permissions(in_container_executable_subdir_file_perm.clone())?;

Ok(())
});

clean_mount(&rnosuid_dir_path, &rnosuid_subdir_path);

result
}

fn check_recursive_noexec() -> TestResult {
let rnoexec_test_base_dir = PathBuf::from_str("/tmp").unwrap();
let rnoexec_dir_path = rnoexec_test_base_dir.join("rnoexec_dir");
let rnoexec_subdir_path = rnoexec_dir_path.join("rnoexec_subdir");
let mount_dest_path = PathBuf::from_str("/mnt").unwrap();

let mount_options = vec!["rbind".to_string(), "rnoexec".to_string()];
let mut mount_spec = Mount::default();
mount_spec
.set_destination(mount_dest_path.clone())
.set_typ(None)
.set_source(Some(rnoexec_dir_path.clone()))
.set_options(Some(mount_options.clone()));
let spec = get_spec(
vec![mount_spec],
vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
);

let result = test_inside_container(spec, &|bundle_path| {
setup_mount(&rnoexec_dir_path, &rnoexec_subdir_path);

let executable_file_name = "echo";
let executable_file_path = bundle_path.join("bin").join(executable_file_name);
let in_container_executable_file_path = rnoexec_dir_path.join(executable_file_name);
let in_container_executable_subdir_file_path =
rnoexec_subdir_path.join(executable_file_name);

fs::copy(&executable_file_path, &in_container_executable_file_path)?;
fs::copy(
&executable_file_path,
&in_container_executable_subdir_file_path,
)?;

Ok(())
});

clean_mount(&rnoexec_dir_path, &rnoexec_subdir_path);

result
}

pub fn get_mounts_recursive_test() -> TestGroup {
let rro_test = Test::new("rro_test", Box::new(check_recursive_readonly));
let rnosuid_test = Test::new("rnosuid_test", Box::new(check_recursive_nosuid));
let rnoexec_test = Test::new("rnoexec_test", Box::new(check_recursive_noexec));

let mut tg = TestGroup::new("mounts_recursive");
tg.add(vec![Box::new(rro_test), Box::new(rnosuid_test), Box::new(rnoexec_test)]);

tg
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ fn get_spec(readonly_paths: Vec<String>) -> Spec {
)
.process(
ProcessBuilder::default()
.args(vec!["runtimetest".to_string()])
.args(vec![
"runtimetest".to_string(),
"readonly_paths".to_string(),
])
.build()
.unwrap(),
)
Expand Down
2 changes: 2 additions & 0 deletions tests/rust-integration-tests/runtimetest/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,5 @@ edition = "2021"
[dependencies]
oci-spec = { version = "0.5.8", features = ["runtime"] }
nix = "0.25.0"
anyhow = "1.0"

15 changes: 13 additions & 2 deletions tests/rust-integration-tests/runtimetest/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod utils;

use oci_spec::runtime::Spec;
use std::path::PathBuf;
use std::env;

const SPEC_PATH: &str = "/config.json";

Expand All @@ -19,6 +20,16 @@ fn get_spec() -> Spec {

fn main() {
let spec = get_spec();
tests::validate_readonly_paths(&spec);
tests::validate_hostname(&spec);
let args: Vec<String> = env::args().collect();
let execute_test = match args.get(1) {
Some(execute_test) => execute_test.to_string(),
None => return eprintln!("error due to execute test flag not found"),
};

match &*execute_test {
"readonly_paths" => tests::validate_readonly_paths(&spec),
"set_host_name" => tests::validate_hostname(&spec),
"mounts_recursive" => tests::validate_mounts_recursive(&spec),
_ => eprintln!("error due to unexpected execute test flag: {}", execute_test),
}
}
Loading