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:
Praveen K Paladugu 2024-07-30 14:58:50 +00:00 committed by Bo Chen
parent 513973873c
commit d2f0e8aebb
6 changed files with 317 additions and 299 deletions

View File

@ -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

View File

@ -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,

View File

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

View File

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

View File

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

View File

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