This commit is contained in:
Caspian Chen 2025-08-12 01:49:56 +08:00 committed by GitHub
commit 1fde5bdc90
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 138 additions and 13 deletions

View File

@ -934,6 +934,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<String>,
}
fn default_qgs_port() -> u32 {
@ -1251,12 +1255,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<P: AsRef<Path>>(&self, path: P) -> Result<()> {

View File

@ -531,7 +531,7 @@ impl CloudHypervisorInner {
Ok(())
}
pub(crate) async fn prepare_vm(&mut self, id: &str, netns: Option<String>) -> Result<()> {
pub(crate) async fn prepare_vm(&mut self, id: &str, netns: Option<String>, _selinux_label: Option<String>) -> Result<()> {
self.id = id.to_string();
self.state = VmmState::NotReady;

View File

@ -65,9 +65,10 @@ impl Hypervisor for CloudHypervisor {
id: &str,
netns: Option<String>,
_annotations: &HashMap<String, String>,
selinux_label: Option<String>,
) -> 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<()> {

View File

@ -19,7 +19,7 @@ use crate::{
};
impl DragonballInner {
pub(crate) async fn prepare_vm(&mut self, id: &str, netns: Option<String>) -> Result<()> {
pub(crate) async fn prepare_vm(&mut self, id: &str, netns: Option<String>, _selinux_label: Option<String>) -> Result<()> {
self.id = id.to_string();
self.state = VmmState::NotReady;

View File

@ -75,9 +75,10 @@ impl Hypervisor for Dragonball {
id: &str,
netns: Option<String>,
_annotations: &HashMap<String, String>,
selinux_label: Option<String>,
) -> Result<()> {
let mut inner = self.inner.write().await;
inner.prepare_vm(id, netns).await
inner.prepare_vm(id, netns, selinux_label).await
}
#[instrument]

View File

@ -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<String>) -> Result<()> {
pub(crate) async fn prepare_vm(&mut self, id: &str, _netns: Option<String>, _selinux_label: Option<String>) -> Result<()> {
debug!(sl(), "Preparing Firecracker");
self.id = id.to_string();

View File

@ -64,9 +64,10 @@ impl Hypervisor for Firecracker {
id: &str,
netns: Option<String>,
_annotations: &HashMap<String, String>,
selinux_label: Option<String>,
) -> 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<()> {

View File

@ -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;
@ -103,6 +104,7 @@ pub trait Hypervisor: std::fmt::Debug + Send + Sync {
id: &str,
netns: Option<String>,
annotations: &HashMap<String, String>,
selinux_label: Option<String>,
) -> Result<()>;
async fn start_vm(&self, timeout: i32) -> Result<()>;
async fn stop_vm(&self) -> Result<()>;

View File

@ -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};
@ -62,11 +62,24 @@ impl QemuInner {
}
}
pub(crate) async fn prepare_vm(&mut self, id: &str, netns: Option<String>) -> Result<()> {
pub(crate) async fn prepare_vm(
&mut self,
id: &str,
netns: Option<String>,
selinux_label: Option<String>,
) -> 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)?;
@ -193,11 +206,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(())
});
}

View File

@ -58,9 +58,10 @@ impl Hypervisor for Qemu {
id: &str,
netns: Option<String>,
_annotations: &HashMap<String, String>,
selinux_label: Option<String>,
) -> 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<()> {

View File

@ -146,6 +146,7 @@ impl RemoteInner {
id: &str,
netns: Option<String>,
annotations: &HashMap<String, String>,
_selinux_label: Option<String>,
) -> Result<()> {
info!(sl!(), "Preparing REMOTE VM");
self.id = id.to_string();

View File

@ -48,9 +48,10 @@ impl Hypervisor for Remote {
id: &str,
netns: Option<String>,
annotations: &HashMap<String, String>,
_selinux_label: Option<String>,
) -> 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<()> {

View File

@ -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);
}
}
}

View File

@ -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")?;