vmm: make landlock configs VMM-level config

This requires stashing the config values in `struct Vmm`. The configs
should be validated before before creating the VMM thread. Refactor the
code and update documentation where necessary.

The place where the rules are applied remain the same.

Signed-off-by: Wei Liu <liuwe@microsoft.com>
This commit is contained in:
Wei Liu 2024-07-02 21:12:03 +00:00 committed by Liu Wei
parent 8452edfcc7
commit 94929889ac
6 changed files with 299 additions and 317 deletions

View File

@ -30,10 +30,7 @@ Linux kernel confirms Landlock support with above message in dmesg.
## Implementation Details ## Implementation Details
To enable Landlock, Cloud-Hypervisor process needs the full list of files it To enable Landlock, Cloud-Hypervisor process needs the full list of files it
needs to access over its lifetime. Most of these files are received as VM needs to access over its lifetime. Landlock is enabled in the `vm_create` stage.
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 ## Enable Landlock

View File

@ -190,8 +190,6 @@ impl RequestHandler for StubApiRequestHandler {
platform: None, platform: None,
tpm: None, tpm: None,
preserved_fds: None, preserved_fds: None,
landlock_enable: false,
landlock_config: None,
})), })),
state: VmState::Running, state: VmState::Running,
memory_actual_size: 0, memory_actual_size: 0,

View File

@ -285,14 +285,14 @@ fn create_app(default_vcpus: String, default_memory: String, default_rng: String
) )
.action(ArgAction::SetTrue) .action(ArgAction::SetTrue)
.default_value("false") .default_value("false")
.group("vm-config"), .group("vmm-config"),
) )
.arg( .arg(
Arg::new("landlock-rules") Arg::new("landlock-rules")
.long("landlock-rules") .long("landlock-rules")
.help(config::LandlockConfig::SYNTAX) .help(config::LandlockConfig::SYNTAX)
.num_args(1..) .num_args(1..)
.group("vm-config"), .group("vmm-config"),
) )
.arg( .arg(
Arg::new("net") Arg::new("net")
@ -657,7 +657,31 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
let vm_debug_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateDebugEventFd)?; let vm_debug_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateDebugEventFd)?;
let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateExitEventFd)?; let exit_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::CreateExitEventFd)?;
let landlock_enable = cmd_arguments.get_flag("landlock"); 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)] #[allow(unused_mut)]
let mut event_monitor = cmd_arguments let mut event_monitor = cmd_arguments
@ -755,6 +779,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
&seccomp_action, &seccomp_action,
hypervisor, hypervisor,
landlock_enable, landlock_enable,
landlock_config,
) )
.map_err(Error::StartVmmThread)?; .map_err(Error::StartVmmThread)?;
@ -1065,8 +1090,6 @@ mod unit_tests {
platform: None, platform: None,
tpm: None, tpm: None,
preserved_fds: None, preserved_fds: None,
landlock_enable: false,
landlock_config: None,
}; };
assert_eq!(expected_vm_config, result_vm_config); assert_eq!(expected_vm_config, result_vm_config);

View File

