Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add rsuid and rnosymfollow #1685

Merged
Merged
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
use crate::utils::test_inside_container;
use anyhow::anyhow;
use nix::libc;
use nix::mount::{mount, umount, MsFlags};
use nix::sys::stat::Mode;
use nix::unistd::{chown, Uid};
Expand All @@ -8,6 +10,8 @@ use oci_spec::runtime::{
};
use std::collections::hash_set::HashSet;
use std::fs;
use std::fs::File;
use std::os::unix::fs::symlink;
use std::os::unix::prelude::PermissionsExt;
use std::path::{Path, PathBuf};
use std::str::FromStr;
Expand Down Expand Up @@ -202,6 +206,37 @@ fn check_recursive_nosuid() -> TestResult {
result
}

fn check_recursive_rsuid() -> TestResult {
let rsuid_dir_path = PathBuf::from_str("/tmp/rsuid_dir").unwrap();
let mount_dest_path = PathBuf::from_str("/mnt/rsuid_dir").unwrap();
fs::create_dir_all(rsuid_dir_path.clone()).unwrap();

let mount_options = vec!["rbind".to_string(), "rsuid".to_string()];
let mut mount_spec = Mount::default();
mount_spec
.set_destination(mount_dest_path)
.set_typ(None)
.set_source(Some(rsuid_dir_path.clone()))
.set_options(Some(mount_options));
let spec = get_spec(
vec![mount_spec],
vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
);
let result = test_inside_container(spec, &|_| {
let original_file_path = format!("{}/{}", rsuid_dir_path.to_str().unwrap(), "file");
File::create(original_file_path.clone())?;
// chmod +s /tmp/rsuid_dir/file && chmod +g /tmp/rsuid_dir/file
lengrongfu marked this conversation as resolved.
Show resolved Hide resolved
let mode = libc::S_ISUID | libc::S_ISGID;
let result = unsafe { libc::chmod(original_file_path.as_ptr() as *const i8, mode) };
if result == -1 {
return Err(anyhow!(std::io::Error::last_os_error()));
}
Ok(())
});
fs::remove_dir_all(rsuid_dir_path).unwrap();
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");
Expand Down Expand Up @@ -492,6 +527,45 @@ fn check_recursive_rstrictatime() -> TestResult {
result
}

fn check_recursive_rnosymfollow() -> TestResult {
let rnosymfollow_dir_path = PathBuf::from_str("/tmp/rnosymfollow").unwrap();
let mount_dest_path = PathBuf::from_str("/mnt/rnosymfollow").unwrap();
fs::create_dir_all(rnosymfollow_dir_path.clone()).unwrap();

let mount_options = vec![
"rbind".to_string(),
"rnosymfollow".to_string(),
"rsuid".to_string(),
];
let mut mount_spec = Mount::default();
mount_spec
.set_destination(mount_dest_path)
.set_typ(None)
.set_source(Some(rnosymfollow_dir_path.clone()))
.set_options(Some(mount_options));
let spec = get_spec(
vec![mount_spec],
vec!["runtimetest".to_string(), "mounts_recursive".to_string()],
);
let result = test_inside_container(spec, &|_| {
let original_file_path = format!("{}/{}", rnosymfollow_dir_path.to_str().unwrap(), "file");
File::create(original_file_path.clone())?;
let link_file_path = format!("{}/{}", rnosymfollow_dir_path.to_str().unwrap(), "link");
println!("original file: {original_file_path:?},link file: {link_file_path:?}");
let mode = libc::S_ISUID | libc::S_ISGID;
let result = unsafe { libc::chmod(original_file_path.as_ptr() as *const i8, mode) };
if result == -1 {
return Err(anyhow!(std::io::Error::last_os_error()));
};
symlink(original_file_path, link_file_path)?;
println!("symlink success");
Ok(())
});

fs::remove_dir_all(rnosymfollow_dir_path).unwrap();
result
}

/// this mount test how to work?
/// 1. Create mount_options based on the mount properties of the test
/// 2. Create OCI.Spec content, container one process is runtimetest,(runtimetest is cargo model, file path `tests/rust-integration-tests/runtimetest/`)
Expand All @@ -500,6 +574,7 @@ fn check_recursive_rstrictatime() -> TestResult {
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 rsuid_test = Test::new("rsuid_test", Box::new(check_recursive_rsuid));
let rnoexec_test = Test::new("rnoexec_test", Box::new(check_recursive_noexec));
let rnodiratime_test = Test::new("rnodiratime_test", Box::new(check_recursive_rnodiratime));
let rdiratime_test = Test::new("rdiratime_test", Box::new(check_recursive_rdiratime));
Expand All @@ -511,11 +586,13 @@ pub fn get_mounts_recursive_test() -> TestGroup {
let rnorelatime_test = Test::new("rnorelatime_test", Box::new(check_recursive_rnorelatime));
let rnoatime_test = Test::new("rnoatime_test", Box::new(check_recursive_rnoatime));
let rstrictatime_test = Test::new("rstrictatime_test", Box::new(check_recursive_rstrictatime));
let rnosymfollow_test = Test::new("rnosymfollow_test", Box::new(check_recursive_rnosymfollow));

let mut tg = TestGroup::new("mounts_recursive");
tg.add(vec![
Box::new(rro_test),
Box::new(rnosuid_test),
Box::new(rsuid_test),
Box::new(rnoexec_test),
Box::new(rdiratime_test),
Box::new(rnodiratime_test),
Expand All @@ -527,6 +604,7 @@ pub fn get_mounts_recursive_test() -> TestGroup {
Box::new(rnorelatime_test),
Box::new(rnoatime_test),
Box::new(rstrictatime_test),
Box::new(rnosymfollow_test),
]);

tg
Expand Down
14 changes: 14 additions & 0 deletions tests/rust-integration-tests/runtimetest/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,20 @@ pub fn validate_mounts_recursive(spec: &Spec) {
eprintln!("path expected to be rstrictatime, found not rstrictatime, error: {e}");
}
}
"rnosymfollow" => {
if let Err(e) = utils::test_mount_rnosymfollow_option(
mount.destination().to_str().unwrap(),
) {
eprintln!("path expected to be rnosymfollow, found not rnosymfollow, error: {e}");
}
}
"rsuid" => {
if let Err(e) = utils::test_mount_rsuid_option(
mount.destination().to_str().unwrap(),
) {
eprintln!("path expected to be rsuid, found not rsuid, error: {e}");
}
}
_ => {}
}
}
Expand Down
46 changes: 46 additions & 0 deletions tests/rust-integration-tests/runtimetest/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use nix::sys::stat::stat;
use nix::sys::stat::SFlag;
use std::fs;
use std::fs::metadata;
use std::fs::symlink_metadata;
use std::os::unix::prelude::MetadataExt;
use std::path::PathBuf;
use std::process::Command;
Expand Down Expand Up @@ -401,3 +403,47 @@ pub fn test_mount_rstrictatime_option(path: &str) -> Result<(), std::io::Error>
}
Ok(())
}

pub fn test_mount_rnosymfollow_option(path: &str) -> Result<(), std::io::Error> {
let path = format!("{}/{}", path, "link");
let metadata = match symlink_metadata(path.clone()) {
Ok(metadata) => metadata,
Err(e) => {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("get file symlink_metadata err {path:?}, {e}"),
));
}
};
// check symbolic is followed
if metadata.file_type().is_symlink() && metadata.mode() & 0o777 == 0o777 {
Ok(())
} else {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("get file symlink_metadata err {path:?}"),
))
}
}

pub fn test_mount_rsuid_option(path: &str) -> Result<(), std::io::Error> {
let path = PathBuf::from(path).join("file");

let metadata = match metadata(path.clone()) {
Ok(metadata) => metadata,
Err(e) => {
return Err(std::io::Error::new(std::io::ErrorKind::Other, e));
}
};
// check suid and sgid
let suid = metadata.mode() & 0o4000 == 0o4000;
let sgid = metadata.mode() & 0o2000 == 0o2000;
println!("suid: {suid:?},sgid: {sgid:?}");
if suid && sgid {
return Ok(());
}
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("rsuid error {path:?}"),
));
}