vmm: Separate seccomp filters between VMM and API threads

This separates the filters used between the VMM and API threads, so that
we can apply different rules for each thread.

Signed-off-by: Sebastien Boeuf <sebastien.boeuf@intel.com>
This commit is contained in:
Sebastien Boeuf 2020-03-20 17:57:03 +01:00
parent 5120c275a2
commit feb8d7ae90
3 changed files with 57 additions and 11 deletions

View File

@ -7,7 +7,7 @@ use crate::api::http_endpoint::{
VmActionHandler, VmAddDevice, VmCreate, VmInfo, VmRemoveDevice, VmResize, VmmPing, VmmShutdown, VmActionHandler, VmAddDevice, VmCreate, VmInfo, VmRemoveDevice, VmResize, VmmPing, VmmShutdown,
}; };
use crate::api::{ApiRequest, VmAction}; use crate::api::{ApiRequest, VmAction};
use crate::seccomp_filters::get_seccomp_filter; use crate::seccomp_filters::{get_seccomp_filter, Thread};
use crate::{Error, Result}; use crate::{Error, Result};
use micro_http::{HttpServer, MediaType, Request, Response, StatusCode, Version}; use micro_http::{HttpServer, MediaType, Request, Response, StatusCode, Version};
use seccomp::{SeccompFilter, SeccompLevel}; use seccomp::{SeccompFilter, SeccompLevel};
@ -101,7 +101,7 @@ pub fn start_http_thread(
// Retrieve seccomp filter for API thread // Retrieve seccomp filter for API thread
let api_seccomp_filter = let api_seccomp_filter =
get_seccomp_filter(seccomp_level).map_err(Error::CreateSeccompFilter)?; get_seccomp_filter(seccomp_level, Thread::Api).map_err(Error::CreateSeccompFilter)?;
thread::Builder::new() thread::Builder::new()
.name("http-server".to_string()) .name("http-server".to_string())

View File

@ -17,7 +17,7 @@ extern crate vmm_sys_util;
use crate::api::{ApiError, ApiRequest, ApiResponse, ApiResponsePayload, VmInfo, VmmPingResponse}; use crate::api::{ApiError, ApiRequest, ApiResponse, ApiResponsePayload, VmInfo, VmmPingResponse};
use crate::config::{DeviceConfig, VmConfig}; use crate::config::{DeviceConfig, VmConfig};
use crate::seccomp_filters::get_seccomp_filter; use crate::seccomp_filters::{get_seccomp_filter, Thread};
use crate::vm::{Error as VmError, Vm, VmState}; use crate::vm::{Error as VmError, Vm, VmState};
use libc::EFD_NONBLOCK; use libc::EFD_NONBLOCK;
use seccomp::{SeccompFilter, SeccompLevel}; use seccomp::{SeccompFilter, SeccompLevel};
@ -176,7 +176,7 @@ pub fn start_vmm_thread(
// Retrieve seccomp filter // Retrieve seccomp filter
let vmm_seccomp_filter = let vmm_seccomp_filter =
get_seccomp_filter(seccomp_level).map_err(Error::CreateSeccompFilter)?; get_seccomp_filter(seccomp_level, Thread::Vmm).map_err(Error::CreateSeccompFilter)?;
// Find the path that the "/proc/<pid>/exe" symlink points to. Must be done before spawning // Find the path that the "/proc/<pid>/exe" symlink points to. Must be done before spawning
// a thread as Rust does not put the child threads in the same thread group which prevents the // a thread as Rust does not put the child threads in the same thread group which prevents the

View File

@ -12,6 +12,11 @@ use seccomp::{
}; };
use std::convert::TryInto; use std::convert::TryInto;
pub enum Thread {
Vmm,
Api,
}
/// Shorthand for chaining `SeccompCondition`s with the `and` operator in a `SeccompRule`. /// Shorthand for chaining `SeccompCondition`s with the `and` operator in a `SeccompRule`.
/// The rule will take the `Allow` action if _all_ the conditions are true. /// The rule will take the `Allow` action if _all_ the conditions are true.
/// ///
@ -94,7 +99,7 @@ const VFIO_IOMMU_MAP_DMA: u64 = 0x3b71;
const VFIO_IOMMU_UNMAP_DMA: u64 = 0x3b72; const VFIO_IOMMU_UNMAP_DMA: u64 = 0x3b72;
const VFIO_DEVICE_IOEVENTFD: u64 = 0x3b74; const VFIO_DEVICE_IOEVENTFD: u64 = 0x3b74;
fn create_ioctl_seccomp_rule() -> Result<Vec<SeccompRule>, Error> { fn create_vmm_ioctl_seccomp_rule() -> Result<Vec<SeccompRule>, Error> {
Ok(or![ Ok(or![
and![Cond::new(1, ArgLen::DWORD, Eq, FIOCLEX)?], and![Cond::new(1, ArgLen::DWORD, Eq, FIOCLEX)?],
and![Cond::new(1, ArgLen::DWORD, Eq, FIONBIO)?], and![Cond::new(1, ArgLen::DWORD, Eq, FIONBIO)?],
@ -156,9 +161,13 @@ fn create_ioctl_seccomp_rule() -> Result<Vec<SeccompRule>, Error> {
]) ])
} }
/// The default filter containing the white listed syscall rules required by fn create_api_ioctl_seccomp_rule() -> Result<Vec<SeccompRule>, Error> {
/// the VMM to function. Ok(or![and![Cond::new(1, ArgLen::DWORD, Eq, FIONBIO)?],])
pub fn default_filter() -> Result<SeccompFilter, Error> { }
/// The filter containing the white listed syscall rules required by the VMM to
/// function.
pub fn vmm_thread_filter() -> Result<SeccompFilter, Error> {
Ok(SeccompFilter::new( Ok(SeccompFilter::new(
vec![ vec![
allow_syscall(libc::SYS_accept4), allow_syscall(libc::SYS_accept4),
@ -187,7 +196,7 @@ pub fn default_filter() -> Result<SeccompFilter, Error> {
allow_syscall(libc::SYS_getpid), allow_syscall(libc::SYS_getpid),
allow_syscall(libc::SYS_getrandom), allow_syscall(libc::SYS_getrandom),
allow_syscall(libc::SYS_getuid), allow_syscall(libc::SYS_getuid),
allow_syscall_if(libc::SYS_ioctl, create_ioctl_seccomp_rule()?), allow_syscall_if(libc::SYS_ioctl, create_vmm_ioctl_seccomp_rule()?),
allow_syscall(libc::SYS_listen), allow_syscall(libc::SYS_listen),
allow_syscall(libc::SYS_lseek), allow_syscall(libc::SYS_lseek),
allow_syscall(libc::SYS_madvise), allow_syscall(libc::SYS_madvise),
@ -234,11 +243,48 @@ pub fn default_filter() -> Result<SeccompFilter, Error> {
)?) )?)
} }
/// The filter containing the white listed syscall rules required by the API to
/// function.
pub fn api_thread_filter() -> Result<SeccompFilter, Error> {
Ok(SeccompFilter::new(
vec![
allow_syscall(libc::SYS_accept4),
allow_syscall(libc::SYS_bind),
allow_syscall(libc::SYS_close),
allow_syscall(libc::SYS_dup),
allow_syscall(libc::SYS_epoll_create1),
allow_syscall(libc::SYS_epoll_ctl),
allow_syscall(libc::SYS_epoll_wait),
allow_syscall(libc::SYS_exit),
allow_syscall(libc::SYS_futex),
allow_syscall(libc::SYS_getrandom),
allow_syscall_if(libc::SYS_ioctl, create_api_ioctl_seccomp_rule()?),
allow_syscall(libc::SYS_listen),
allow_syscall(libc::SYS_madvise),
allow_syscall(libc::SYS_munmap),
allow_syscall(libc::SYS_recvfrom),
allow_syscall(libc::SYS_sigaltstack),
allow_syscall(libc::SYS_socket),
allow_syscall(libc::SYS_write),
]
.into_iter()
.collect(),
SeccompAction::Trap,
)?)
}
/// Generate a BPF program based on a seccomp level value. /// Generate a BPF program based on a seccomp level value.
pub fn get_seccomp_filter(seccomp_level: &SeccompLevel) -> Result<BpfProgram, SeccompError> { pub fn get_seccomp_filter(
seccomp_level: &SeccompLevel,
thread_type: Thread,
) -> Result<BpfProgram, SeccompError> {
let filter = match thread_type {
Thread::Vmm => vmm_thread_filter(),
Thread::Api => api_thread_filter(),
};
match *seccomp_level { match *seccomp_level {
SeccompLevel::None => Ok(vec![]), SeccompLevel::None => Ok(vec![]),
_ => default_filter() _ => filter
.and_then(|filter| filter.try_into()) .and_then(|filter| filter.try_into())
.map_err(SeccompError::SeccompFilter), .map_err(SeccompError::SeccompFilter),
} }