@ -496,8 +496,6 @@ pub struct VmParams<'a> {
pub igvm: Option<&'a str>, pub igvm: Option<&'a str>,
#[cfg(feature = "sev_snp")] #[cfg(feature = "sev_snp")]
pub host_data: Option<&'a str>, pub host_data: Option<&'a str>,
pub landlock_enable: bool,
pub landlock_config: Option<Vec<&'a str>>,
} }
impl<'a> VmParams<'a> { impl<'a> VmParams<'a> {
@ -563,10 +561,6 @@ impl<'a> VmParams<'a> {
let igvm = args.get_one::<String>("igvm").map(|x| x as &str); let igvm = args.get_one::<String>("igvm").map(|x| x as &str);
#[cfg(feature = "sev_snp")] #[cfg(feature = "sev_snp")]
let host_data = args.get_one::<String>("host-data").map(|x| x as &str); 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 { VmParams {
cpus, cpus,
@ -605,8 +599,6 @@ impl<'a> VmParams<'a> {
igvm, igvm,
#[cfg(feature = "sev_snp")] #[cfg(feature = "sev_snp")]
host_data, host_data,
landlock_enable,
landlock_config,
} }
} }
} }
@ -2334,6 +2326,12 @@ impl TpmConfig {
} }
} }
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct LandlockConfig {
pub path: PathBuf,
pub access: String,
}
impl LandlockConfig { impl LandlockConfig {
pub const SYNTAX: &'static str = "Landlock parameters \ pub const SYNTAX: &'static str = "Landlock parameters \
\"path=<path/to/{file/dir}>,access=[rw]\""; \"path=<path/to/{file/dir}>,access=[rw]\"";
@ -2725,12 +2723,6 @@ impl VmConfig {
.map(|p| p.iommu_segments.is_some()) .map(|p| p.iommu_segments.is_some())
.unwrap_or_default(); .unwrap_or_default();
if let Some(landlock_configs) = &self.landlock_config {
for landlock_config in landlock_configs {
landlock_config.validate()?;
}
}
Ok(id_list) Ok(id_list)
} }
@ -2901,16 +2893,6 @@ impl VmConfig {
#[cfg(feature = "guest_debug")] #[cfg(feature = "guest_debug")]
let gdb = vm_params.gdb; 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 { let mut config = VmConfig {
cpus: CpusConfig::parse(vm_params.cpus)?, cpus: CpusConfig::parse(vm_params.cpus)?,
memory: MemoryConfig::parse(vm_params.memory, vm_params.memory_zones)?, memory: MemoryConfig::parse(vm_params.memory, vm_params.memory_zones)?,
@ -2942,8 +2924,6 @@ impl VmConfig {
platform, platform,
tpm, tpm,
preserved_fds: None, preserved_fds: None,
landlock_enable: vm_params.landlock_enable,
landlock_config,
}; };
config.validate().map_err(Error::Validation)?; config.validate().map_err(Error::Validation)?;
Ok(config) Ok(config)
@ -3070,7 +3050,6 @@ impl Clone for VmConfig {
.as_ref() .as_ref()
// SAFETY: FFI call with valid FDs // SAFETY: FFI call with valid FDs
.map(|fds| fds.iter().map(|fd| unsafe { libc::dup(*fd) }).collect()), .map(|fds| fds.iter().map(|fd| unsafe { libc::dup(*fd) }).collect()),
landlock_config: self.landlock_config.clone(),
..*self ..*self
} }
} }
@ -3869,8 +3848,6 @@ mod tests {
..net_fixture() ..net_fixture()
}, },
]), ]),
landlock_enable: false,
landlock_config: None,
}; };
let valid_config = RestoreConfig { let valid_config = RestoreConfig {
@ -4059,8 +4036,6 @@ mod tests {
platform: None, platform: None,
tpm: None, tpm: None,
preserved_fds: None, preserved_fds: None,
landlock_enable: false,
landlock_config: None,
}; };
assert!(valid_config.validate().is_ok()); assert!(valid_config.validate().is_ok());

View File

@ -12,8 +12,11 @@ use crate::api::{
ApiRequest, ApiResponse, RequestHandler, VmInfoResponse, VmReceiveMigrationData, ApiRequest, ApiResponse, RequestHandler, VmInfoResponse, VmReceiveMigrationData,
VmSendMigrationData, VmmPingResponse, VmSendMigrationData, VmmPingResponse,
}; };
#[cfg(target_arch = "x86_64")]
use crate::config::DebugConsoleConfig;
use crate::config::{ use crate::config::{
add_to_config, DeviceConfig, DiskConfig, FsConfig, NetConfig, PmemConfig, RestoreConfig, add_to_config, ConsoleConfig, DeviceConfig, DiskConfig, FsConfig, LandlockConfig,
MemoryZoneConfig, NetConfig, PayloadConfig, PmemConfig, RestoreConfig, RngConfig, TpmConfig,
UserDeviceConfig, VdpaConfig, VmConfig, VsockConfig, UserDeviceConfig, VdpaConfig, VmConfig, VsockConfig,
}; };
#[cfg(all(target_arch = "x86_64", feature = "guest_debug"))] #[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
@ -39,6 +42,7 @@ use serde::ser::{SerializeStruct, Serializer};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use signal_hook::iterator::{Handle, Signals}; use signal_hook::iterator::{Handle, Signals};
use std::collections::HashMap; use std::collections::HashMap;
use std::fs;
use std::fs::File; use std::fs::File;
use std::io; use std::io;
use std::io::{stdout, Read, Write}; use std::io::{stdout, Read, Write};
@ -413,6 +417,7 @@ pub fn start_vmm_thread(
seccomp_action: &SeccompAction, seccomp_action: &SeccompAction,
hypervisor: Arc<dyn hypervisor::Hypervisor>, hypervisor: Arc<dyn hypervisor::Hypervisor>,
landlock_enable: bool, landlock_enable: bool,
landlock_config: Option<Vec<LandlockConfig>>,
) -> Result<VmmThreadHandle> { ) -> Result<VmmThreadHandle> {
#[cfg(feature = "guest_debug")] #[cfg(feature = "guest_debug")]
let gdb_hw_breakpoints = hypervisor.get_guest_debug_hw_bps(); let gdb_hw_breakpoints = hypervisor.get_guest_debug_hw_bps();
@ -451,6 +456,8 @@ pub fn start_vmm_thread(
vmm_seccomp_action, vmm_seccomp_action,
hypervisor, hypervisor,
exit_event, exit_event,
landlock_enable,
landlock_config,
)?; )?;
vmm.setup_signal_handler(landlock_enable)?; vmm.setup_signal_handler(landlock_enable)?;
@ -578,6 +585,8 @@ pub struct Vmm {
original_termios_opt: Arc<Mutex<Option<termios>>>, original_termios_opt: Arc<Mutex<Option<termios>>>,
console_resize_pipe: Option<File>, console_resize_pipe: Option<File>,
console_info: Option<ConsoleInfo>, console_info: Option<ConsoleInfo>,
landlock_enable: bool,
landlock_config: Option<Vec<LandlockConfig>>,
} }
impl Vmm { impl Vmm {
@ -684,6 +693,8 @@ impl Vmm {
seccomp_action: SeccompAction, seccomp_action: SeccompAction,
hypervisor: Arc<dyn hypervisor::Hypervisor>, hypervisor: Arc<dyn hypervisor::Hypervisor>,
exit_evt: EventFd, exit_evt: EventFd,
landlock_enable: bool,
landlock_config: Option<Vec<LandlockConfig>>,
) -> Result<Self> { ) -> Result<Self> {
let mut epoll = EpollContext::new().map_err(Error::Epoll)?; let mut epoll = EpollContext::new().map_err(Error::Epoll)?;
let reset_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?; let reset_evt = EventFd::new(EFD_NONBLOCK).map_err(Error::EventFdCreate)?;
@ -730,6 +741,8 @@ impl Vmm {
original_termios_opt: Arc::new(Mutex::new(None)), original_termios_opt: Arc::new(Mutex::new(None)),
console_resize_pipe: None, console_resize_pipe: None,
console_info: None, console_info: None,
landlock_enable,
landlock_config,
}) })
} }
@ -766,15 +779,12 @@ impl Vmm {
MigratableError::MigrateReceive(anyhow!("Error creating console devices: {:?}", e)) MigratableError::MigrateReceive(anyhow!("Error creating console devices: {:?}", e))
})?); })?);
if self if self.landlock_enable {
.vm_config apply_landlock(
.as_ref() &self.vm_config.as_ref().unwrap().clone(),
.unwrap() &self.landlock_config,
.lock() )
.unwrap() .map_err(|e| {
.landlock_enable
{
apply_landlock(self.vm_config.as_ref().unwrap().clone()).map_err(|e| {
MigratableError::MigrateReceive(anyhow!("Error applying landlock: {:?}", e)) MigratableError::MigrateReceive(anyhow!("Error applying landlock: {:?}", e))
})?; })?;
} }
@ -1264,8 +1274,236 @@ impl Vmm {
} }
} }
fn apply_landlock(vm_config: Arc<Mutex<VmConfig>>) -> result::Result<(), LandlockError> { pub type LandlockResult<T> = result::Result<T, LandlockError>;
vm_config.lock().unwrap().apply_landlock()?; /// 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()?;
Ok(()) Ok(())
} }
@ -1278,16 +1516,12 @@ impl RequestHandler for Vmm {
self.console_info = self.console_info =
Some(pre_create_console_devices(self).map_err(VmError::CreateConsoleDevices)?); Some(pre_create_console_devices(self).map_err(VmError::CreateConsoleDevices)?);
if self if self.landlock_enable {
.vm_config apply_landlock(
.as_ref() &self.vm_config.as_ref().unwrap().clone(),
.unwrap() &self.landlock_config,
.lock() )
.unwrap() .map_err(VmError::ApplyLandlock)?;
.landlock_enable
{
apply_landlock(self.vm_config.as_ref().unwrap().clone())
.map_err(VmError::ApplyLandlock)?;
} }
Ok(()) Ok(())
} else { } else {
@ -1472,16 +1706,12 @@ impl RequestHandler for Vmm {
)?; )?;
self.vm = Some(vm); self.vm = Some(vm);
if self if self.landlock_enable {
.vm_config apply_landlock(
.as_ref() &self.vm_config.as_ref().unwrap().clone(),
.unwrap() &self.landlock_config,
.lock() )
.unwrap() .map_err(VmError::ApplyLandlock)?;
.landlock_enable
{
apply_landlock(self.vm_config.as_ref().unwrap().clone())
.map_err(VmError::ApplyLandlock)?;
} }
// Now we can restore the rest of the VM. // Now we can restore the rest of the VM.
@ -2195,6 +2425,8 @@ mod unit_tests {
SeccompAction::Allow, SeccompAction::Allow,
hypervisor::new().unwrap(), hypervisor::new().unwrap(),
EventFd::new(EFD_NONBLOCK).unwrap(), EventFd::new(EFD_NONBLOCK).unwrap(),
false,
None,
) )
.unwrap() .unwrap()
} }
@ -2273,8 +2505,6 @@ mod unit_tests {
platform: None, platform: None,
tpm: None, tpm: None,
preserved_fds: None, preserved_fds: None,
landlock_enable: false,
landlock_config: None,
})) }))
} }

