Compare commits
18 Commits
2b8495d300
...
810906d859
Author | SHA1 | Date |
---|---|---|
Praveen K Paladugu | 810906d859 | |
dependabot[bot] | d0cfa3a014 | |
Praveen K Paladugu | 5cb47f6434 | |
Praveen K Paladugu | cca808865d | |
Praveen K Paladugu | bc545f3b64 | |
Praveen K Paladugu | 69a4cda8ec | |
Praveen K Paladugu | 5021aa05e8 | |
Praveen K Paladugu | ab0f103dac | |
Praveen K Paladugu | 57c9124379 | |
Praveen K Paladugu | 885e69e7d1 | |
Praveen K Paladugu | 7c51498ebf | |
Praveen K Paladugu | f19c4922bf | |
Praveen K Paladugu | 74632493d1 | |
Praveen K Paladugu | 1a5919c94a | |
Praveen K Paladugu | 80c629d7e9 | |
Praveen K Paladugu | f938cd12d6 | |
Praveen K Paladugu | 1ca9891b86 | |
Praveen K Paladugu | 766cb9ba50 |
|
@ -352,6 +352,9 @@ name = "bitflags"
|
|||
version = "2.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block"
|
||||
|
@ -1204,6 +1207,17 @@ dependencies = [
|
|||
"vmm-sys-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "landlock"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9baa9eeb6e315942429397e617a190f4fdc696ef1ee0342939d641029cbb4ea7"
|
||||
dependencies = [
|
||||
"enumflags2",
|
||||
"libc",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
|
@ -2637,6 +2651,7 @@ dependencies = [
|
|||
"hypervisor",
|
||||
"igvm",
|
||||
"igvm_defs",
|
||||
"landlock",
|
||||
"libc",
|
||||
"linux-loader",
|
||||
"log",
|
||||
|
|
|
@ -184,9 +184,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.3"
|
||||
version = "4.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813"
|
||||
checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -23,6 +23,7 @@ pub enum OptionParserError {
|
|||
UnknownOption(String),
|
||||
InvalidSyntax(String),
|
||||
Conversion(String, String),
|
||||
InvalidValue(String),
|
||||
}
|
||||
|
||||
impl fmt::Display for OptionParserError {
|
||||
|
@ -33,6 +34,7 @@ impl fmt::Display for OptionParserError {
|
|||
OptionParserError::Conversion(field, value) => {
|
||||
write!(f, "unable to convert {value} for {field}")
|
||||
}
|
||||
OptionParserError::InvalidValue(s) => write!(f, "invalid value: {s}"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
28
src/main.rs
28
src/main.rs
|
@ -259,6 +259,24 @@ fn create_app(default_vcpus: String, default_memory: String, default_rng: String
|
|||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("landlock")
|
||||
.long("landlock")
|
||||
.num_args(0)
|
||||
.help(
|
||||
"eanble/disable landlock.",
|
||||
)
|
||||
.action(ArgAction::SetTrue)
|
||||
.default_value("false")
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("landlock-rules")
|
||||
.long("landlock-rules")
|
||||
.help(config::LandlockConfig::SYNTAX)
|
||||
.num_args(1..)
|
||||
.group("vm-config"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("net")
|
||||
.long("net")
|
||||
|
@ -615,6 +633,8 @@ 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)?;
|
||||
// TODO: landlock_enable is being parsed in 2 places: here and VMparams:parse() how to merge both?
|
||||
let landlock_enable = cmd_arguments.get_flag("landlock");
|
||||
|
||||
#[allow(unused_mut)]
|
||||
let mut event_monitor = cmd_arguments
|
||||
|
@ -684,6 +704,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
|||
vmm::start_event_monitor_thread(
|
||||
monitor,
|
||||
&seccomp_action,
|
||||
landlock_enable,
|
||||
hypervisor.hypervisor_type(),
|
||||
exit_evt.try_clone().unwrap(),
|
||||
)
|
||||
|
@ -710,6 +731,7 @@ fn start_vmm(cmd_arguments: ArgMatches) -> Result<Option<String>, Error> {
|
|||
exit_evt.try_clone().unwrap(),
|
||||
&seccomp_action,
|
||||
hypervisor,
|
||||
landlock_enable,
|
||||
)
|
||||
.map_err(Error::StartVmmThread)?;
|
||||
|
||||
|
@ -913,12 +935,16 @@ mod unit_tests {
|
|||
mode: ConsoleOutputMode::Null,
|
||||
iommu: false,
|
||||
socket: None,
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
},
|
||||
console: ConsoleConfig {
|
||||
file: None,
|
||||
mode: ConsoleOutputMode::Tty,
|
||||
iommu: false,
|
||||
socket: None,
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
},
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
debug_console: DebugConsoleConfig::default(),
|
||||
|
@ -937,6 +963,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);
|
||||
|
|
|
@ -2813,6 +2813,10 @@ mod common_parallel {
|
|||
"--platform",
|
||||
&format!("num_pci_segments={MAX_NUM_PCI_SEGMENTS},iommu_segments=[1]"),
|
||||
])
|
||||
.args([
|
||||
"--landlock-rules",
|
||||
format!("path={:?},flags=rw", test_disk_path).as_str(),
|
||||
])
|
||||
.default_disks()
|
||||
.capture_output()
|
||||
.default_net();
|
||||
|
@ -4332,6 +4336,10 @@ mod common_parallel {
|
|||
.args(["--memory", "size=512M"])
|
||||
.args(["--kernel", direct_kernel_boot_path().to_str().unwrap()])
|
||||
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.args([
|
||||
"--landlock-rules",
|
||||
format!("path={:?},flags=rw", console_path.to_str().unwrap()).as_str(),
|
||||
])
|
||||
.default_disks()
|
||||
.default_net()
|
||||
.args([
|
||||
|
@ -5157,6 +5165,10 @@ mod common_parallel {
|
|||
let kernel_path = edk2_path();
|
||||
|
||||
let api_socket = temp_api_path(&guest.tmp_dir);
|
||||
//Hotplugged disk path
|
||||
let mut blk_file_path = dirs::home_dir().unwrap();
|
||||
blk_file_path.push("workloads");
|
||||
blk_file_path.push("blk.img");
|
||||
|
||||
let mut child = GuestCommand::new(&guest)
|
||||
.args(["--api-socket", &api_socket])
|
||||
|
@ -5164,6 +5176,14 @@ mod common_parallel {
|
|||
.args(["--memory", "size=512M"])
|
||||
.args(["--kernel", kernel_path.to_str().unwrap()])
|
||||
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.args([
|
||||
"--landlock-rules",
|
||||
format!(
|
||||
"path={:?},flags=rw",
|
||||
blk_file_path.as_path().to_str().unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
])
|
||||
.default_disks()
|
||||
.default_net()
|
||||
.capture_output()
|
||||
|
@ -5184,10 +5204,6 @@ mod common_parallel {
|
|||
0
|
||||
);
|
||||
|
||||
// Now let's add the extra disk.
|
||||
let mut blk_file_path = dirs::home_dir().unwrap();
|
||||
blk_file_path.push("workloads");
|
||||
blk_file_path.push("blk.img");
|
||||
let (cmd_success, cmd_output) = remote_command_w_output(
|
||||
&api_socket,
|
||||
"add-disk",
|
||||
|
@ -5671,11 +5687,22 @@ mod common_parallel {
|
|||
|
||||
let mut cmd = GuestCommand::new(&guest);
|
||||
|
||||
let pmem_temp_file = TempFile::new().unwrap();
|
||||
pmem_temp_file.as_file().set_len(128 << 20).unwrap();
|
||||
|
||||
cmd.args(["--api-socket", &api_socket])
|
||||
.args(["--cpus", "boot=1"])
|
||||
.args(["--memory", "size=512M"])
|
||||
.args(["--kernel", kernel_path.to_str().unwrap()])
|
||||
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.args([
|
||||
"--landlock-rules",
|
||||
format!(
|
||||
"path={:?},flags=rw",
|
||||
pmem_temp_file.as_path().to_str().unwrap()
|
||||
)
|
||||
.as_str(),
|
||||
])
|
||||
.default_disks()
|
||||
.default_net()
|
||||
.capture_output();
|
||||
|
@ -5703,8 +5730,6 @@ mod common_parallel {
|
|||
0
|
||||
);
|
||||
|
||||
let pmem_temp_file = TempFile::new().unwrap();
|
||||
pmem_temp_file.as_file().set_len(128 << 20).unwrap();
|
||||
let (cmd_success, cmd_output) = remote_command_w_output(
|
||||
&api_socket,
|
||||
"add-pmem",
|
||||
|
@ -5816,6 +5841,10 @@ mod common_parallel {
|
|||
.args(["--memory", "size=512M"])
|
||||
.args(["--kernel", kernel_path.to_str().unwrap()])
|
||||
.args(["--cmdline", DIRECT_KERNEL_BOOT_CMDLINE])
|
||||
.args([
|
||||
"--landlock-rules",
|
||||
format!("path={:?},flags=rw", "/dev/net/tun").as_str(),
|
||||
])
|
||||
.default_disks()
|
||||
.capture_output();
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ acpi_tables = { git = "https://github.com/rust-vmm/acpi_tables", branch = "main"
|
|||
anyhow = "1.0.79"
|
||||
arc-swap = "1.5.1"
|
||||
arch = { path = "../arch" }
|
||||
bitflags = "2.4.2"
|
||||
bitflags = { version = "2.4.2", features = ["serde"] }
|
||||
block = { path = "../block" }
|
||||
blocking = { version = "1.5.1", optional = true }
|
||||
cfg-if = "1.0.0"
|
||||
|
@ -37,6 +37,7 @@ hex = { version = "0.4.3", optional = true }
|
|||
hypervisor = { path = "../hypervisor" }
|
||||
igvm_defs = { git = "https://github.com/microsoft/igvm", branch = "main", package = "igvm_defs", optional = true }
|
||||
igvm_parser = { git = "https://github.com/microsoft/igvm", branch = "main", package = "igvm", optional = true }
|
||||
landlock = "0.3.1"
|
||||
libc = "0.2.153"
|
||||
linux-loader = { version = "0.11.0", features = ["elf", "bzimage", "pe"] }
|
||||
log = "0.4.20"
|
||||
|
|
|
@ -12,6 +12,7 @@ use crate::api::{
|
|||
VmReceiveMigration, VmRemoveDevice, VmResize, VmResizeZone, VmRestore, VmResume,
|
||||
VmSendMigration, VmShutdown, VmSnapshot,
|
||||
};
|
||||
use crate::landlock::Landlock;
|
||||
use crate::seccomp_filters::{get_seccomp_filter, Thread};
|
||||
use crate::{Error as VmmError, Result};
|
||||
use core::fmt;
|
||||
|
@ -303,6 +304,7 @@ fn start_http_thread(
|
|||
seccomp_action: &SeccompAction,
|
||||
exit_evt: EventFd,
|
||||
hypervisor_type: HypervisorType,
|
||||
landlock_enable: bool,
|
||||
) -> Result<HttpApiHandle> {
|
||||
// Retrieve seccomp filter for API thread
|
||||
let api_seccomp_filter = get_seccomp_filter(seccomp_action, Thread::HttpApi, hypervisor_type)
|
||||
|
@ -329,6 +331,18 @@ fn start_http_thread(
|
|||
})?;
|
||||
}
|
||||
|
||||
if landlock_enable {
|
||||
Landlock::new()
|
||||
.map_err(VmmError::ApplyLandlock)?
|
||||
.restrict_self()
|
||||
.map_err(VmmError::ApplyLandlock)
|
||||
.map_err(|e| {
|
||||
error!("Error applying landlock to http-server thread: {:?}", e);
|
||||
exit_evt.write(1).ok();
|
||||
e
|
||||
})?;
|
||||
}
|
||||
|
||||
std::panic::catch_unwind(AssertUnwindSafe(move || {
|
||||
server.start_server().unwrap();
|
||||
loop {
|
||||
|
@ -375,6 +389,7 @@ pub fn start_http_path_thread(
|
|||
seccomp_action: &SeccompAction,
|
||||
exit_evt: EventFd,
|
||||
hypervisor_type: HypervisorType,
|
||||
landlock_enable: bool,
|
||||
) -> Result<HttpApiHandle> {
|
||||
let socket_path = PathBuf::from(path);
|
||||
let socket_fd = UnixListener::bind(socket_path).map_err(VmmError::CreateApiServerSocket)?;
|
||||
|
@ -389,6 +404,7 @@ pub fn start_http_path_thread(
|
|||
seccomp_action,
|
||||
exit_evt,
|
||||
hypervisor_type,
|
||||
landlock_enable,
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -399,6 +415,7 @@ pub fn start_http_fd_thread(
|
|||
seccomp_action: &SeccompAction,
|
||||
exit_evt: EventFd,
|
||||
hypervisor_type: HypervisorType,
|
||||
landlock_enable: bool,
|
||||
) -> Result<HttpApiHandle> {
|
||||
// SAFETY: Valid FD
|
||||
let server = unsafe { HttpServer::new_from_fd(fd) }.map_err(VmmError::CreateApiServer)?;
|
||||
|
@ -409,6 +426,7 @@ pub fn start_http_fd_thread(
|
|||
seccomp_action,
|
||||
exit_evt,
|
||||
hypervisor_type,
|
||||
landlock_enable,
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
|
||||
use crate::landlock;
|
||||
pub use crate::vm_config::*;
|
||||
use clap::ArgMatches;
|
||||
use option_parser::{
|
||||
|
@ -108,6 +109,10 @@ pub enum Error {
|
|||
ParseTpm(OptionParserError),
|
||||
/// Missing path for TPM device
|
||||
ParseTpmPathMissing,
|
||||
/// Error parsing Landlock rules
|
||||
ParseLandlockRules(OptionParserError),
|
||||
/// Missing fields in Landlock rules
|
||||
ParseLandlockMissingFields,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Error)]
|
||||
|
@ -197,6 +202,8 @@ pub enum ValidationError {
|
|||
InvalidIoPortHex(String),
|
||||
#[cfg(feature = "sev_snp")]
|
||||
InvalidHostData,
|
||||
/// Path provided in landlock-rules doesn't exist
|
||||
LandlockPathDoesNotExist(PathBuf),
|
||||
}
|
||||
|
||||
type ValidationResult<T> = std::result::Result<T, ValidationError>;
|
||||
|
@ -336,6 +343,13 @@ impl fmt::Display for ValidationError {
|
|||
InvalidHostData => {
|
||||
write!(f, "Invalid host data format")
|
||||
}
|
||||
LandlockPathDoesNotExist(s) => {
|
||||
write!(
|
||||
f,
|
||||
"Path {:?} provided in landlock-rules does not exist",
|
||||
s.as_path()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -400,6 +414,11 @@ impl fmt::Display for Error {
|
|||
ParseVdpaPathMissing => write!(f, "Error parsing --vdpa: path missing"),
|
||||
ParseTpm(o) => write!(f, "Error parsing --tpm: {o}"),
|
||||
ParseTpmPathMissing => write!(f, "Error parsing --tpm: path missing"),
|
||||
ParseLandlockRules(o) => write!(f, "Error parsing --landlock-rules: {o}"),
|
||||
ParseLandlockMissingFields => write!(
|
||||
f,
|
||||
"Error parsing --landlock-rules: path/flags field missing"
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -450,6 +469,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> {
|
||||
|
@ -512,6 +533,11 @@ 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,
|
||||
memory,
|
||||
|
@ -548,6 +574,8 @@ impl<'a> VmParams<'a> {
|
|||
igvm,
|
||||
#[cfg(feature = "sev_snp")]
|
||||
host_data,
|
||||
landlock_enable,
|
||||
landlock_config,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1693,6 +1721,8 @@ impl ConsoleConfig {
|
|||
mode,
|
||||
iommu,
|
||||
socket,
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1746,7 +1776,13 @@ impl DebugConsoleConfig {
|
|||
}
|
||||
}
|
||||
|
||||
Ok(Self { file, mode, iobase })
|
||||
Ok(Self {
|
||||
file,
|
||||
mode,
|
||||
iobase,
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2110,6 +2146,56 @@ impl TpmConfig {
|
|||
}
|
||||
}
|
||||
|
||||
impl LandlockConfig {
|
||||
pub const SYNTAX: &'static str = "Landlock parameters \
|
||||
\"path=<path/to/{file/dir}>,flags={r,w,x}\". Both path and flags options are required.\"";
|
||||
|
||||
pub fn parse(landlock_rule: &str) -> Result<Self> {
|
||||
let mut parser = OptionParser::new();
|
||||
parser.add("path").add("flags");
|
||||
parser
|
||||
.parse(landlock_rule)
|
||||
.map_err(Error::ParseLandlockRules)?;
|
||||
|
||||
let path = parser
|
||||
.get("path")
|
||||
.map(PathBuf::from)
|
||||
.ok_or(Error::ParseLandlockMissingFields)?;
|
||||
let mut path_flags = landlock::Perms::empty();
|
||||
if let Some(flags) = parser.get("flags") {
|
||||
if flags.chars().count() > 3 {
|
||||
return Err(Error::ParseLandlockRules(OptionParserError::InvalidValue(
|
||||
flags.to_string(),
|
||||
)));
|
||||
}
|
||||
for c in flags.chars() {
|
||||
match c {
|
||||
'r' => path_flags |= landlock::Perms::READ,
|
||||
'w' => path_flags |= landlock::Perms::WRITE,
|
||||
'x' => path_flags |= landlock::Perms::EXECUTE,
|
||||
_ => {
|
||||
return Err(Error::ParseLandlockRules(OptionParserError::UnknownOption(
|
||||
c.to_string(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(LandlockConfig {
|
||||
path,
|
||||
flags: path_flags,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn validate(&self, _vm_config: &VmConfig) -> ValidationResult<()> {
|
||||
if !self.path.exists() {
|
||||
return Err(ValidationError::LandlockPathDoesNotExist(self.path.clone()));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl VmConfig {
|
||||
fn validate_identifier(
|
||||
id_list: &mut BTreeSet<String>,
|
||||
|
@ -2616,6 +2702,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)?,
|
||||
|
@ -2646,6 +2742,8 @@ impl VmConfig {
|
|||
platform,
|
||||
tpm,
|
||||
preserved_fds: None,
|
||||
landlock_enable: vm_params.landlock_enable,
|
||||
landlock_config,
|
||||
};
|
||||
config.validate().map_err(Error::Validation)?;
|
||||
Ok(config)
|
||||
|
@ -2771,6 +2869,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
|
||||
}
|
||||
}
|
||||
|
@ -3268,6 +3367,8 @@ mod tests {
|
|||
iommu: false,
|
||||
file: None,
|
||||
socket: None,
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -3277,6 +3378,8 @@ mod tests {
|
|||
iommu: false,
|
||||
file: None,
|
||||
socket: None,
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -3286,6 +3389,8 @@ mod tests {
|
|||
iommu: false,
|
||||
file: None,
|
||||
socket: None,
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -3295,6 +3400,8 @@ mod tests {
|
|||
iommu: false,
|
||||
file: None,
|
||||
socket: None,
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -3304,6 +3411,8 @@ mod tests {
|
|||
iommu: false,
|
||||
file: Some(PathBuf::from("/tmp/console")),
|
||||
socket: None,
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -3313,6 +3422,8 @@ mod tests {
|
|||
iommu: true,
|
||||
file: None,
|
||||
socket: None,
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -3322,6 +3433,8 @@ mod tests {
|
|||
iommu: true,
|
||||
file: Some(PathBuf::from("/tmp/console")),
|
||||
socket: None,
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
|
@ -3331,6 +3444,8 @@ mod tests {
|
|||
iommu: true,
|
||||
file: None,
|
||||
socket: Some(PathBuf::from("/tmp/serial.sock")),
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
}
|
||||
);
|
||||
Ok(())
|
||||
|
@ -3515,12 +3630,16 @@ mod tests {
|
|||
mode: ConsoleOutputMode::Null,
|
||||
iommu: false,
|
||||
socket: None,
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
},
|
||||
console: ConsoleConfig {
|
||||
file: None,
|
||||
mode: ConsoleOutputMode::Tty,
|
||||
iommu: false,
|
||||
socket: None,
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
},
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
debug_console: DebugConsoleConfig::default(),
|
||||
|
@ -3539,6 +3658,8 @@ mod tests {
|
|||
platform: None,
|
||||
tpm: None,
|
||||
preserved_fds: None,
|
||||
landlock_enable: false,
|
||||
landlock_config: None,
|
||||
};
|
||||
|
||||
assert!(valid_config.validate().is_ok());
|
||||
|
@ -4074,4 +4195,24 @@ mod tests {
|
|||
}
|
||||
let _still_valid_config = still_valid_config.clone();
|
||||
}
|
||||
#[test]
|
||||
fn test_landlock_parsing() -> Result<()> {
|
||||
// should not be empty
|
||||
assert!(LandlockConfig::parse("").is_err());
|
||||
assert_eq!(
|
||||
LandlockConfig::parse("path=/dir/path1,flags=rw")?,
|
||||
LandlockConfig {
|
||||
path: PathBuf::from("/dir/path1"),
|
||||
flags: landlock::Perms::READ | landlock::Perms::WRITE,
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
LandlockConfig::parse("path=/dir/path2,flags=x")?,
|
||||
LandlockConfig {
|
||||
path: PathBuf::from("/dir/path2"),
|
||||
flags: landlock::Perms::EXECUTE,
|
||||
}
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -493,6 +493,9 @@ pub enum DeviceManagerError {
|
|||
|
||||
/// Cannot create a RateLimiterGroup
|
||||
RateLimiterGroupCreate(rate_limiter::group::Error),
|
||||
|
||||
// Missing Pty device info
|
||||
MissingPtyDeviceInfo,
|
||||
}
|
||||
|
||||
pub type DeviceManagerResult<T> = result::Result<T, DeviceManagerError>;
|
||||
|
@ -2059,16 +2062,24 @@ impl DeviceManager {
|
|||
self.console_resize_pipe = resize_pipe.map(Arc::new);
|
||||
Endpoint::PtyPair(file.try_clone().unwrap(), file)
|
||||
} else {
|
||||
let (main, sub, path) =
|
||||
create_pty().map_err(DeviceManagerError::ConsolePtyOpen)?;
|
||||
self.set_raw_mode(&sub)
|
||||
.map_err(DeviceManagerError::SetPtyRaw)?;
|
||||
self.config.lock().unwrap().console.file = Some(path.clone());
|
||||
let file = main.try_clone().unwrap();
|
||||
assert!(resize_pipe.is_none());
|
||||
self.listen_for_sigwinch_on_tty(sub).unwrap();
|
||||
self.console_pty = Some(Arc::new(Mutex::new(PtyPair { main, path })));
|
||||
Endpoint::PtyPair(file.try_clone().unwrap(), file)
|
||||
/* In non-reboot cases pty should be configured in vm_create/vm_restore */
|
||||
if console_config.pty_main.is_none()
|
||||
|| console_config.pty_sub.is_none()
|
||||
|| console_config.file.is_none()
|
||||
{
|
||||
return Err(DeviceManagerError::MissingPtyDeviceInfo);
|
||||
} else {
|
||||
let main = unsafe { File::from_raw_fd(console_config.pty_main.unwrap()) };
|
||||
let sub = unsafe { File::from_raw_fd(console_config.pty_sub.unwrap()) };
|
||||
let path = console_config.file.unwrap().clone();
|
||||
self.set_raw_mode(&sub)
|
||||
.map_err(DeviceManagerError::SetPtyRaw)?;
|
||||
let file = main.try_clone().unwrap();
|
||||
assert!(resize_pipe.is_none());
|
||||
self.listen_for_sigwinch_on_tty(sub).unwrap();
|
||||
self.console_pty = Some(Arc::new(Mutex::new(PtyPair { main, path })));
|
||||
Endpoint::PtyPair(file.try_clone().unwrap(), file)
|
||||
}
|
||||
}
|
||||
}
|
||||
ConsoleOutputMode::Tty => {
|
||||
|
@ -2184,12 +2195,20 @@ impl DeviceManager {
|
|||
self.config.lock().unwrap().serial.file = Some(pty.path.clone());
|
||||
self.serial_pty = Some(Arc::new(Mutex::new(pty)));
|
||||
} else {
|
||||
let (main, sub, path) =
|
||||
create_pty().map_err(DeviceManagerError::SerialPtyOpen)?;
|
||||
self.set_raw_mode(&sub)
|
||||
.map_err(DeviceManagerError::SetPtyRaw)?;
|
||||
self.config.lock().unwrap().serial.file = Some(path.clone());
|
||||
self.serial_pty = Some(Arc::new(Mutex::new(PtyPair { main, path })));
|
||||
/* In non-reboot cases pty should be configured in vm_create/vm_restore */
|
||||
if serial_config.pty_main.is_none()
|
||||
|| serial_config.pty_sub.is_none()
|
||||
|| serial_config.file.is_none()
|
||||
{
|
||||
return Err(DeviceManagerError::MissingPtyDeviceInfo);
|
||||
} else {
|
||||
let main = unsafe { File::from_raw_fd(serial_config.pty_main.unwrap()) };
|
||||
let sub = unsafe { File::from_raw_fd(serial_config.pty_sub.unwrap()) };
|
||||
let path = serial_config.file.unwrap().clone();
|
||||
self.set_raw_mode(&sub)
|
||||
.map_err(DeviceManagerError::SetPtyRaw)?;
|
||||
self.serial_pty = Some(Arc::new(Mutex::new(PtyPair { main, path })));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -2243,12 +2262,24 @@ impl DeviceManager {
|
|||
self.config.lock().unwrap().debug_console.file = Some(pty.path.clone());
|
||||
self.debug_console_pty = Some(Arc::new(Mutex::new(pty)));
|
||||
} else {
|
||||
let (main, sub, path) =
|
||||
create_pty().map_err(DeviceManagerError::DebugconPtyOpen)?;
|
||||
self.set_raw_mode(&sub)
|
||||
.map_err(DeviceManagerError::SetPtyRaw)?;
|
||||
self.config.lock().unwrap().debug_console.file = Some(path.clone());
|
||||
self.debug_console_pty = Some(Arc::new(Mutex::new(PtyPair { main, path })));
|
||||
/* In non-reboot cases pty should be configured in vm_create/vm_restore */
|
||||
if debug_console_config.pty_main.is_none()
|
||||
|| debug_console_config.pty_sub.is_none()
|
||||
|| debug_console_config.file.is_none()
|
||||
{
|
||||
return Err(DeviceManagerError::MissingPtyDeviceInfo);
|
||||
} else {
|
||||
let main = unsafe {
|
||||
File::from_raw_fd(debug_console_config.pty_main.unwrap())
|
||||
};
|
||||
let sub =
|
||||
unsafe { File::from_raw_fd(debug_console_config.pty_sub.unwrap()) };
|
||||
let path = debug_console_config.file.unwrap().clone();
|
||||
self.set_raw_mode(&sub)
|
||||
.map_err(DeviceManagerError::SetPtyRaw)?;
|
||||
self.debug_console_pty =
|
||||
Some(Arc::new(Mutex::new(PtyPair { main, path })));
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
// Copyright © 2024 Microsoft Corporation
|
||||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
use crate::{Deserialize, Serialize};
|
||||
use bitflags::bitflags;
|
||||
use thiserror::Error;
|
||||
|
||||
bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct Perms: u8 {
|
||||
const READ = 0b00000001;
|
||||
const WRITE = 0b00000010;
|
||||
const EXECUTE = 0b00000100;
|
||||
}
|
||||
}
|
||||
use crate::vm_config::LandlockConfig;
|
||||
use landlock::{
|
||||
path_beneath_rules, Access, AccessFs, BitFlags, Ruleset, RulesetAttr, RulesetCreated,
|
||||
RulesetCreatedAttr, RulesetError, ABI,
|
||||
};
|
||||
use std::io::Error as IoError;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Debug, Error)]
|
||||
pub enum LandlockError {
|
||||
/// All RulesetErrors from Landlock library are wrapped in this error
|
||||
#[error("Error creating/adding/restricting ruleset: {0}")]
|
||||
ManageRuleset(#[source] RulesetError),
|
||||
|
||||
/// Error opening Device Path
|
||||
#[error("Error opening path: {0}")]
|
||||
OpenPath(#[source] IoError),
|
||||
|
||||
/// Invalid Path
|
||||
#[error("Invalid path")]
|
||||
InvalidPath,
|
||||
|
||||
#[error("Error creating ptys")]
|
||||
CreatePtysFailed(#[source] std::io::Error),
|
||||
}
|
||||
|
||||
static ABI: ABI = ABI::V3;
|
||||
pub struct Landlock {
|
||||
ruleset: RulesetCreated,
|
||||
}
|
||||
|
||||
impl Landlock {
|
||||
pub fn new() -> Result<Landlock, LandlockError> {
|
||||
let file_access = AccessFs::from_all(ABI);
|
||||
|
||||
let def_ruleset = Ruleset::default()
|
||||
.handle_access(file_access)
|
||||
.map_err(LandlockError::ManageRuleset)?;
|
||||
|
||||
let ruleset = def_ruleset.create().map_err(LandlockError::ManageRuleset)?;
|
||||
|
||||
Ok(Landlock { ruleset })
|
||||
}
|
||||
|
||||
pub fn add_rule(
|
||||
&mut self,
|
||||
path: PathBuf,
|
||||
access: BitFlags<AccessFs>,
|
||||
) -> Result<(), LandlockError> {
|
||||
// path_beneath_rules in landlock crate handles file and directory access rules.
|
||||
// Incoming path/s are passed to path_beneath_rules, so that we don't
|
||||
// have to worry about the type of the path.
|
||||
let paths = vec![path.clone()];
|
||||
let path_beneath_rules = path_beneath_rules(paths, access);
|
||||
self.ruleset
|
||||
.as_mut()
|
||||
.add_rules(path_beneath_rules)
|
||||
.map_err(LandlockError::ManageRuleset)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flags_to_access(&self, flags: Perms) -> BitFlags<AccessFs> {
|
||||
let mut perms = BitFlags::<AccessFs>::empty();
|
||||
if flags & Perms::READ != Perms::empty() {
|
||||
perms |= AccessFs::from_read(ABI);
|
||||
}
|
||||
if flags & Perms::READ != Perms::empty() {
|
||||
perms |= AccessFs::from_write(ABI);
|
||||
}
|
||||
perms
|
||||
}
|
||||
|
||||
pub fn add_rule_with_flags(
|
||||
&mut self,
|
||||
path: PathBuf,
|
||||
flags: Perms,
|
||||
) -> Result<(), LandlockError> {
|
||||
self.add_rule(path.to_path_buf(), self.flags_to_access(flags))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn apply_config(
|
||||
&mut self,
|
||||
landlock_config: Vec<LandlockConfig>,
|
||||
) -> Result<(), LandlockError> {
|
||||
for config in landlock_config {
|
||||
self.add_rule(config.path, self.flags_to_access(config.flags))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn restrict_self(self) -> Result<(), LandlockError> {
|
||||
self.ruleset
|
||||
.restrict_self()
|
||||
.map_err(LandlockError::ManageRuleset)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
109
vmm/src/lib.rs
109
vmm/src/lib.rs
|
@ -18,6 +18,7 @@ use crate::config::{
|
|||
};
|
||||
#[cfg(all(target_arch = "x86_64", feature = "guest_debug"))]
|
||||
use crate::coredump::GuestDebuggable;
|
||||
use crate::landlock::Landlock;
|
||||
use crate::memory_manager::MemoryManager;
|
||||
#[cfg(all(feature = "kvm", target_arch = "x86_64"))]
|
||||
use crate::migration::get_vm_snapshot;
|
||||
|
@ -28,6 +29,8 @@ use anyhow::anyhow;
|
|||
#[cfg(feature = "dbus_api")]
|
||||
use api::dbus::{DBusApiOptions, DBusApiShutdownChannels};
|
||||
use api::http::HttpApiHandle;
|
||||
use device_manager::create_pty;
|
||||
use landlock::LandlockError;
|
||||
use libc::{tcsetattr, termios, EFD_NONBLOCK, SIGINT, SIGTERM, TCSANOW};
|
||||
use memory_manager::MemoryManagerSnapshotData;
|
||||
use pci::PciBdf;
|
||||
|
@ -39,6 +42,7 @@ use std::collections::HashMap;
|
|||
use std::fs::File;
|
||||
use std::io;
|
||||
use std::io::{stdout, Read, Write};
|
||||
use std::os::fd::IntoRawFd;
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
|
||||
use std::os::unix::net::UnixListener;
|
||||
use std::os::unix::net::UnixStream;
|
||||
|
@ -51,6 +55,7 @@ use std::time::Instant;
|
|||
use std::{result, thread};
|
||||
use thiserror::Error;
|
||||
use tracer::trace_scoped;
|
||||
use vm_config::ConsoleOutputMode;
|
||||
use vm_memory::bitmap::AtomicBitmap;
|
||||
use vm_memory::{ReadVolatile, WriteVolatile};
|
||||
use vm_migration::{protocol::*, Migratable};
|
||||
|
@ -73,6 +78,7 @@ mod gdb;
|
|||
#[cfg(feature = "igvm")]
|
||||
mod igvm;
|
||||
pub mod interrupt;
|
||||
pub mod landlock;
|
||||
pub mod memory_manager;
|
||||
pub mod migration;
|
||||
mod pci_segment;
|
||||
|
@ -193,6 +199,10 @@ pub enum Error {
|
|||
|
||||
#[error("Failed to join on threads: {0:?}")]
|
||||
ThreadCleanup(std::boxed::Box<dyn std::any::Any + std::marker::Send>),
|
||||
|
||||
/// Cannot apply landlock LSM
|
||||
#[error("Error applying Landlock LSM: {0}")]
|
||||
ApplyLandlock(LandlockError),
|
||||
}
|
||||
pub type Result<T> = result::Result<T, Error>;
|
||||
|
||||
|
@ -325,6 +335,7 @@ pub fn feature_list() -> Vec<String> {
|
|||
pub fn start_event_monitor_thread(
|
||||
mut monitor: event_monitor::Monitor,
|
||||
seccomp_action: &SeccompAction,
|
||||
landlock_enable: bool,
|
||||
hypervisor_type: hypervisor::HypervisorType,
|
||||
exit_event: EventFd,
|
||||
) -> Result<thread::JoinHandle<Result<()>>> {
|
||||
|
@ -345,6 +356,17 @@ pub fn start_event_monitor_thread(
|
|||
e
|
||||
})?;
|
||||
}
|
||||
if landlock_enable {
|
||||
Landlock::new()
|
||||
.map_err(Error::ApplyLandlock)?
|
||||
.restrict_self()
|
||||
.map_err(Error::ApplyLandlock)
|
||||
.map_err(|e| {
|
||||
error!("Error applying landlock to event monitor thread: {:?}", e);
|
||||
exit_event.write(1).ok();
|
||||
e
|
||||
})?;
|
||||
}
|
||||
|
||||
std::panic::catch_unwind(AssertUnwindSafe(move || {
|
||||
while let Ok(event) = monitor.rx.recv() {
|
||||
|
@ -387,6 +409,7 @@ pub fn start_vmm_thread(
|
|||
exit_event: EventFd,
|
||||
seccomp_action: &SeccompAction,
|
||||
hypervisor: Arc<dyn hypervisor::Hypervisor>,
|
||||
landlock_enable: bool,
|
||||
) -> Result<VmmThreadHandle> {
|
||||
#[cfg(feature = "guest_debug")]
|
||||
let gdb_hw_breakpoints = hypervisor.get_guest_debug_hw_bps();
|
||||
|
@ -427,7 +450,7 @@ pub fn start_vmm_thread(
|
|||
exit_event,
|
||||
)?;
|
||||
|
||||
vmm.setup_signal_handler()?;
|
||||
vmm.setup_signal_handler(landlock_enable)?;
|
||||
|
||||
vmm.control_loop(
|
||||
Rc::new(api_receiver),
|
||||
|
@ -464,6 +487,7 @@ pub fn start_vmm_thread(
|
|||
seccomp_action,
|
||||
exit_event,
|
||||
hypervisor_type,
|
||||
landlock_enable,
|
||||
)?)
|
||||
} else if let Some(http_fd) = http_fd {
|
||||
Some(api::start_http_fd_thread(
|
||||
|
@ -473,6 +497,7 @@ pub fn start_vmm_thread(
|
|||
seccomp_action,
|
||||
exit_event,
|
||||
hypervisor_type,
|
||||
landlock_enable,
|
||||
)?)
|
||||
} else {
|
||||
None
|
||||
|
@ -586,7 +611,7 @@ impl Vmm {
|
|||
}
|
||||
}
|
||||
|
||||
fn setup_signal_handler(&mut self) -> Result<()> {
|
||||
fn setup_signal_handler(&mut self, landlock_enable: bool) -> Result<()> {
|
||||
let signals = Signals::new(Self::HANDLED_SIGNALS);
|
||||
match signals {
|
||||
Ok(signals) => {
|
||||
|
@ -613,6 +638,21 @@ impl Vmm {
|
|||
return;
|
||||
}
|
||||
}
|
||||
if landlock_enable{
|
||||
match Landlock::new() {
|
||||
Ok(landlock) => {
|
||||
let _ = landlock.restrict_self().map_err(Error::ApplyLandlock).map_err(|e| {
|
||||
error!("Error applying Landlock to signal handler thread: {:?}", e);
|
||||
exit_evt.write(1).ok();
|
||||
});
|
||||
}
|
||||
Err(e) => {
|
||||
error!("Error creating Landlock instance: {:?}", e);
|
||||
exit_evt.write(1).ok();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
std::panic::catch_unwind(AssertUnwindSafe(|| {
|
||||
Vmm::signal_handler(signals, original_termios_opt, &exit_evt);
|
||||
}))
|
||||
|
@ -630,6 +670,7 @@ impl Vmm {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn new(
|
||||
vmm_version: VmmVersionInfo,
|
||||
api_evt: EventFd,
|
||||
|
@ -1202,12 +1243,58 @@ impl Vmm {
|
|||
}
|
||||
}
|
||||
|
||||
/* Create ptys here and add the slave paths to Serial, Console and DebugConsole
|
||||
configs. This allows apply_landlok to add the slave paths to landlock rules
|
||||
later.
|
||||
*/
|
||||
fn create_ptys(config: Arc<Mutex<VmConfig>>) -> result::Result<(), LandlockError> {
|
||||
if config.lock().unwrap().console.mode == ConsoleOutputMode::Pty {
|
||||
let (main_fd, sub_fd, path) = create_pty().map_err(LandlockError::CreatePtysFailed)?;
|
||||
config.lock().unwrap().console.pty_main = Some(main_fd.into_raw_fd());
|
||||
config.lock().unwrap().console.pty_sub = Some(sub_fd.into_raw_fd());
|
||||
config.lock().unwrap().console.file = Some(path);
|
||||
}
|
||||
|
||||
if config.lock().unwrap().serial.mode == ConsoleOutputMode::Pty {
|
||||
let (main_fd, sub_fd, path) = create_pty().map_err(LandlockError::CreatePtysFailed)?;
|
||||
config.lock().unwrap().serial.pty_main = Some(main_fd.into_raw_fd());
|
||||
config.lock().unwrap().serial.pty_sub = Some(sub_fd.into_raw_fd());
|
||||
config.lock().unwrap().serial.file = Some(path);
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
if config.lock().unwrap().debug_console.mode == ConsoleOutputMode::Pty {
|
||||
let (main_fd, sub_fd, path) = create_pty().map_err(LandlockError::CreatePtysFailed)?;
|
||||
config.lock().unwrap().debug_console.pty_main = Some(main_fd.into_raw_fd());
|
||||
config.lock().unwrap().debug_console.pty_sub = Some(sub_fd.into_raw_fd());
|
||||
config.lock().unwrap().debug_console.file = Some(path);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn apply_landlock(vm_config: Arc<Mutex<VmConfig>>) -> result::Result<(), LandlockError> {
|
||||
create_ptys(vm_config.clone())?;
|
||||
vm_config.lock().unwrap().apply_landlock()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl RequestHandler for Vmm {
|
||||
fn vm_create(&mut self, config: Arc<Mutex<VmConfig>>) -> result::Result<(), VmError> {
|
||||
// We only store the passed VM config.
|
||||
// The VM will be created when being asked to boot it.
|
||||
if self.vm_config.is_none() {
|
||||
self.vm_config = Some(config);
|
||||
if self
|
||||
.vm_config
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.landlock_enable
|
||||
{
|
||||
apply_landlock(self.vm_config.as_mut().unwrap().clone())
|
||||
.map_err(VmError::ApplyLandlock)?;
|
||||
}
|
||||
Ok(())
|
||||
} else {
|
||||
Err(VmError::VmAlreadyCreated)
|
||||
|
@ -1363,6 +1450,18 @@ impl RequestHandler for Vmm {
|
|||
)?;
|
||||
self.vm = Some(vm);
|
||||
|
||||
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.
|
||||
if let Some(ref mut vm) = self.vm {
|
||||
vm.restore()
|
||||
|
@ -2130,12 +2229,16 @@ mod unit_tests {
|
|||
mode: ConsoleOutputMode::Null,
|
||||
iommu: false,
|
||||
socket: None,
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
},
|
||||
console: ConsoleConfig {
|
||||
file: None,
|
||||
mode: ConsoleOutputMode::Tty,
|
||||
iommu: false,
|
||||
socket: None,
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
},
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
debug_console: DebugConsoleConfig::default(),
|
||||
|
@ -2154,6 +2257,8 @@ mod unit_tests {
|
|||
platform: None,
|
||||
tpm: None,
|
||||
preserved_fds: None,
|
||||
landlock_enable: false,
|
||||
landlock_config: None,
|
||||
}))
|
||||
}
|
||||
|
||||
|
|
|
@ -495,9 +495,12 @@ fn signal_handler_thread_rules() -> Result<Vec<(i64, Vec<SeccompRule>)>, Backend
|
|||
(libc::SYS_exit_group, vec![]),
|
||||
(libc::SYS_futex, vec![]),
|
||||
(libc::SYS_ioctl, create_signal_handler_ioctl_seccomp_rule()?),
|
||||
(libc::SYS_landlock_create_ruleset, vec![]),
|
||||
(libc::SYS_landlock_restrict_self, vec![]),
|
||||
(libc::SYS_madvise, vec![]),
|
||||
(libc::SYS_mmap, vec![]),
|
||||
(libc::SYS_munmap, vec![]),
|
||||
(libc::SYS_prctl, vec![]),
|
||||
(libc::SYS_recvfrom, vec![]),
|
||||
(libc::SYS_rt_sigprocmask, vec![]),
|
||||
(libc::SYS_rt_sigreturn, vec![]),
|
||||
|
@ -600,6 +603,9 @@ fn vmm_thread_rules(
|
|||
(libc::SYS_io_uring_setup, vec![]),
|
||||
(libc::SYS_io_uring_register, vec![]),
|
||||
(libc::SYS_kill, vec![]),
|
||||
(libc::SYS_landlock_create_ruleset, vec![]),
|
||||
(libc::SYS_landlock_add_rule, vec![]),
|
||||
(libc::SYS_landlock_restrict_self, vec![]),
|
||||
(libc::SYS_listen, vec![]),
|
||||
(libc::SYS_lseek, vec![]),
|
||||
(libc::SYS_madvise, vec![]),
|
||||
|
@ -838,10 +844,13 @@ fn http_api_thread_rules() -> Result<Vec<(i64, Vec<SeccompRule>)>, BackendError>
|
|||
(libc::SYS_futex, vec![]),
|
||||
(libc::SYS_getrandom, vec![]),
|
||||
(libc::SYS_ioctl, create_api_ioctl_seccomp_rule()?),
|
||||
(libc::SYS_landlock_create_ruleset, vec![]),
|
||||
(libc::SYS_landlock_restrict_self, vec![]),
|
||||
(libc::SYS_madvise, vec![]),
|
||||
(libc::SYS_mmap, vec![]),
|
||||
(libc::SYS_mprotect, vec![]),
|
||||
(libc::SYS_munmap, vec![]),
|
||||
(libc::SYS_prctl, vec![]),
|
||||
(libc::SYS_recvfrom, vec![]),
|
||||
(libc::SYS_recvmsg, vec![]),
|
||||
(libc::SYS_sched_yield, vec![]),
|
||||
|
@ -891,9 +900,13 @@ fn dbus_api_thread_rules() -> Result<Vec<(i64, Vec<SeccompRule>)>, BackendError>
|
|||
fn event_monitor_thread_rules() -> Result<Vec<(i64, Vec<SeccompRule>)>, BackendError> {
|
||||
Ok(vec![
|
||||
(libc::SYS_brk, vec![]),
|
||||
(libc::SYS_close, vec![]),
|
||||
(libc::SYS_futex, vec![]),
|
||||
(libc::SYS_landlock_create_ruleset, vec![]),
|
||||
(libc::SYS_landlock_restrict_self, vec![]),
|
||||
(libc::SYS_mmap, vec![]),
|
||||
(libc::SYS_munmap, vec![]),
|
||||
(libc::SYS_prctl, vec![]),
|
||||
(libc::SYS_sched_yield, vec![]),
|
||||
(libc::SYS_write, vec![]),
|
||||
])
|
||||
|
|
|
@ -27,6 +27,7 @@ use crate::device_tree::DeviceTree;
|
|||
use crate::gdb::{Debuggable, DebuggableError, GdbRequestPayload, GdbResponsePayload};
|
||||
#[cfg(feature = "igvm")]
|
||||
use crate::igvm::igvm_loader;
|
||||
use crate::landlock::LandlockError;
|
||||
use crate::memory_manager::{
|
||||
Error as MemoryManagerError, MemoryManager, MemoryManagerSnapshotData,
|
||||
};
|
||||
|
@ -121,6 +122,9 @@ pub enum Error {
|
|||
#[error("Cannot load the kernel command line in memory: {0}")]
|
||||
LoadCmdLine(#[source] linux_loader::loader::Error),
|
||||
|
||||
#[error("Failed to apply landlock config during vm_create: {0}")]
|
||||
ApplyLandlock(#[source] LandlockError),
|
||||
|
||||
#[error("Cannot modify the kernel command line: {0}")]
|
||||
CmdLineInsertStr(#[source] linux_loader::cmdline::Error),
|
||||
|
||||
|
|
|
@ -2,11 +2,17 @@
|
|||
//
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
//
|
||||
use crate::{
|
||||
landlock::{self, LandlockError},
|
||||
Landlock,
|
||||
};
|
||||
use net_util::MacAddr;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{net::Ipv4Addr, path::PathBuf};
|
||||
use std::{fs, net::Ipv4Addr, os::fd::RawFd, path::PathBuf, result};
|
||||
use virtio_devices::RateLimiterConfig;
|
||||
|
||||
pub type LandlockResult<T> = result::Result<T, LandlockError>;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct CpuAffinity {
|
||||
pub vcpu: u8,
|
||||
|
@ -115,7 +121,15 @@ pub struct MemoryZoneConfig {
|
|||
#[serde(default)]
|
||||
pub prefault: bool,
|
||||
}
|
||||
|
||||
impl MemoryZoneConfig {
|
||||
pub fn apply_landlock(self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
let memory_zone_flags = landlock::Perms::READ | landlock::Perms::WRITE;
|
||||
if let Some(file) = &self.file {
|
||||
landlock.add_rule_with_flags(file.to_path_buf(), memory_zone_flags)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
|
||||
pub enum HotplugMethod {
|
||||
#[default]
|
||||
|
@ -229,6 +243,16 @@ pub struct DiskConfig {
|
|||
pub queue_affinity: Option<Vec<VirtQueueAffinity>>,
|
||||
}
|
||||
|
||||
impl DiskConfig {
|
||||
pub fn apply_landlock(self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
// Allow Read and Write permissions to Disk Paths
|
||||
let disk_flags = landlock::Perms::READ | landlock::Perms::WRITE;
|
||||
if let Some(path) = self.path {
|
||||
landlock.add_rule_with_flags(path, disk_flags)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
pub const DEFAULT_DISK_NUM_QUEUES: usize = 1;
|
||||
|
||||
pub fn default_diskconfig_num_queues() -> usize {
|
||||
|
@ -362,6 +386,15 @@ impl Default for RngConfig {
|
|||
}
|
||||
}
|
||||
|
||||
impl RngConfig {
|
||||
pub fn apply_landlock(&mut self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
// Allow Read permissions to Rng Paths
|
||||
let rng_flags = landlock::Perms::READ;
|
||||
landlock.add_rule_with_flags(self.src.to_path_buf(), rng_flags)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct BalloonConfig {
|
||||
pub size: u64,
|
||||
|
@ -395,6 +428,14 @@ pub fn default_fsconfig_queue_size() -> u16 {
|
|||
1024
|
||||
}
|
||||
|
||||
impl FsConfig {
|
||||
pub fn apply_landlock(self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
let fs_flags = landlock::Perms::READ | landlock::Perms::WRITE;
|
||||
landlock.add_rule_with_flags(self.socket, fs_flags)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct PmemConfig {
|
||||
pub file: PathBuf,
|
||||
|
@ -410,6 +451,15 @@ pub struct PmemConfig {
|
|||
pub pci_segment: u16,
|
||||
}
|
||||
|
||||
impl PmemConfig {
|
||||
pub fn apply_landlock(self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
// Allow Read and Write permissions to Pmem Paths
|
||||
let pmem_flags = landlock::Perms::READ | landlock::Perms::WRITE;
|
||||
landlock.add_rule_with_flags(self.file, pmem_flags)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub enum ConsoleOutputMode {
|
||||
Off,
|
||||
|
@ -428,12 +478,29 @@ pub struct ConsoleConfig {
|
|||
#[serde(default)]
|
||||
pub iommu: bool,
|
||||
pub socket: Option<PathBuf>,
|
||||
#[serde(skip)]
|
||||
pub pty_main: Option<RawFd>,
|
||||
pub pty_sub: Option<RawFd>,
|
||||
}
|
||||
|
||||
pub fn default_consoleconfig_file() -> Option<PathBuf> {
|
||||
None
|
||||
}
|
||||
|
||||
impl ConsoleConfig {
|
||||
pub fn apply_landlock(self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
let console_flags = landlock::Perms::READ | landlock::Perms::WRITE;
|
||||
|
||||
if let Some(file) = self.file {
|
||||
landlock.add_rule_with_flags(file, console_flags)?;
|
||||
}
|
||||
if let Some(socket) = self.socket {
|
||||
landlock.add_rule_with_flags(socket, console_flags)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct DebugConsoleConfig {
|
||||
|
@ -442,6 +509,9 @@ pub struct DebugConsoleConfig {
|
|||
pub mode: ConsoleOutputMode,
|
||||
/// Optionally dedicated I/O-port, if the default port should not be used.
|
||||
pub iobase: Option<u16>,
|
||||
#[serde(skip)]
|
||||
pub pty_main: Option<RawFd>,
|
||||
pub pty_sub: Option<RawFd>,
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
|
@ -451,9 +521,21 @@ impl Default for DebugConsoleConfig {
|
|||
file: None,
|
||||
mode: ConsoleOutputMode::Off,
|
||||
iobase: Some(devices::debug_console::DEFAULT_PORT as u16),
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
impl DebugConsoleConfig {
|
||||
pub fn apply_landlock(self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
let debug_console_flags = landlock::Perms::READ | landlock::Perms::WRITE;
|
||||
if let Some(file) = self.file {
|
||||
landlock.add_rule_with_flags(file, debug_console_flags)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct DeviceConfig {
|
||||
|
@ -468,6 +550,23 @@ pub struct DeviceConfig {
|
|||
pub x_nv_gpudirect_clique: Option<u8>,
|
||||
}
|
||||
|
||||
impl DeviceConfig {
|
||||
pub fn apply_landlock(self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
let device_flags = landlock::Perms::READ | landlock::Perms::WRITE;
|
||||
let device_path = fs::read_link(self.path).map_err(LandlockError::OpenPath)?;
|
||||
let iommu_group = device_path.file_name();
|
||||
if let Some(iommu_group) = iommu_group {
|
||||
if let Some(iommu_group_str) = iommu_group.to_str() {
|
||||
let vfio_group_path = "/dev/vfio/".to_owned() + iommu_group_str;
|
||||
landlock.add_rule_with_flags(vfio_group_path.into(), device_flags)?;
|
||||
} else {
|
||||
return Err(LandlockError::InvalidPath);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct UserDeviceConfig {
|
||||
pub socket: PathBuf,
|
||||
|
@ -477,6 +576,14 @@ pub struct UserDeviceConfig {
|
|||
pub pci_segment: u16,
|
||||
}
|
||||
|
||||
impl UserDeviceConfig {
|
||||
pub fn apply_landlock(self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
let user_device_flags = landlock::Perms::READ | landlock::Perms::WRITE;
|
||||
landlock.add_rule_with_flags(self.socket, user_device_flags)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct VdpaConfig {
|
||||
pub path: PathBuf,
|
||||
|
@ -494,6 +601,14 @@ pub fn default_vdpaconfig_num_queues() -> usize {
|
|||
1
|
||||
}
|
||||
|
||||
impl VdpaConfig {
|
||||
pub fn apply_landlock(self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
let vdpa_flags = landlock::Perms::READ | landlock::Perms::WRITE;
|
||||
landlock.add_rule_with_flags(self.path, vdpa_flags)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct VsockConfig {
|
||||
pub cid: u32,
|
||||
|
@ -506,6 +621,13 @@ pub struct VsockConfig {
|
|||
pub pci_segment: u16,
|
||||
}
|
||||
|
||||
impl VsockConfig {
|
||||
pub fn apply_landlock(self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
let vsock_flags = landlock::Perms::READ | landlock::Perms::WRITE;
|
||||
landlock.add_rule_with_flags(self.socket, vsock_flags)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct SgxEpcConfig {
|
||||
|
@ -559,12 +681,38 @@ pub struct PayloadConfig {
|
|||
pub host_data: Option<String>,
|
||||
}
|
||||
|
||||
impl PayloadConfig {
|
||||
pub fn apply_landlock(self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
let payload_flags = landlock::Perms::READ;
|
||||
|
||||
if let Some(firmware) = self.firmware.as_ref() {
|
||||
landlock.add_rule_with_flags(firmware.to_path_buf(), payload_flags)?;
|
||||
}
|
||||
|
||||
if let Some(kernel) = self.kernel.as_ref() {
|
||||
landlock.add_rule_with_flags(kernel.to_path_buf(), payload_flags)?;
|
||||
}
|
||||
|
||||
if let Some(initramfs) = self.initramfs.as_ref() {
|
||||
landlock.add_rule_with_flags(initramfs.to_path_buf(), payload_flags)?;
|
||||
}
|
||||
|
||||
#[cfg(feature = "igvm")]
|
||||
if let Some(igvm) = self.igvm.as_ref() {
|
||||
landlock.add_rule_with_flags(igvm.to_path_buf(), payload_flags)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn default_serial() -> ConsoleConfig {
|
||||
ConsoleConfig {
|
||||
file: None,
|
||||
mode: ConsoleOutputMode::Null,
|
||||
iommu: false,
|
||||
socket: None,
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -574,6 +722,8 @@ pub fn default_console() -> ConsoleConfig {
|
|||
mode: ConsoleOutputMode::Tty,
|
||||
iommu: false,
|
||||
socket: None,
|
||||
pty_main: None,
|
||||
pty_sub: None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -582,6 +732,24 @@ pub struct TpmConfig {
|
|||
pub socket: PathBuf,
|
||||
}
|
||||
|
||||
impl TpmConfig {
|
||||
pub fn apply_landlock(self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
let tpm_flags = landlock::Perms::READ | landlock::Perms::WRITE;
|
||||
landlock.add_rule_with_flags(self.socket, tpm_flags)
|
||||
}
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct LandlockConfig {
|
||||
pub path: PathBuf,
|
||||
pub flags: landlock::Perms,
|
||||
}
|
||||
|
||||
impl LandlockConfig {
|
||||
pub fn apply_landlock(self, landlock: &mut Landlock) -> LandlockResult<()> {
|
||||
landlock.add_rule_with_flags(self.path, self.flags)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct VmConfig {
|
||||
#[serde(default)]
|
||||
|
@ -628,4 +796,96 @@ 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 fn apply_landlock(&self) -> LandlockResult<()> {
|
||||
let mut landlock = Landlock::new()?;
|
||||
|
||||
if let Some(mem_zones) = self.memory.zones.as_ref() {
|
||||
for zone in mem_zones.iter() {
|
||||
zone.clone().apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
let disks = self.disks.as_ref();
|
||||
if let Some(disks) = disks {
|
||||
for disk in disks.iter() {
|
||||
disk.clone().apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.rng.clone().apply_landlock(&mut landlock)?;
|
||||
|
||||
if let Some(fs_configs) = self.fs.as_ref() {
|
||||
for fs_config in fs_configs.iter() {
|
||||
fs_config.clone().apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(pmem_configs) = self.pmem.as_ref() {
|
||||
for pmem_config in pmem_configs.iter() {
|
||||
pmem_config.clone().apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
self.console.clone().apply_landlock(&mut landlock)?;
|
||||
self.serial.clone().apply_landlock(&mut landlock)?;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
self.debug_console.clone().apply_landlock(&mut landlock)?;
|
||||
}
|
||||
|
||||
if let Some(devices) = self.devices.as_ref() {
|
||||
let vfio_dev_flags = landlock::Perms::READ | landlock::Perms::WRITE;
|
||||
landlock.add_rule_with_flags("/dev/vfio/vfio".into(), vfio_dev_flags)?;
|
||||
for device in devices.iter() {
|
||||
device.clone().apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(user_devices) = self.user_devices.as_ref() {
|
||||
for user_devices in user_devices.iter() {
|
||||
user_devices.clone().apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(vdpa_configs) = self.vdpa.as_ref() {
|
||||
for vdpa_config in vdpa_configs.iter() {
|
||||
vdpa_config.clone().apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(vsock_config) = self.vsock.as_ref() {
|
||||
vsock_config.clone().apply_landlock(&mut landlock)?;
|
||||
}
|
||||
|
||||
let payload = self.payload.as_ref();
|
||||
if let Some(payload) = payload {
|
||||
payload.clone().apply_landlock(&mut landlock)?;
|
||||
}
|
||||
|
||||
if let Some(tpm_config) = self.tpm.as_ref() {
|
||||
tpm_config.clone().apply_landlock(&mut landlock)?;
|
||||
}
|
||||
|
||||
if self.net.is_some() {
|
||||
let net_flags = landlock::Perms::READ | landlock::Perms::WRITE;
|
||||
landlock.add_rule_with_flags("/dev/net/tun".into(), net_flags)?;
|
||||
}
|
||||
|
||||
if self.landlock_config.is_some() {
|
||||
for landlock_config in self.landlock_config.as_ref().unwrap() {
|
||||
landlock_config.clone().apply_landlock(&mut landlock)?;
|
||||
}
|
||||
}
|
||||
|
||||
landlock.restrict_self()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue