mirror of
https://github.com/cloud-hypervisor/cloud-hypervisor.git
synced 2024-12-22 13:45:20 +00:00
Revert "vmm: make landlock configs VMM-level config"
This reverts commit 94929889ac
.
This revert moves landlock config back to VMConfig.
Signed-off-by: Praveen K Paladugu <prapal@linux.microsoft.com>
This commit is contained in:
parent
513973873c
commit
d2f0e8aebb
@ -30,7 +30,10 @@ Linux kernel confirms Landlock support with above message in dmesg.
|
||||
## Implementation Details
|
||||
|
||||
To enable Landlock, Cloud-Hypervisor process needs the full list of files it
|
||||
needs to access over its lifetime. Landlock is enabled in the `vm_create` stage.
|
||||
needs to access over its lifetime. Most of these files are received as VM
|
||||
Configuration (`struct VmConfig`). Landlock is enabled in `vm_create` stage, as
|
||||
this is the earliest stage in guest boot sequence which has access to guest's
|
||||
VM Configuration.
|
||||
|
||||
## Enable Landlock
|
||||
|
||||
|
@ -190,6 +190,8 @@ impl RequestHandler for StubApiRequestHandler {
|
||||
platform: None,
|
||||
tpm: None,
|
||||
preserved_fds: None,
|
||||
landlock_enable: false,
|
||||
landlock_config: None,
|
||||
})),
|
||||
state: VmState::Running,
|
||||
memory_actual_size: 0,
|
||||
|
31
src/main.rs
31
src/main.rs
@ -285,14 +285,14 @@ fn create_app(default_vcpus: String, default_memory: String, default_rng: String
|
||||
)
|
||||
.action(ArgAction::SetTrue)
|
||||
.default_value("false")
|
||||
.group("vmm-config"),
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("landlock-rules")
|
||||
.long("landlock-rules")
|
||||
.help(config::LandlockConfig::SYNTAX)
|
||||
.num_args(1..)
|
||||
.group("vmm-config"),
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("net")
|
||||
@ -657,31 +657,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
let vm_debug_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateDebugEventFd)?;
|
||||
|
||||
let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateExitEventFd)?;
|
||||
|
||||
let landlock_enable = cmd_arguments.get_flag("landlock");
|
||||
let landlock_config_str_vec: Option<Vec<&str>> = cmd_arguments
|
||||
.get_many::<String>("landlock-rules")
|
||||
.map(|x| x.map(|y| y as &str).collect());
|
||||
|
||||
let landlock_config = if let Some(str_vec) = landlock_config_str_vec {
|
||||
Some(
|
||||
str_vec
|
||||
.into_iter()
|
||||
.map(config::LandlockConfig::parse)
|
||||
.collect::<config::Result<Vec<config::LandlockConfig>>>()
|
||||
.map_err(Error::ParsingConfig)?,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
if let Some(lc) = landlock_config.as_ref() {
|
||||
for c in lc.iter() {
|
||||
c.validate()
|
||||
.map_err(config::Error::Validation)
|
||||
.map_err(Error::ParsingConfig)?;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut event_monitor = cmd_arguments
|
||||
@ -779,7 +755,6 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
||||
&seccomp_action,
|
||||
hypervisor,
|
||||
landlock_enable,
|
||||
landlock_config,
|
||||
)
|
||||
.map_err(Error::StartVmmThread)?;
|
||||
|
||||
@ -1090,6 +1065,8 @@ mod unit_tests {
|
||||
platform: None,
|
||||
tpm: None,
|
||||
preserved_fds: None,
|
||||
landlock_enable: false,
|
||||
landlock_config: None,
|
||||
};
|
||||
|
||||
assert_eq!(expected_vm_config, result_vm_config);
|
||||
|
@ -496,6 +496,8 @@ pub struct VmParams<'a> {
|
||||
pub igvm: Option<&'a str>,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
pub host_data: Option<&'a str>,
|
||||
pub landlock_enable: bool,
|
||||
pub landlock_config: Option<Vec<&'a str>>,
|
||||
}
|
||||
|
||||
impl<'a> VmParams<'a> {
|
||||
@ -561,6 +563,10 @@ impl<'a> VmParams<'a> {
|
||||
let igvm = args.get_one::<String>("igvm").map(|x| x as &str);
|
||||
#[cfg(feature = "sev_snp")]
|
||||
let host_data = args.get_one::<String>("host-data").map(|x| x as &str);
|
||||
let landlock_enable = args.get_flag("landlock");
|
||||
let landlock_config: Option<Vec<&str>> = args
|
||||
.get_many::<String>("landlock-rules")
|
||||
.map(|x| x.map(|y| y as &str).collect());
|
||||
|
||||
VmParams {
|
||||
cpus,
|
||||
@ -599,6 +605,8 @@ impl<'a> VmParams<'a> {
|
||||
igvm,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
host_data,
|
||||
landlock_enable,
|
||||
landlock_config,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2326,12 +2334,6 @@ impl TpmConfig {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct LandlockConfig {
|
||||
pub path: PathBuf,
|
||||
pub access: String,
|
||||
}
|
||||
|
||||
impl LandlockConfig {
|
||||
pub const SYNTAX: &'static str = "Landlock parameters \
|
||||
\"path=<path/to/{file/dir}>,access=[rw]\"";
|
||||
@ -2723,6 +2725,12 @@ impl VmConfig {
|
||||
.map(|p| p.iommu_segments.is_some())
|
||||
.unwrap_or_default();
|
||||
|
||||
if let Some(landlock_configs) = &self.landlock_config {
|
||||
for landlock_config in landlock_configs {
|
||||
landlock_config.validate()?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(id_list)
|
||||
}
|
||||
|
||||
@ -2893,6 +2901,16 @@ impl VmConfig {
|
||||
#[cfg(feature = "guest_debug")]
|
||||
let gdb = vm_params.gdb;
|
||||
|
||||
let mut landlock_config: Option<Vec<LandlockConfig>> = None;
|
||||
if let Some(ll_config) = vm_params.landlock_config {
|
||||
landlock_config = Some(
|
||||
ll_config
|
||||
.iter()
|
||||
.map(|rule| LandlockConfig::parse(rule))
|
||||
.collect::<Result<Vec<LandlockConfig>>>()?,
|
||||
);
|
||||
}
|
||||
|
||||
let mut config = VmConfig {
|
||||
cpus: CpusConfig::parse(vm_params.cpus)?,
|
||||
memory: MemoryConfig::parse(vm_params.memory, vm_params.memory_zones)?,
|
||||
@ -2924,6 +2942,8 @@ impl VmConfig {
|
||||
platform,
|
||||
tpm,
|
||||
preserved_fds: None,
|
||||
landlock_enable: vm_params.landlock_enable,
|
||||
landlock_config,
|
||||
};
|
||||
config.validate().map_err(Error::Validation)?;
|
||||
Ok(config)
|
||||
@ -3050,6 +3070,7 @@ impl Clone for VmConfig {
|
||||
.as_ref()
|
||||
// SAFETY: FFI call with valid FDs
|
||||
.map(|fds| fds.iter().map(|fd| unsafe { libc::dup(*fd) }).collect()),
|
||||
landlock_config: self.landlock_config.clone(),
|
||||
..*self
|
||||
}
|
||||
}
|
||||
@ -3848,6 +3869,8 @@ mod tests {
|
||||
..net_fixture()
|
||||
},
|
||||
]),
|
||||
landlock_enable: false,
|
||||
landlock_config: None,
|
||||
};
|
||||
|
||||
let valid_config = RestoreConfig {
|
||||
@ -4036,6 +4059,8 @@ mod tests {
|
||||
platform: None,
|
||||
tpm: None,
|
||||
preserved_fds: None,
|
||||
landlock_enable: false,
|
||||
landlock_config: None,
|
||||
};
|
||||
|
||||
assert!(valid_config.validate().is_ok());
|
||||
|
298
vmm/src/lib.rs
298
vmm/src/lib.rs
@ -12,11 +12,8 @@ use crate::api::{
|
||||
ApiRequest, ApiResponse, RequestHandler, VmInfoResponse, VmReceiveMigrationData,
|
||||
VmSendMigrationData, VmmPingResponse,
|
||||
};
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
use crate::config::DebugConsoleConfig;
|
||||
use crate::config::{
|
||||
add_to_config, ConsoleConfig, DeviceConfig, DiskConfig, FsConfig, LandlockConfig,
|
||||
MemoryZoneConfig, NetConfig, PayloadConfig, PmemConfig, RestoreConfig, RngConfig, TpmConfig,
|
||||
add_to_config, DeviceConfig, DiskConfig, FsConfig, NetConfig, PmemConfig, RestoreConfig,
|
||||
UserDeviceConfig, VdpaConfig, VmConfig, VsockConfig,
|
||||
};
|
||||
#[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
|
||||
@ -42,7 +39,6 @@ use serde::ser::{SerializeStruct, Serializer};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use signal_hook::iterator::{Handle, Signals};
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{stdout, Read, Write};
|
||||
@ -417,7 +413,6 @@ pub fn start_vmm_thread(
|
||||
seccomp_action: &SeccompAction,
|
||||
hypervisor: Arc<dyn hypervisor::Hypervisor>,
|
||||
landlock_enable: bool,
|
||||
landlock_config: Option<Vec<LandlockConfig>>,
|
||||
) -> Result<VmmThreadHandle> {
|
||||
#[cfg(feature = "guest_debug")]
|
||||
let gdb_hw_breakpoints = hypervisor.get_guest_debug_hw_bps();
|
||||
@ -456,8 +451,6 @@ pub fn start_vmm_thread(
|
||||
vmm_seccomp_action,
|
||||
hypervisor,
|
||||
exit_event,
|
||||
landlock_enable,
|
||||
landlock_config,
|
||||
)?;
|
||||
|
||||
vmm.setup_signal_handler(landlock_enable)?;
|
||||
@ -585,8 +578,6 @@ pub struct Vmm {
|
||||
original_termios_opt: Arc<Mutex<Option<termios>>>,
|
||||
console_resize_pipe: Option<File>,
|
||||
console_info: Option<ConsoleInfo>,
|
||||
landlock_enable: bool,
|
||||
landlock_config: Option<Vec<LandlockConfig>>,
|
||||
}
|
||||
|
||||
impl Vmm {
|
||||
@ -693,8 +684,6 @@ impl Vmm {
|
||||
seccomp_action: SeccompAction,
|
||||
hypervisor: Arc<dyn hypervisor::Hypervisor>,
|
||||
exit_evt: EventFd,
|
||||
landlock_enable: bool,
|
||||
landlock_config: Option<Vec<LandlockConfig>>,
|
||||
) -> Result<Self> {
|
||||
let mut epoll = EpollContext::new().map_err(Error::Epoll)?;
|
||||
let reset_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?;
|
||||
@ -741,8 +730,6 @@ impl Vmm {
|
||||
original_termios_opt: Arc::new(Mutex::new(None)),
|
||||
console_resize_pipe: None,
|
||||
console_info: None,
|
||||
landlock_enable,
|
||||
landlock_config,
|
||||
})
|
||||
}
|
||||
|
||||
@ -779,12 +766,15 @@ impl Vmm {
|
||||
MigratableError::MigrateReceive(anyhow!("Error creating console devices: {:?}", e))
|
||||
})?);
|
||||
|
||||
if self.landlock_enable {
|
||||
apply_landlock(
|
||||
&self.vm_config.as_ref().unwrap().clone(),
|
||||
&self.landlock_config,
|
||||
)
|
||||
.map_err(|e| {
|
||||
if self
|
||||
.vm_config
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.landlock_enable
|
||||
{
|
||||
apply_landlock(self.vm_config.as_ref().unwrap().clone()).map_err(|e| {
|
||||
MigratableError::MigrateReceive(anyhow!("Error applying landlock: {:?}", e))
|
||||
})?;
|
||||
}
|
||||
@ -1243,236 +1233,8 @@ impl Vmm {
|
||||
}
|
||||
}
|
||||
|
||||
pub type LandlockResult<T> = result::Result<T, LandlockError>;
|
||||
/// Trait to apply Landlock on VmConfig elements
|
||||
pub(crate) trait ApplyLandlock {
|
||||
/// Apply Landlock rules to file paths
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>;
|
||||
}
|
||||
|
||||
impl ApplyLandlock for MemoryZoneConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
if let Some(file) = &self.file {
|
||||
landlock.add_rule_with_access(file.to_path_buf(), "rw")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplyLandlock for DiskConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
if let Some(path) = &self.path {
|
||||
landlock.add_rule_with_access(path.to_path_buf(), "rw")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplyLandlock for RngConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
// Rng Path only need read access
|
||||
landlock.add_rule_with_access(self.src.to_path_buf(), "r")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplyLandlock for FsConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplyLandlock for PmemConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
landlock.add_rule_with_access(self.file.to_path_buf(), "rw")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplyLandlock for ConsoleConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
if let Some(file) = &self.file {
|
||||
landlock.add_rule_with_access(file.to_path_buf(), "rw")?;
|
||||
}
|
||||
if let Some(socket) = &self.socket {
|
||||
landlock.add_rule_with_access(socket.to_path_buf(), "rw")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
impl ApplyLandlock for DebugConsoleConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
if let Some(file) = &self.file {
|
||||
landlock.add_rule_with_access(file.to_path_buf(), "rw")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplyLandlock for DeviceConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
let device_path = fs::read_link(self.path.as_path()).map_err(LandlockError::OpenPath)?;
|
||||
let iommu_group = device_path.file_name();
|
||||
let iommu_group_str = iommu_group
|
||||
.ok_or(LandlockError::InvalidPath)?
|
||||
.to_str()
|
||||
.ok_or(LandlockError::InvalidPath)?;
|
||||
|
||||
let vfio_group_path = "/dev/vfio/".to_owned() + iommu_group_str;
|
||||
landlock.add_rule_with_access(vfio_group_path.into(), "rw")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplyLandlock for UserDeviceConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplyLandlock for VdpaConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
landlock.add_rule_with_access(self.path.to_path_buf(), "rw")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplyLandlock for VsockConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplyLandlock for PayloadConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
// Payload only needs read access
|
||||
if let Some(firmware) = &self.firmware {
|
||||
landlock.add_rule_with_access(firmware.to_path_buf(), "r")?;
|
||||
}
|
||||
|
||||
if let Some(kernel) = &self.kernel {
|
||||
landlock.add_rule_with_access(kernel.to_path_buf(), "r")?;
|
||||
}
|
||||
|
||||
if let Some(initramfs) = &self.initramfs {
|
||||
landlock.add_rule_with_access(initramfs.to_path_buf(), "r")?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "igvm")]
|
||||
if let Some(igvm) = &self.igvm {
|
||||
landlock.add_rule_with_access(igvm.to_path_buf(), "r")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplyLandlock for TpmConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplyLandlock for LandlockConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
landlock.add_rule_with_access(self.path.to_path_buf(), self.access.clone().as_str())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn apply_landlock(
|
||||
vm_config: &Arc<Mutex<VmConfig>>,
|
||||
landlock_config: &Option<Vec<LandlockConfig>>,
|
||||
) -> LandlockResult<()> {
|
||||
let vm_config = vm_config.lock().unwrap();
|
||||
let mut landlock = Landlock::new()?;
|
||||
|
||||
if let Some(mem_zones) = &vm_config.memory.zones {
|
||||
for zone in mem_zones.iter() {
|
||||
zone.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
let disks = &vm_config.disks;
|
||||
if let Some(disks) = disks {
|
||||
for disk in disks.iter() {
|
||||
disk.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
vm_config.rng.apply_landlock(&mut landlock)?;
|
||||
|
||||
if let Some(fs_configs) = &vm_config.fs {
|
||||
for fs_config in fs_configs.iter() {
|
||||
fs_config.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(pmem_configs) = &vm_config.pmem {
|
||||
for pmem_config in pmem_configs.iter() {
|
||||
pmem_config.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
vm_config.console.apply_landlock(&mut landlock)?;
|
||||
vm_config.serial.apply_landlock(&mut landlock)?;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
vm_config.debug_console.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
|
||||
if let Some(devices) = &vm_config.devices {
|
||||
landlock.add_rule_with_access("/dev/vfio/vfio".into(), "rw")?;
|
||||
|
||||
for device in devices.iter() {
|
||||
device.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(user_devices) = &vm_config.user_devices {
|
||||
for user_devices in user_devices.iter() {
|
||||
user_devices.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(vdpa_configs) = &vm_config.vdpa {
|
||||
for vdpa_config in vdpa_configs.iter() {
|
||||
vdpa_config.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(vsock_config) = &vm_config.vsock {
|
||||
vsock_config.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
|
||||
if let Some(payload) = &vm_config.payload {
|
||||
payload.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
|
||||
if let Some(tpm_config) = &vm_config.tpm {
|
||||
tpm_config.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
|
||||
if vm_config.net.is_some() {
|
||||
landlock.add_rule_with_access("/dev/net/tun".into(), "rw")?;
|
||||
}
|
||||
|
||||
if let Some(landlock_configs) = landlock_config {
|
||||
for landlock_config in landlock_configs.iter() {
|
||||
landlock_config.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
landlock.restrict_self()?;
|
||||
|
||||
fn apply_landlock(vm_config: Arc<Mutex<VmConfig>>) -> result::Result<(), LandlockError> {
|
||||
vm_config.lock().unwrap().apply_landlock()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -1485,12 +1247,16 @@ impl RequestHandler for Vmm {
|
||||
self.console_info =
|
||||
Some(pre_create_console_devices(self).map_err(VmError::CreateConsoleDevices)?);
|
||||
|
||||
if self.landlock_enable {
|
||||
apply_landlock(
|
||||
&self.vm_config.as_ref().unwrap().clone(),
|
||||
&self.landlock_config,
|
||||
)
|
||||
.map_err(VmError::ApplyLandlock)?;
|
||||
if self
|
||||
.vm_config
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.landlock_enable
|
||||
{
|
||||
apply_landlock(self.vm_config.as_ref().unwrap().clone())
|
||||
.map_err(VmError::ApplyLandlock)?;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
@ -1675,12 +1441,16 @@ impl RequestHandler for Vmm {
|
||||
)?;
|
||||
self.vm = Some(vm);
|
||||
|
||||
if self.landlock_enable {
|
||||
apply_landlock(
|
||||
&self.vm_config.as_ref().unwrap().clone(),
|
||||
&self.landlock_config,
|
||||
)
|
||||
.map_err(VmError::ApplyLandlock)?;
|
||||
if self
|
||||
.vm_config
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.landlock_enable
|
||||
{
|
||||
apply_landlock(self.vm_config.as_ref().unwrap().clone())
|
||||
.map_err(VmError::ApplyLandlock)?;
|
||||
}
|
||||
|
||||
// Now we can restore the rest of the VM.
|
||||
@ -2394,8 +2164,6 @@ mod unit_tests {
|
||||
SeccompAction::Allow,
|
||||
hypervisor::new().unwrap(),
|
||||
EventFd::new(EFD_NONBLOCK).unwrap(),
|
||||
false,
|
||||
None,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
@ -2474,6 +2242,8 @@ mod unit_tests {
|
||||
platform: None,
|
||||
tpm: None,
|
||||
preserved_fds: None,
|
||||
landlock_enable: false,
|
||||
landlock_config: None,
|
||||
}))
|
||||
}
|
||||
|
||||
|
@ -2,11 +2,20 @@
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
use crate::{landlock::LandlockError, Landlock};
|
||||
use net_util::MacAddr;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{net::Ipv4Addr, path::PathBuf};
|
||||
use std::{fs, net::Ipv4Addr, path::PathBuf, result};
|
||||
use virtio_devices::RateLimiterConfig;
|
||||
|
||||
pub type LandlockResult<T> = result::Result<T, LandlockError>;
|
||||
|
||||
/// Trait to apply Landlock on VmConfig elements
|
||||
pub(crate) trait ApplyLandlock {
|
||||
/// Apply Landlock rules to file paths
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct CpuAffinity {
|
||||
pub vcpu: u8,
|
||||
@ -132,6 +141,15 @@ pub struct MemoryZoneConfig {
|
||||
pub prefault: bool,
|
||||
}
|
||||
|
||||
impl ApplyLandlock for MemoryZoneConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
if let Some(file) = &self.file {
|
||||
landlock.add_rule_with_access(file.to_path_buf(), "rw")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
|
||||
pub enum HotplugMethod {
|
||||
#[default]
|
||||
@ -245,6 +263,15 @@ pub struct DiskConfig {
|
||||
pub queue_affinity: Option<Vec<VirtQueueAffinity>>,
|
||||
}
|
||||
|
||||
impl ApplyLandlock for DiskConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
if let Some(path) = &self.path {
|
||||
landlock.add_rule_with_access(path.to_path_buf(), "rw")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub const DEFAULT_DISK_NUM_QUEUES: usize = 1;
|
||||
|
||||
pub fn default_diskconfig_num_queues() -> usize {
|
||||
@ -378,6 +405,14 @@ impl Default for RngConfig {
|
||||
}
|
||||
}
|
||||
|
||||
impl ApplyLandlock for RngConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
// Rng Path only need read access
|
||||
landlock.add_rule_with_access(self.src.to_path_buf(), "r")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct BalloonConfig {
|
||||
pub size: u64,
|
||||
@ -411,6 +446,13 @@ pub fn default_fsconfig_queue_size() -> u16 {
|
||||
1024
|
||||
}
|
||||
|
||||
impl ApplyLandlock for FsConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct PmemConfig {
|
||||
pub file: PathBuf,
|
||||
@ -426,6 +468,13 @@ pub struct PmemConfig {
|
||||
pub pci_segment: u16,
|
||||
}
|
||||
|
||||
impl ApplyLandlock for PmemConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
landlock.add_rule_with_access(self.file.to_path_buf(), "rw")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub enum ConsoleOutputMode {
|
||||
Off,
|
||||
@ -450,6 +499,18 @@ pub fn default_consoleconfig_file() -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
|
||||
impl ApplyLandlock for ConsoleConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
if let Some(file) = &self.file {
|
||||
landlock.add_rule_with_access(file.to_path_buf(), "rw")?;
|
||||
}
|
||||
if let Some(socket) = &self.socket {
|
||||
landlock.add_rule_with_access(socket.to_path_buf(), "rw")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct DebugConsoleConfig {
|
||||
@ -470,6 +531,15 @@ impl Default for DebugConsoleConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
impl ApplyLandlock for DebugConsoleConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
if let Some(file) = &self.file {
|
||||
landlock.add_rule_with_access(file.to_path_buf(), "rw")?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct DeviceConfig {
|
||||
@ -484,6 +554,22 @@ pub struct DeviceConfig {
|
||||
pub x_nv_gpudirect_clique: Option<u8>,
|
||||
}
|
||||
|
||||
impl ApplyLandlock for DeviceConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
let device_path = fs::read_link(self.path.as_path()).map_err(LandlockError::OpenPath)?;
|
||||
let iommu_group = device_path.file_name();
|
||||
let iommu_group_str = iommu_group
|
||||
.ok_or(LandlockError::InvalidPath)?
|
||||
.to_str()
|
||||
.ok_or(LandlockError::InvalidPath)?;
|
||||
|
||||
let vfio_group_path = "/dev/vfio/".to_owned() + iommu_group_str;
|
||||
landlock.add_rule_with_access(vfio_group_path.into(), "rw")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct UserDeviceConfig {
|
||||
pub socket: PathBuf,
|
||||
@ -493,6 +579,13 @@ pub struct UserDeviceConfig {
|
||||
pub pci_segment: u16,
|
||||
}
|
||||
|
||||
impl ApplyLandlock for UserDeviceConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct VdpaConfig {
|
||||
pub path: PathBuf,
|
||||
@ -510,6 +603,13 @@ pub fn default_vdpaconfig_num_queues() -> usize {
|
||||
1
|
||||
}
|
||||
|
||||
impl ApplyLandlock for VdpaConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
landlock.add_rule_with_access(self.path.to_path_buf(), "rw")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct VsockConfig {
|
||||
pub cid: u32,
|
||||
@ -522,6 +622,13 @@ pub struct VsockConfig {
|
||||
pub pci_segment: u16,
|
||||
}
|
||||
|
||||
impl ApplyLandlock for VsockConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct SgxEpcConfig {
|
||||
@ -575,6 +682,30 @@ pub struct PayloadConfig {
|
||||
pub host_data: Option<String>,
|
||||
}
|
||||
|
||||
impl ApplyLandlock for PayloadConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
// Payload only needs read access
|
||||
if let Some(firmware) = &self.firmware {
|
||||
landlock.add_rule_with_access(firmware.to_path_buf(), "r")?;
|
||||
}
|
||||
|
||||
if let Some(kernel) = &self.kernel {
|
||||
landlock.add_rule_with_access(kernel.to_path_buf(), "r")?;
|
||||
}
|
||||
|
||||
if let Some(initramfs) = &self.initramfs {
|
||||
landlock.add_rule_with_access(initramfs.to_path_buf(), "r")?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "igvm")]
|
||||
if let Some(igvm) = &self.igvm {
|
||||
landlock.add_rule_with_access(igvm.to_path_buf(), "r")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_serial() -> ConsoleConfig {
|
||||
ConsoleConfig {
|
||||
file: None,
|
||||
@ -598,6 +729,26 @@ pub struct TpmConfig {
|
||||
pub socket: PathBuf,
|
||||
}
|
||||
|
||||
impl ApplyLandlock for TpmConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
landlock.add_rule_with_access(self.socket.to_path_buf(), "rw")?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct LandlockConfig {
|
||||
pub path: PathBuf,
|
||||
pub access: String,
|
||||
}
|
||||
|
||||
impl ApplyLandlock for LandlockConfig {
|
||||
fn apply_landlock(&self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
landlock.add_rule_with_access(self.path.to_path_buf(), self.access.clone().as_str())?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct VmConfig {
|
||||
#[serde(default)]
|
||||
@ -645,4 +796,94 @@ pub struct VmConfig {
|
||||
// valid, and will be closed when the holding VmConfig instance is destroyed.
|
||||
#[serde(skip)]
|
||||
pub preserved_fds: Option<Vec<i32>>,
|
||||
#[serde(default)]
|
||||
pub landlock_enable: bool,
|
||||
pub landlock_config: Option<Vec<LandlockConfig>>,
|
||||
}
|
||||
|
||||
impl VmConfig {
|
||||
pub(crate) fn apply_landlock(&self) -> LandlockResult<()> {
|
||||
let mut landlock = Landlock::new()?;
|
||||
|
||||
if let Some(mem_zones) = &self.memory.zones {
|
||||
for zone in mem_zones.iter() {
|
||||
zone.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
let disks = &self.disks;
|
||||
if let Some(disks) = disks {
|
||||
for disk in disks.iter() {
|
||||
disk.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.rng.apply_landlock(&mut landlock)?;
|
||||
|
||||
if let Some(fs_configs) = &self.fs {
|
||||
for fs_config in fs_configs.iter() {
|
||||
fs_config.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(pmem_configs) = &self.pmem {
|
||||
for pmem_config in pmem_configs.iter() {
|
||||
pmem_config.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.console.apply_landlock(&mut landlock)?;
|
||||
self.serial.apply_landlock(&mut landlock)?;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
self.debug_console.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
|
||||
if let Some(devices) = &self.devices {
|
||||
landlock.add_rule_with_access("/dev/vfio/vfio".into(), "rw")?;
|
||||
|
||||
for device in devices.iter() {
|
||||
device.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(user_devices) = &self.user_devices {
|
||||
for user_devices in user_devices.iter() {
|
||||
user_devices.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(vdpa_configs) = &self.vdpa {
|
||||
for vdpa_config in vdpa_configs.iter() {
|
||||
vdpa_config.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(vsock_config) = &self.vsock {
|
||||
vsock_config.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
|
||||
if let Some(payload) = &self.payload {
|
||||
payload.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
|
||||
if let Some(tpm_config) = &self.tpm {
|
||||
tpm_config.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
|
||||
if self.net.is_some() {
|
||||
landlock.add_rule_with_access("/dev/net/tun".into(), "rw")?;
|
||||
}
|
||||
|
||||
if let Some(landlock_configs) = &self.landlock_config {
|
||||
for landlock_config in landlock_configs.iter() {
|
||||
landlock_config.apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
landlock.restrict_self()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user