mirror of
https://github.com/kata-containers/kata-containers.git
synced 2025-04-30 04:34:27 +00:00
agent: Only allow proc mount if it is procfs
This only allows some whitelists files bind mounted under proc and prevent other malicious mount to procfs. Fixes: #807 Signed-off-by: fupan.lfp <fupan.lfp@antfin.com>
This commit is contained in:
parent
33513fb49b
commit
acaa806cc7
@ -14,6 +14,7 @@ use nix::NixPath;
|
|||||||
use oci::{LinuxDevice, Mount, Spec};
|
use oci::{LinuxDevice, Mount, Spec};
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::fs::{self, OpenOptions};
|
use std::fs::{self, OpenOptions};
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
use std::os::unix;
|
use std::os::unix;
|
||||||
use std::os::unix::io::RawFd;
|
use std::os::unix::io::RawFd;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
@ -48,6 +49,13 @@ pub struct Info {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const MOUNTINFOFORMAT: &'static str = "{d} {d} {d}:{d} {} {} {} {}";
|
const MOUNTINFOFORMAT: &'static str = "{d} {d} {d}:{d} {} {} {} {}";
|
||||||
|
const PROC_PATH: &str = "/proc";
|
||||||
|
|
||||||
|
// since libc didn't defined this const for musl, thus redefined it here.
|
||||||
|
#[cfg(all(target_os = "linux", target_env = "gnu"))]
|
||||||
|
const PROC_SUPER_MAGIC: libc::c_long = 0x00009fa0;
|
||||||
|
#[cfg(all(target_os = "linux", target_env = "musl"))]
|
||||||
|
const PROC_SUPER_MAGIC: libc::c_ulong = 0x00009fa0;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref PROPAGATION: HashMap<&'static str, MsFlags> = {
|
static ref PROPAGATION: HashMap<&'static str, MsFlags> = {
|
||||||
@ -181,6 +189,7 @@ pub fn init_rootfs(
|
|||||||
m.destination
|
m.destination
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
if m.r#type == "cgroup" {
|
if m.r#type == "cgroup" {
|
||||||
mount_cgroups(cfd_log, &m, rootfs, flags, &data, cpath, mounts)?;
|
mount_cgroups(cfd_log, &m, rootfs, flags, &data, cpath, mounts)?;
|
||||||
} else {
|
} else {
|
||||||
@ -188,6 +197,10 @@ pub fn init_rootfs(
|
|||||||
flags &= !MsFlags::MS_RDONLY;
|
flags &= !MsFlags::MS_RDONLY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m.r#type == "bind" {
|
||||||
|
check_proc_mount(m)?;
|
||||||
|
}
|
||||||
|
|
||||||
mount_from(cfd_log, &m, &rootfs, flags, &data, "")?;
|
mount_from(cfd_log, &m, &rootfs, flags, &data, "")?;
|
||||||
// bind mount won't change mount options, we need remount to make mount options
|
// bind mount won't change mount options, we need remount to make mount options
|
||||||
// effective.
|
// effective.
|
||||||
@ -216,6 +229,59 @@ pub fn init_rootfs(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_proc_mount(m: &Mount) -> Result<()> {
|
||||||
|
// White list, it should be sub directories of invalid destinations
|
||||||
|
// These entries can be bind mounted by files emulated by fuse,
|
||||||
|
// so commands like top, free displays stats in container.
|
||||||
|
let valid_destinations = [
|
||||||
|
"/proc/cpuinfo",
|
||||||
|
"/proc/diskstats",
|
||||||
|
"/proc/meminfo",
|
||||||
|
"/proc/stat",
|
||||||
|
"/proc/swaps",
|
||||||
|
"/proc/uptime",
|
||||||
|
"/proc/loadavg",
|
||||||
|
"/proc/net/dev",
|
||||||
|
];
|
||||||
|
|
||||||
|
for i in valid_destinations.iter() {
|
||||||
|
if m.destination.as_str() == *i {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.destination == PROC_PATH {
|
||||||
|
// only allow a mount on-top of proc if it's source is "proc"
|
||||||
|
unsafe {
|
||||||
|
let mut stats = MaybeUninit::<libc::statfs>::uninit();
|
||||||
|
if let Ok(_) = m
|
||||||
|
.source
|
||||||
|
.with_nix_path(|path| libc::statfs(path.as_ptr(), stats.as_mut_ptr()))
|
||||||
|
{
|
||||||
|
if stats.assume_init().f_type == PROC_SUPER_MAGIC {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(anyhow!(format!(
|
||||||
|
"{} cannot be mounted to {} because it is not of type proc",
|
||||||
|
m.source, m.destination
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if m.destination.starts_with(PROC_PATH) {
|
||||||
|
return Err(anyhow!(format!(
|
||||||
|
"{} cannot be mounted because it is inside /proc",
|
||||||
|
m.destination
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
|
||||||
fn mount_cgroups_v2(cfd_log: RawFd, m: &Mount, rootfs: &str, flags: MsFlags) -> Result<()> {
|
fn mount_cgroups_v2(cfd_log: RawFd, m: &Mount, rootfs: &str, flags: MsFlags) -> Result<()> {
|
||||||
let olddir = unistd::getcwd()?;
|
let olddir = unistd::getcwd()?;
|
||||||
unistd::chdir(rootfs)?;
|
unistd::chdir(rootfs)?;
|
||||||
@ -1126,4 +1192,33 @@ mod tests {
|
|||||||
let ret = stat::stat("fifo");
|
let ret = stat::stat("fifo");
|
||||||
assert!(ret.is_ok(), "Should pass. Got: {:?}", ret);
|
assert!(ret.is_ok(), "Should pass. Got: {:?}", ret);
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn test_check_proc_mount() {
|
||||||
|
let mount = oci::Mount {
|
||||||
|
destination: "/proc".to_string(),
|
||||||
|
r#type: "bind".to_string(),
|
||||||
|
source: "/test".to_string(),
|
||||||
|
options: vec!["shared".to_string()],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(check_proc_mount(&mount).is_err());
|
||||||
|
|
||||||
|
let mount = oci::Mount {
|
||||||
|
destination: "/proc/cpuinfo".to_string(),
|
||||||
|
r#type: "bind".to_string(),
|
||||||
|
source: "/test".to_string(),
|
||||||
|
options: vec!["shared".to_string()],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(check_proc_mount(&mount).is_ok());
|
||||||
|
|
||||||
|
let mount = oci::Mount {
|
||||||
|
destination: "/proc/test".to_string(),
|
||||||
|
r#type: "bind".to_string(),
|
||||||
|
source: "/test".to_string(),
|
||||||
|
options: vec!["shared".to_string()],
|
||||||
|
};
|
||||||
|
|
||||||
|
assert!(check_proc_mount(&mount).is_err());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user