diff --git a/tests/contest/contest/src/main.rs b/tests/contest/contest/src/main.rs index 8060519a4..fb52bac7e 100644 --- a/tests/contest/contest/src/main.rs +++ b/tests/contest/contest/src/main.rs @@ -24,6 +24,7 @@ use crate::tests::pidfile::get_pidfile_test; use crate::tests::process_rlimits::get_process_rlimits_test; use crate::tests::process_user::get_process_user_test; use crate::tests::readonly_paths::get_ro_paths_test; +use crate::tests::root_readonly_true::get_root_readonly_test; use crate::tests::scheduler::get_scheduler_test; use crate::tests::seccomp::get_seccomp_test; use crate::tests::seccomp_notify::get_seccomp_notify_test; @@ -116,6 +117,7 @@ fn main() -> Result<()> { let scheduler = get_scheduler_test(); let io_priority_test = get_io_priority_test(); let devices = get_devices_test(); + let root_readonly = get_root_readonly_test(); let process_user = get_process_user_test(); let process_rlimtis = get_process_rlimits_test(); let no_pivot = get_no_pivot_test(); @@ -142,6 +144,7 @@ fn main() -> Result<()> { tm.add_test_group(Box::new(sysctl)); tm.add_test_group(Box::new(scheduler)); tm.add_test_group(Box::new(devices)); + tm.add_test_group(Box::new(root_readonly)); tm.add_test_group(Box::new(process_user)); tm.add_test_group(Box::new(process_rlimtis)); tm.add_test_group(Box::new(no_pivot)); diff --git a/tests/contest/contest/src/tests/mod.rs b/tests/contest/contest/src/tests/mod.rs index 68cca2e1d..98904f571 100644 --- a/tests/contest/contest/src/tests/mod.rs +++ b/tests/contest/contest/src/tests/mod.rs @@ -14,6 +14,7 @@ pub mod pidfile; pub mod process_rlimits; pub mod process_user; pub mod readonly_paths; +pub mod root_readonly_true; pub mod scheduler; pub mod seccomp; pub mod seccomp_notify; diff --git a/tests/contest/contest/src/tests/root_readonly_true/mod.rs b/tests/contest/contest/src/tests/root_readonly_true/mod.rs new file mode 100644 index 000000000..32bcfe81d --- /dev/null +++ b/tests/contest/contest/src/tests/root_readonly_true/mod.rs @@ -0,0 +1,2 @@ +mod root_readonly_tests; +pub use root_readonly_tests::get_root_readonly_test; diff --git a/tests/contest/contest/src/tests/root_readonly_true/root_readonly_tests.rs b/tests/contest/contest/src/tests/root_readonly_true/root_readonly_tests.rs new file mode 100644 index 000000000..21699dc7c --- /dev/null +++ b/tests/contest/contest/src/tests/root_readonly_true/root_readonly_tests.rs @@ -0,0 +1,43 @@ +use anyhow::{Context, Ok, Result}; +use oci_spec::runtime::{ProcessBuilder, RootBuilder, Spec, SpecBuilder}; +use test_framework::{test_result, Test, TestGroup, TestResult}; + +use crate::utils::test_inside_container; + +fn create_spec(readonly: bool) -> Result { + let spec = SpecBuilder::default() + .root(RootBuilder::default().readonly(readonly).build().unwrap()) + .process( + ProcessBuilder::default() + .args(vec!["runtimetest".to_string(), "root_readonly".to_string()]) + .build() + .expect("error in creating config"), + ) + .build() + .context("failed to build spec")?; + + Ok(spec) +} + +fn root_readonly_true_test() -> TestResult { + let spec_true = test_result!(create_spec(true)); + test_inside_container(spec_true, &|_| Ok(())) +} + +fn root_readonly_false_test() -> TestResult { + let spec_false = test_result!(create_spec(false)); + test_inside_container(spec_false, &|_| Ok(())) +} + +pub fn get_root_readonly_test() -> TestGroup { + let mut root_readonly_test_group = TestGroup::new("root_readonly"); + + let test_true = Test::new("root_readonly_true_test", Box::new(root_readonly_true_test)); + let test_false = Test::new( + "root_readonly_false_test", + Box::new(root_readonly_false_test), + ); + root_readonly_test_group.add(vec![Box::new(test_true), Box::new(test_false)]); + + root_readonly_test_group +} diff --git a/tests/contest/runtimetest/src/main.rs b/tests/contest/runtimetest/src/main.rs index 7e98f0847..339bacad0 100644 --- a/tests/contest/runtimetest/src/main.rs +++ b/tests/contest/runtimetest/src/main.rs @@ -44,6 +44,7 @@ fn main() { "io_priority_class_be" => tests::test_io_priority_class(&spec, IoprioClassBe), "io_priority_class_idle" => tests::test_io_priority_class(&spec, IoprioClassIdle), "devices" => tests::validate_devices(&spec), + "root_readonly" => tests::test_validate_root_readonly(&spec), "process_user" => tests::validate_process_user(&spec), "process_rlimits" => tests::validate_process_rlimits(&spec), "no_pivot" => tests::validate_rootfs(), diff --git a/tests/contest/runtimetest/src/tests.rs b/tests/contest/runtimetest/src/tests.rs index 8b358b285..c1b04cadb 100644 --- a/tests/contest/runtimetest/src/tests.rs +++ b/tests/contest/runtimetest/src/tests.rs @@ -15,7 +15,9 @@ use oci_spec::runtime::{ LinuxDevice, LinuxDeviceType, LinuxSchedulerPolicy, PosixRlimit, PosixRlimitType, Spec, }; -use crate::utils::{self, test_read_access, test_write_access}; +use crate::utils::{ + self, test_dir_read_access, test_dir_write_access, test_read_access, test_write_access, +}; ////////// ANCHOR: example_hello_world pub fn hello_world(_spec: &Spec) { @@ -550,6 +552,44 @@ pub fn test_io_priority_class(spec: &Spec, io_priority_class: IOPriorityClass) { } } +pub fn test_validate_root_readonly(spec: &Spec) { + let root = spec.root().as_ref().unwrap(); + if root.readonly().unwrap() { + if let Err(e) = test_dir_write_access("/") { + let errno = Errno::from_raw(e.raw_os_error().unwrap()); + if errno == Errno::EROFS { + /* This is expected */ + } else { + eprintln!( + "readonly root filesystem, error in testing write access for path /, error: {}", + errno + ); + } + } + if let Err(e) = test_dir_read_access("/") { + let errno = Errno::from_raw(e.raw_os_error().unwrap()); + if errno == Errno::EROFS { + /* This is expected */ + } else { + eprintln!( + "readonly root filesystem, error in testing read access for path /, error: {}", + errno + ); + } + } + } else if let Err(e) = test_dir_write_access("/") { + if e.raw_os_error().is_some() { + let errno = Errno::from_raw(e.raw_os_error().unwrap()); + eprintln!( + "readt only root filesystem is false but write access for path / is err, error: {}", + errno + ); + } else { + /* This is expected */ + } + } +} + pub fn validate_process_user(spec: &Spec) { let process = spec.process().as_ref().unwrap(); let expected_uid = Uid::from(process.user().uid()); diff --git a/tests/contest/runtimetest/src/utils.rs b/tests/contest/runtimetest/src/utils.rs index 4976fe5ae..fd1c1cbde 100644 --- a/tests/contest/runtimetest/src/utils.rs +++ b/tests/contest/runtimetest/src/utils.rs @@ -14,7 +14,7 @@ fn test_file_read_access(path: &str) -> Result<(), std::io::Error> { Ok(()) } -fn test_dir_read_access(path: &str) -> Result<(), std::io::Error> { +pub fn test_dir_read_access(path: &str) -> Result<(), std::io::Error> { let _ = std::fs::read_dir(path)?; Ok(()) } @@ -51,7 +51,7 @@ fn test_file_write_access(path: &str) -> Result<(), std::io::Error> { Ok(()) } -fn test_dir_write_access(path: &str) -> Result<(), std::io::Error> { +pub fn test_dir_write_access(path: &str) -> Result<(), std::io::Error> { let _ = std::fs::OpenOptions::new() .create(true) .truncate(true)