View File

@ -2,20 +2,11 @@
// //
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
// //
use crate::{landlock::LandlockError, Landlock};
use net_util::MacAddr; use net_util::MacAddr;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{fs, net::Ipv4Addr, path::PathBuf, result}; use std::{net::Ipv4Addr, path::PathBuf};
use virtio_devices::RateLimiterConfig; 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)] #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct CpuAffinity { pub struct CpuAffinity {
pub vcpu: u8, pub vcpu: u8,
@ -141,15 +132,6 @@ pub struct MemoryZoneConfig {
pub prefault: bool, 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)] #[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
pub enum HotplugMethod { pub enum HotplugMethod {
#[default] #[default]
@ -263,15 +245,6 @@ pub struct DiskConfig {
pub queue_affinity: Option<Vec<VirtQueueAffinity>>, 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 const DEFAULT_DISK_NUM_QUEUES: usize = 1;
pub fn default_diskconfig_num_queues() -> usize { pub fn default_diskconfig_num_queues() -> usize {
@ -405,14 +378,6 @@ 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)] #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct BalloonConfig { pub struct BalloonConfig {
pub size: u64, pub size: u64,
@ -446,13 +411,6 @@ pub fn default_fsconfig_queue_size() -> u16 {
1024 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)] #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct PmemConfig { pub struct PmemConfig {
pub file: PathBuf, pub file: PathBuf,
@ -468,13 +426,6 @@ pub struct PmemConfig {
pub pci_segment: u16, 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)] #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub enum ConsoleOutputMode { pub enum ConsoleOutputMode {
Off, Off,
@ -499,18 +450,6 @@ pub fn default_consoleconfig_file() -> Option<PathBuf> {
None 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")] #[cfg(target_arch = "x86_64")]
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct DebugConsoleConfig { pub struct DebugConsoleConfig {
@ -531,15 +470,6 @@ 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)] #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct DeviceConfig { pub struct DeviceConfig {
@ -554,22 +484,6 @@ pub struct DeviceConfig {
pub x_nv_gpudirect_clique: Option<u8>, 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)] #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct UserDeviceConfig { pub struct UserDeviceConfig {
pub socket: PathBuf, pub socket: PathBuf,
@ -579,13 +493,6 @@ pub struct UserDeviceConfig {
pub pci_segment: u16, 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)] #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct VdpaConfig { pub struct VdpaConfig {
pub path: PathBuf, pub path: PathBuf,
@ -603,13 +510,6 @@ pub fn default_vdpaconfig_num_queues() -> usize {
1 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)] #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct VsockConfig { pub struct VsockConfig {
pub cid: u32, pub cid: u32,
@ -622,13 +522,6 @@ pub struct VsockConfig {
pub pci_segment: u16, 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")] #[cfg(target_arch = "x86_64")]
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct SgxEpcConfig { pub struct SgxEpcConfig {
@ -682,30 +575,6 @@ pub struct PayloadConfig {
pub host_data: Option<String>, 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 { pub fn default_serial() -> ConsoleConfig {
ConsoleConfig { ConsoleConfig {
file: None, file: None,
@ -729,26 +598,6 @@ pub struct TpmConfig {
pub socket: PathBuf, 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)] #[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
pub struct VmConfig { pub struct VmConfig {
#[serde(default)] #[serde(default)]
@ -796,94 +645,4 @@ pub struct VmConfig {
// valid, and will be closed when the holding VmConfig instance is destroyed. // valid, and will be closed when the holding VmConfig instance is destroyed.
#[serde(skip)] #[serde(skip)]
pub preserved_fds: Option<Vec<i32>>, 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(())
}
} }