From b8c5f648f0047fc22bc68fba257187cea387a148 Mon Sep 17 00:00:00 2001 From: Caspian443 Date: Tue, 22 Jul 2025 16:20:28 +0800 Subject: [PATCH 1/7] runtime-rs: hypervisor: add SELinux support functions Implement core SELinux functionality including label parsing, validation, and process label setting to support SELinux enforcement on hypervisor processes. Fixes #9866 Signed-off-by: Caspian443 --- src/runtime-rs/crates/hypervisor/src/lib.rs | 1 + .../crates/hypervisor/src/selinux.rs | 71 +++++++++++++++++++ 2 files changed, 72 insertions(+) create mode 100644 src/runtime-rs/crates/hypervisor/src/selinux.rs diff --git a/src/runtime-rs/crates/hypervisor/src/lib.rs b/src/runtime-rs/crates/hypervisor/src/lib.rs index 6185fa8021..c22840bb51 100644 --- a/src/runtime-rs/crates/hypervisor/src/lib.rs +++ b/src/runtime-rs/crates/hypervisor/src/lib.rs @@ -18,6 +18,7 @@ pub mod dragonball; pub mod firecracker; mod kernel_param; pub mod qemu; +pub mod selinux; pub mod remote; pub use kernel_param::Param; pub mod utils; diff --git a/src/runtime-rs/crates/hypervisor/src/selinux.rs b/src/runtime-rs/crates/hypervisor/src/selinux.rs new file mode 100644 index 0000000000..eebc69716a --- /dev/null +++ b/src/runtime-rs/crates/hypervisor/src/selinux.rs @@ -0,0 +1,71 @@ +// Copyright 2024 The Kata Containers community +// +// SPDX-License-Identifier: Apache-2.0 + +use std::fs::{self, OpenOptions}; +use std::io::prelude::*; +use std::path::Path; + +use anyhow::{Context, Result}; +use nix::unistd::gettid; + +/// Check if SELinux is enabled on the system +pub fn is_selinux_enabled() -> bool { + let buf = match fs::read_to_string("/proc/mounts") { + Ok(content) => content, + Err(_) => return false, + }; + buf.contains("selinuxfs") +} + +pub fn set_exec_label(label: &str) -> Result<()> { + let mut attr_path = Path::new("/proc/thread-self/attr/exec").to_path_buf(); + if !attr_path.exists() { + // Fall back to the old convention + attr_path = Path::new("/proc/self/task") + .join(gettid().to_string()) + .join("attr/exec") + } + + let mut file = OpenOptions::new() + .write(true) + .truncate(true) + .open(attr_path)?; + file.write_all(label.as_bytes()) + .with_context(|| "failed to apply SELinux label")?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + const TEST_LABEL: &str = "system_u:system_r:unconfined_t:s0"; + + #[test] + fn test_is_selinux_enabled() { + let str = fs::read_to_string("/proc/mounts").unwrap(); + let expected = str.contains("selinuxfs"); + assert_eq!(is_selinux_enabled(), expected); + } + + #[test] + fn test_set_exec_label() { + let ret = set_exec_label(TEST_LABEL); + if is_selinux_enabled() { + assert!(ret.is_ok(), "Expecting Ok, Got {:?}", ret); + // 检查 label 是否被正确设置 + let mut attr_path = std::path::Path::new("/proc/thread-self/attr/exec").to_path_buf(); + if !attr_path.exists() { + attr_path = std::path::Path::new("/proc/self/task") + .join(nix::unistd::gettid().to_string()) + .join("attr/exec"); + } + let label = std::fs::read_to_string(attr_path).unwrap(); + assert_eq!(label.trim_end_matches('\0'), TEST_LABEL); + } else { + assert!(ret.is_err(), "Expecting error, Got {:?}", ret); + } + } +} From ce6430a0cbd75f719b449ef5056218db516d1101 Mon Sep 17 00:00:00 2001 From: Caspian443 Date: Tue, 22 Jul 2025 16:21:05 +0800 Subject: [PATCH 2/7] runtime-rs: config: add disable_selinux and selinux_label fields Add SELinux configuration fields to the hypervisor struct to bring runtime-rs in line with the runtime-go SELinux feature implementation. - disable_selinux: whether to disable SELinux enforcement on the VMM process - selinux_label: the SELinux context label to apply to the VMM process Fixes #9866 Signed-off-by: Caspian443 --- src/libs/kata-types/src/config/hypervisor/mod.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/libs/kata-types/src/config/hypervisor/mod.rs b/src/libs/kata-types/src/config/hypervisor/mod.rs index 09b0f150cc..8f26ef3e88 100644 --- a/src/libs/kata-types/src/config/hypervisor/mod.rs +++ b/src/libs/kata-types/src/config/hypervisor/mod.rs @@ -904,6 +904,10 @@ pub struct SecurityInfo { rename = "tdx_quote_generation_service_socket_port" )] pub qgs_port: u32, + + /// selinux_label defines SELinux label for the guest + #[serde(default)] + pub selinux_label: Option, } fn default_qgs_port() -> u32 { @@ -1221,12 +1225,20 @@ pub struct Hypervisor { /// Disable applying SELinux on the container process. #[serde(default = "yes")] pub disable_guest_selinux: bool, + + /// Disable applying SELinux on the VMM process. + #[serde(default = "no")] + pub disable_selinux: bool, } fn yes() -> bool { true } +fn no() -> bool { + false +} + impl Hypervisor { /// Validate path of hypervisor executable. pub fn validate_hypervisor_path>(&self, path: P) -> Result<()> { From ff369c1b1acc4f3eb6a96f719c44f1f37ee74442 Mon Sep 17 00:00:00 2001 From: Caspian443 Date: Tue, 22 Jul 2025 16:21:39 +0800 Subject: [PATCH 3/7] runtime-rs: sandbox: read selinux_label from OCI spec and pass to preparevm Extract the selinux_label field from the OCI spec during sandbox setup and forward it to the prepare_vm API so the VMM process receives the appropriate SELinux context label. - sandbox: read selinux_label from OCI spec - prepare_vm: accept and apply selinux_label parameter Signed-off-by: Caspian443 --- .../crates/runtimes/virt_container/src/sandbox.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/runtime-rs/crates/runtimes/virt_container/src/sandbox.rs b/src/runtime-rs/crates/runtimes/virt_container/src/sandbox.rs index 801718e734..3d55a81f84 100644 --- a/src/runtime-rs/crates/runtimes/virt_container/src/sandbox.rs +++ b/src/runtime-rs/crates/runtimes/virt_container/src/sandbox.rs @@ -38,6 +38,7 @@ use kata_types::config::hypervisor::Hypervisor as HypervisorConfig; use kata_types::config::hypervisor::HYPERVISOR_NAME_CH; use kata_types::config::TomlConfig; use kata_types::initdata::{calculate_initdata_digest, ProtectedPlatform}; +use kata_sys_util::spec::load_oci_spec; use oci_spec::runtime as oci; use persist::{self, sandbox_persist::Persist}; use protobuf::SpecialFields; @@ -510,12 +511,20 @@ impl Sandbox for VirtSandbox { warn!(sl!(), "sandbox is started"); return Ok(()); } + let selinux_label = load_oci_spec() + .ok() + .and_then(|spec| { + spec.process() + .as_ref() + .and_then(|process| process.selinux_label().clone()) + }); self.hypervisor .prepare_vm( id, sandbox_config.network_env.netns.clone(), &sandbox_config.annotations, + selinux_label, ) .await .context("prepare vm")?; From cdb7fe831516e510e9b9f7ca2925ca475ec9ea84 Mon Sep 17 00:00:00 2001 From: Caspian443 Date: Tue, 22 Jul 2025 16:22:47 +0800 Subject: [PATCH 4/7] runtime-rs: sandbox: read selinux_label from OCI spec and pass to preparevm Extract the selinux_label field from the OCI spec during sandbox setup and forward it to the prepare_vm API so the VMM process receives the appropriate SELinux context label. - sandbox: read selinux_label from OCI spec - prepare_vm: accept and apply selinux_label parameter Signed-off-by: Caspian443 --- src/runtime-rs/crates/hypervisor/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/runtime-rs/crates/hypervisor/src/lib.rs b/src/runtime-rs/crates/hypervisor/src/lib.rs index c22840bb51..d1ac432c15 100644 --- a/src/runtime-rs/crates/hypervisor/src/lib.rs +++ b/src/runtime-rs/crates/hypervisor/src/lib.rs @@ -104,6 +104,7 @@ pub trait Hypervisor: std::fmt::Debug + Send + Sync { id: &str, netns: Option, annotations: &HashMap, + selinux_label: Option, ) -> Result<()>; async fn start_vm(&self, timeout: i32) -> Result<()>; async fn stop_vm(&self) -> Result<()>; From f18528a1073f64b25886416018342961f9ffd521 Mon Sep 17 00:00:00 2001 From: Caspian443 Date: Tue, 22 Jul 2025 16:24:07 +0800 Subject: [PATCH 5/7] runtime-rs: hypervisor: set SELinux label for virtiofsd process I set the SELinux label for containerd, and virtiofsd successfully inherited this label. Fixes #9866 Signed-off-by: Caspian443 --- .../crates/hypervisor/src/qemu/inner.rs | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/runtime-rs/crates/hypervisor/src/qemu/inner.rs b/src/runtime-rs/crates/hypervisor/src/qemu/inner.rs index b781de8177..600fec4585 100644 --- a/src/runtime-rs/crates/hypervisor/src/qemu/inner.rs +++ b/src/runtime-rs/crates/hypervisor/src/qemu/inner.rs @@ -7,8 +7,8 @@ use super::cmdline_generator::{get_network_device, QemuCmdLine, QMP_SOCKET_FILE} use super::qmp::Qmp; use crate::device::topology::PCIePort; use crate::{ - device::driver::ProtectionDeviceConfig, hypervisor_persist::HypervisorState, HypervisorConfig, - MemoryConfig, VcpuThreadIds, VsockDevice, HYPERVISOR_QEMU, + device::driver::ProtectionDeviceConfig, hypervisor_persist::HypervisorState, selinux, + HypervisorConfig, MemoryConfig, VcpuThreadIds, VsockDevice, HYPERVISOR_QEMU, }; use crate::utils::{bytes_to_megs, enter_netns, megs_to_bytes}; @@ -63,11 +63,24 @@ impl QemuInner { } } - pub(crate) async fn prepare_vm(&mut self, id: &str, netns: Option) -> Result<()> { + pub(crate) async fn prepare_vm( + &mut self, + id: &str, + netns: Option, + selinux_label: Option, + ) -> Result<()> { info!(sl!(), "Preparing QEMU VM"); self.id = id.to_string(); self.netns = netns; + if !self.hypervisor_config().disable_selinux { + if let Some(label) = selinux_label.as_ref() { + self.config.security_info.selinux_label = Some(label.to_string()); + selinux::set_exec_label(&selinux_label.unwrap()) + .context("failed to set SELinux process label ")?; + } + } + let vm_path = [KATA_PATH, self.id.as_str()].join("/"); std::fs::create_dir_all(vm_path)?; From 94cb80789186a3f63ba1ffb114ebdbaee0808395 Mon Sep 17 00:00:00 2001 From: Caspian443 Date: Tue, 22 Jul 2025 16:25:42 +0800 Subject: [PATCH 6/7] runtime-rs: hypervisor: set SELinux label for qemu I set the SELinux label for qemu. Fixes #9866 Signed-off-by: Caspian443 --- .../crates/hypervisor/src/qemu/inner.rs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/runtime-rs/crates/hypervisor/src/qemu/inner.rs b/src/runtime-rs/crates/hypervisor/src/qemu/inner.rs index 600fec4585..7efae6379c 100644 --- a/src/runtime-rs/crates/hypervisor/src/qemu/inner.rs +++ b/src/runtime-rs/crates/hypervisor/src/qemu/inner.rs @@ -207,11 +207,23 @@ impl QemuInner { info!(sl!(), "qemu cmd: {:?}", command); - // we need move the qemu process into Network Namespace. + // we need move the qemu process into Network Namespace and set SELinux label. unsafe { + let selinux_label = self.config.security_info.selinux_label.clone(); let _pre_exec = command.pre_exec(move || { let _ = enter_netns(&netns); - + if let Some(label) = selinux_label.as_ref() { + if let Err(e) = selinux::set_exec_label(&label) { + error!(sl!(), "Failed to set SELinux label in child process: {}", e); + // Don't return error here to avoid breaking the process startup + // Log the error and continue + } else { + info!( + sl!(), + "Successfully set SELinux label in child process: {}", &label + ); + } + } Ok(()) }); } From 3d326df62848f34e8627078fef21ded2d4640974 Mon Sep 17 00:00:00 2001 From: Caspian443 Date: Tue, 22 Jul 2025 16:26:27 +0800 Subject: [PATCH 7/7] runtime-rs: hypervisor: add selinux_label parameter stubs to CH, Dragonball, Firecracker, and Remote Introduce a parameter stub for the API in the following hypervisor backends. The actual SELinux enforcement logic is not implemented yet and will be added in subsequent commits. - ch (): add stub - dragonball (): add stub - firecracker (): add stub - remote (): add stub Signed-off-by: Caspian443 --- src/runtime-rs/crates/hypervisor/src/ch/inner_hypervisor.rs | 2 +- src/runtime-rs/crates/hypervisor/src/ch/mod.rs | 3 ++- .../crates/hypervisor/src/dragonball/inner_hypervisor.rs | 2 +- src/runtime-rs/crates/hypervisor/src/dragonball/mod.rs | 3 ++- .../crates/hypervisor/src/firecracker/inner_hypervisor.rs | 2 +- src/runtime-rs/crates/hypervisor/src/firecracker/mod.rs | 3 ++- src/runtime-rs/crates/hypervisor/src/qemu/mod.rs | 3 ++- src/runtime-rs/crates/hypervisor/src/remote/inner.rs | 1 + src/runtime-rs/crates/hypervisor/src/remote/mod.rs | 3 ++- 9 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/runtime-rs/crates/hypervisor/src/ch/inner_hypervisor.rs b/src/runtime-rs/crates/hypervisor/src/ch/inner_hypervisor.rs index 6f114718dc..1cf03fa267 100644 --- a/src/runtime-rs/crates/hypervisor/src/ch/inner_hypervisor.rs +++ b/src/runtime-rs/crates/hypervisor/src/ch/inner_hypervisor.rs @@ -531,7 +531,7 @@ impl CloudHypervisorInner { Ok(()) } - pub(crate) async fn prepare_vm(&mut self, id: &str, netns: Option) -> Result<()> { + pub(crate) async fn prepare_vm(&mut self, id: &str, netns: Option, _selinux_label: Option) -> Result<()> { self.id = id.to_string(); self.state = VmmState::NotReady; diff --git a/src/runtime-rs/crates/hypervisor/src/ch/mod.rs b/src/runtime-rs/crates/hypervisor/src/ch/mod.rs index 9381569af8..270ce16719 100644 --- a/src/runtime-rs/crates/hypervisor/src/ch/mod.rs +++ b/src/runtime-rs/crates/hypervisor/src/ch/mod.rs @@ -65,9 +65,10 @@ impl Hypervisor for CloudHypervisor { id: &str, netns: Option, _annotations: &HashMap, + selinux_label: Option, ) -> Result<()> { let mut inner = self.inner.write().await; - inner.prepare_vm(id, netns).await + inner.prepare_vm(id, netns, selinux_label).await } async fn start_vm(&self, timeout: i32) -> Result<()> { diff --git a/src/runtime-rs/crates/hypervisor/src/dragonball/inner_hypervisor.rs b/src/runtime-rs/crates/hypervisor/src/dragonball/inner_hypervisor.rs index 14b0d02198..fbc3cbacc9 100644 --- a/src/runtime-rs/crates/hypervisor/src/dragonball/inner_hypervisor.rs +++ b/src/runtime-rs/crates/hypervisor/src/dragonball/inner_hypervisor.rs @@ -19,7 +19,7 @@ use crate::{ }; impl DragonballInner { - pub(crate) async fn prepare_vm(&mut self, id: &str, netns: Option) -> Result<()> { + pub(crate) async fn prepare_vm(&mut self, id: &str, netns: Option, _selinux_label: Option) -> Result<()> { self.id = id.to_string(); self.state = VmmState::NotReady; diff --git a/src/runtime-rs/crates/hypervisor/src/dragonball/mod.rs b/src/runtime-rs/crates/hypervisor/src/dragonball/mod.rs index 4b85420ad2..8bfb539b2c 100644 --- a/src/runtime-rs/crates/hypervisor/src/dragonball/mod.rs +++ b/src/runtime-rs/crates/hypervisor/src/dragonball/mod.rs @@ -75,9 +75,10 @@ impl Hypervisor for Dragonball { id: &str, netns: Option, _annotations: &HashMap, + selinux_label: Option, ) -> Result<()> { let mut inner = self.inner.write().await; - inner.prepare_vm(id, netns).await + inner.prepare_vm(id, netns, selinux_label).await } #[instrument] diff --git a/src/runtime-rs/crates/hypervisor/src/firecracker/inner_hypervisor.rs b/src/runtime-rs/crates/hypervisor/src/firecracker/inner_hypervisor.rs index bd38385a51..fa96fd3ba8 100644 --- a/src/runtime-rs/crates/hypervisor/src/firecracker/inner_hypervisor.rs +++ b/src/runtime-rs/crates/hypervisor/src/firecracker/inner_hypervisor.rs @@ -19,7 +19,7 @@ pub const ROOT: &str = "root"; const HYBRID_VSOCK_SCHEME: &str = "hvsock"; impl FcInner { - pub(crate) async fn prepare_vm(&mut self, id: &str, _netns: Option) -> Result<()> { + pub(crate) async fn prepare_vm(&mut self, id: &str, _netns: Option, _selinux_label: Option) -> Result<()> { debug!(sl(), "Preparing Firecracker"); self.id = id.to_string(); diff --git a/src/runtime-rs/crates/hypervisor/src/firecracker/mod.rs b/src/runtime-rs/crates/hypervisor/src/firecracker/mod.rs index 7c92a87075..05fd0c57cb 100644 --- a/src/runtime-rs/crates/hypervisor/src/firecracker/mod.rs +++ b/src/runtime-rs/crates/hypervisor/src/firecracker/mod.rs @@ -64,9 +64,10 @@ impl Hypervisor for Firecracker { id: &str, netns: Option, _annotations: &HashMap, + selinux_label: Option, ) -> Result<()> { let mut inner = self.inner.write().await; - inner.prepare_vm(id, netns).await + inner.prepare_vm(id, netns, selinux_label).await } async fn start_vm(&self, timeout: i32) -> Result<()> { diff --git a/src/runtime-rs/crates/hypervisor/src/qemu/mod.rs b/src/runtime-rs/crates/hypervisor/src/qemu/mod.rs index c012aec1ee..9c4bb016f3 100644 --- a/src/runtime-rs/crates/hypervisor/src/qemu/mod.rs +++ b/src/runtime-rs/crates/hypervisor/src/qemu/mod.rs @@ -58,9 +58,10 @@ impl Hypervisor for Qemu { id: &str, netns: Option, _annotations: &HashMap, + selinux_label: Option, ) -> Result<()> { let mut inner = self.inner.write().await; - inner.prepare_vm(id, netns).await + inner.prepare_vm(id, netns, selinux_label).await } async fn start_vm(&self, timeout: i32) -> Result<()> { diff --git a/src/runtime-rs/crates/hypervisor/src/remote/inner.rs b/src/runtime-rs/crates/hypervisor/src/remote/inner.rs index 47eec7e2b3..5627fdfe6e 100644 --- a/src/runtime-rs/crates/hypervisor/src/remote/inner.rs +++ b/src/runtime-rs/crates/hypervisor/src/remote/inner.rs @@ -141,6 +141,7 @@ impl RemoteInner { id: &str, netns: Option, annotations: &HashMap, + _selinux_label: Option, ) -> Result<()> { info!(sl!(), "Preparing REMOTE VM"); self.id = id.to_string(); diff --git a/src/runtime-rs/crates/hypervisor/src/remote/mod.rs b/src/runtime-rs/crates/hypervisor/src/remote/mod.rs index 233aacb3ec..00ca44ff4c 100644 --- a/src/runtime-rs/crates/hypervisor/src/remote/mod.rs +++ b/src/runtime-rs/crates/hypervisor/src/remote/mod.rs @@ -48,9 +48,10 @@ impl Hypervisor for Remote { id: &str, netns: Option, annotations: &HashMap, + _selinux_label: Option, ) -> Result<()> { let mut inner = self.inner.write().await; - inner.prepare_vm(id, netns, annotations).await + inner.prepare_vm(id, netns, annotations, _selinux_label).await } async fn start_vm(&self, timeout: i32) -> Result<()> {