From 3be50adab98a3ab8ee156301693c5218d14c3452 Mon Sep 17 00:00:00 2001 From: Manabu Sugimoto Date: Fri, 16 Jul 2021 14:50:58 +0900 Subject: [PATCH 1/5] agent: Add support for Seccomp The kata-agent supports seccomp feature based on the OCI runtime specification. This seccomp capability in the kata-agent is enabled by default. However, it is not enforced by default: users need to enable that by setting `disable_guest_seccomp` to `false` in the main configuration file. Fixes: #1476 Signed-off-by: Manabu Sugimoto --- src/agent/Cargo.lock | 38 ++++ src/agent/Cargo.toml | 3 + src/agent/Makefile | 21 +- src/agent/README.md | 1 + src/agent/rustjail/Cargo.toml | 4 + src/agent/rustjail/src/container.rs | 25 ++- src/agent/rustjail/src/lib.rs | 2 + src/agent/rustjail/src/seccomp.rs | 237 +++++++++++++++++++++++ src/agent/src/config.rs | 3 + src/agent/src/rpc.rs | 10 +- src/runtime/virtcontainers/kata_agent.go | 4 + 11 files changed, 342 insertions(+), 6 deletions(-) create mode 100644 src/agent/rustjail/src/seccomp.rs diff --git a/src/agent/Cargo.lock b/src/agent/Cargo.lock index 517088b1e..2988b73b9 100644 --- a/src/agent/Cargo.lock +++ b/src/agent/Cargo.lock @@ -594,6 +594,24 @@ dependencies = [ "rle-decode-fast", ] +[[package]] +name = "libseccomp" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36ad71a5b66ceef3acfe6a3178b29b4da063f8bcb2c36dab666d52a7a9cfdb86" +dependencies = [ + "libc", + "libseccomp-sys", + "nix 0.17.0", + "pkg-config", +] + +[[package]] +name = "libseccomp-sys" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "539912de229a4fc16e507e8df12a394038a524a5b5b6c92045ad344472aac475" + [[package]] name = "lock_api" version = "0.4.4" @@ -763,6 +781,19 @@ dependencies = [ "void", ] +[[package]] +name = "nix" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e4785f2c3b7589a0d0c1dd60285e1188adac4006e8abd6dd578e1567027363" +dependencies = [ + "bitflags", + "cc", + "cfg-if 0.1.10", + "libc", + "void", +] + [[package]] name = "nix" version = "0.19.1" @@ -977,6 +1008,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + [[package]] name = "ppv-lite86" version = "0.2.10" @@ -1284,6 +1321,7 @@ dependencies = [ "inotify", "lazy_static", "libc", + "libseccomp", "nix 0.21.0", "oci", "path-absolutize", diff --git a/src/agent/Cargo.toml b/src/agent/Cargo.toml index c4864ef50..6d0189b51 100644 --- a/src/agent/Cargo.toml +++ b/src/agent/Cargo.toml @@ -75,3 +75,6 @@ members = [ [profile.release] lto = true + +[features] +seccomp = ["rustjail/seccomp"] diff --git a/src/agent/Makefile b/src/agent/Makefile index 4806c39f4..24a98e34b 100644 --- a/src/agent/Makefile +++ b/src/agent/Makefile @@ -27,6 +27,20 @@ COMMIT_MSG = $(if $(COMMIT),$(COMMIT),unknown) # Exported to allow cargo to see it export VERSION_COMMIT := $(if $(COMMIT),$(VERSION)-$(COMMIT),$(VERSION)) +EXTRA_RUSTFEATURES := + +##VAR SECCOMP=yes|no define if agent enables seccomp feature +SECCOMP := yes + +# Enable seccomp feature of rust build +ifeq ($(SECCOMP),yes) + override EXTRA_RUSTFEATURES += seccomp +endif + +ifneq ($(EXTRA_RUSTFEATURES),) + override EXTRA_RUSTFEATURES := --features $(EXTRA_RUSTFEATURES) +endif + include ../../utils.mk TARGET_PATH = target/$(TRIPLE)/$(BUILD_TYPE)/$(TARGET) @@ -90,15 +104,14 @@ default: $(TARGET) show-header $(TARGET): $(GENERATED_CODE) $(TARGET_PATH) $(TARGET_PATH): $(SOURCES) | show-summary - @RUSTFLAGS="$(EXTRA_RUSTFLAGS) --deny warnings" cargo build --target $(TRIPLE) --$(BUILD_TYPE) + @RUSTFLAGS="$(EXTRA_RUSTFLAGS) --deny warnings" cargo build --target $(TRIPLE) --$(BUILD_TYPE) $(EXTRA_RUSTFEATURES) $(GENERATED_FILES): %: %.in @sed $(foreach r,$(GENERATED_REPLACEMENTS),-e 's|@$r@|$($r)|g') "$<" > "$@" ##TARGET optimize: optimized build optimize: $(SOURCES) | show-summary show-header - @RUSTFLAGS="-C link-arg=-s $(EXTRA_RUSTFLAGS) --deny-warnings" cargo build --target $(TRIPLE) --$(BUILD_TYPE) - + @RUSTFLAGS="-C link-arg=-s $(EXTRA_RUSTFLAGS) --deny-warnings" cargo build --target $(TRIPLE) --$(BUILD_TYPE) $(EXTRA_RUSTFEATURES) ##TARGET clippy: run clippy linter clippy: $(GENERATED_CODE) @@ -127,7 +140,7 @@ vendor: #TARGET test: run cargo tests test: - @cargo test --all --target $(TRIPLE) -- --nocapture + @cargo test --all --target $(TRIPLE) $(EXTRA_RUSTFEATURES) -- --nocapture ##TARGET check: run test check: clippy format diff --git a/src/agent/README.md b/src/agent/README.md index cc47453bc..08f1c1e8e 100644 --- a/src/agent/README.md +++ b/src/agent/README.md @@ -19,6 +19,7 @@ After that, we drafted the initial code here, and any contributions are welcome. | I/O stream | :white_check_mark: | | Cgroups | :white_check_mark: | | Capabilities, `rlimit`, readonly path, masked path, users | :white_check_mark: | +| Seccomp | :white_check_mark: | | container stats (`stats_container`) | :white_check_mark: | | Hooks | :white_check_mark: | | **Agent Features & APIs** | diff --git a/src/agent/rustjail/Cargo.toml b/src/agent/rustjail/Cargo.toml index e350b2f06..497a86210 100644 --- a/src/agent/rustjail/Cargo.toml +++ b/src/agent/rustjail/Cargo.toml @@ -30,7 +30,11 @@ tokio = { version = "1.2.0", features = ["sync", "io-util", "process", "time", " futures = "0.3" async-trait = "0.1.31" inotify = "0.9.2" +libseccomp = { version = "0.1.3", optional = true } [dev-dependencies] serial_test = "0.5.0" tempfile = "3.1.0" + +[features] +seccomp = ["libseccomp"] diff --git a/src/agent/rustjail/src/container.rs b/src/agent/rustjail/src/container.rs index ac34b4976..6e71552ef 100644 --- a/src/agent/rustjail/src/container.rs +++ b/src/agent/rustjail/src/container.rs @@ -25,6 +25,8 @@ use crate::cgroups::mock::Manager as FsManager; use crate::cgroups::Manager; use crate::log_child; use crate::process::Process; +#[cfg(feature = "seccomp")] +use crate::seccomp; use crate::specconv::CreateOpts; use crate::{mount, validator}; @@ -593,11 +595,22 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { })?; } - // NoNewPeiviledges, Drop capabilities + // NoNewPrivileges if oci_process.no_new_privileges { capctl::prctl::set_no_new_privs().map_err(|_| anyhow!("cannot set no new privileges"))?; } + // Without NoNewPrivileges, we need to set seccomp + // before dropping capabilities because the calling thread + // must have the CAP_SYS_ADMIN. + #[cfg(feature = "seccomp")] + if !oci_process.no_new_privileges { + if let Some(ref scmp) = linux.seccomp { + seccomp::init_seccomp(scmp)?; + } + } + + // Drop capabilities if oci_process.capabilities.is_some() { let c = oci_process.capabilities.as_ref().unwrap(); capabilities::drop_privileges(cfd_log, c)?; @@ -669,6 +682,16 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { unistd::read(fd, &mut buf)?; } + // With NoNewPrivileges, we should set seccomp as close to + // do_exec as possible in order to reduce the amount of + // system calls in the seccomp profiles. + #[cfg(feature = "seccomp")] + if oci_process.no_new_privileges { + if let Some(ref scmp) = linux.seccomp { + seccomp::init_seccomp(scmp)?; + } + } + do_exec(&args); } diff --git a/src/agent/rustjail/src/lib.rs b/src/agent/rustjail/src/lib.rs index f6b394a2a..7535bf990 100644 --- a/src/agent/rustjail/src/lib.rs +++ b/src/agent/rustjail/src/lib.rs @@ -34,6 +34,8 @@ pub mod container; pub mod mount; pub mod pipestream; pub mod process; +#[cfg(feature = "seccomp")] +pub mod seccomp; pub mod specconv; pub mod sync; pub mod sync_with_async; diff --git a/src/agent/rustjail/src/seccomp.rs b/src/agent/rustjail/src/seccomp.rs new file mode 100644 index 000000000..37eb17576 --- /dev/null +++ b/src/agent/rustjail/src/seccomp.rs @@ -0,0 +1,237 @@ +// Copyright 2021 Sony Group Corporation +// +// SPDX-License-Identifier: Apache-2.0 +// + +use anyhow::{anyhow, Result}; +use libseccomp::*; +use oci::{LinuxSeccomp, LinuxSeccompArg}; +use std::str::FromStr; + +fn get_filter_attr_from_flag(flag: &str) -> Result { + match flag { + "SECCOMP_FILTER_FLAG_TSYNC" => Ok(ScmpFilterAttr::CtlTsync), + "SECCOMP_FILTER_FLAG_LOG" => Ok(ScmpFilterAttr::CtlLog), + "SECCOMP_FILTER_FLAG_SPEC_ALLOW" => Ok(ScmpFilterAttr::CtlSsb), + _ => Err(anyhow!("Invalid seccomp flag")), + } +} + +// get_rule_conditions gets rule conditions for a system call from the args. +fn get_rule_conditions(args: &[LinuxSeccompArg]) -> Result> { + let mut conditions: Vec = Vec::new(); + + for arg in args { + if arg.op.is_empty() { + return Err(anyhow!("seccomp opreator is required")); + } + + let cond = ScmpArgCompare::new( + arg.index, + ScmpCompareOp::from_str(&arg.op)?, + arg.value, + Some(arg.value_two), + ); + + conditions.push(cond); + } + + Ok(conditions) +} + +// init_seccomp creates a seccomp filter and loads it for the current process +// including all the child processes. +pub fn init_seccomp(scmp: &LinuxSeccomp) -> Result<()> { + let def_action = ScmpAction::from_str(scmp.default_action.as_str(), Some(libc::EPERM as u32))?; + + // Create a new filter context + let mut filter = ScmpFilterContext::new_filter(def_action)?; + + // Add extra architectures + for arch in &scmp.architectures { + let scmp_arch = ScmpArch::from_str(arch)?; + filter.add_arch(scmp_arch)?; + } + + // Unset no new privileges bit + filter.set_no_new_privs_bit(false)?; + + // Add a rule for each system call + for syscall in &scmp.syscalls { + if syscall.names.is_empty() { + return Err(anyhow!("syscall name is required")); + } + + let action = ScmpAction::from_str(&syscall.action, Some(syscall.errno_ret))?; + if action == def_action { + continue; + } + + for name in &syscall.names { + let syscall_num = get_syscall_from_name(name, None)?; + + if syscall.args.is_empty() { + filter.add_rule(action, syscall_num, None)?; + } else { + let conditions = get_rule_conditions(&syscall.args)?; + filter.add_rule(action, syscall_num, Some(&conditions))?; + } + } + } + + // Set filter attributes for each seccomp flag + for flag in &scmp.flags { + let scmp_attr = get_filter_attr_from_flag(flag)?; + filter.set_filter_attr(scmp_attr, 1)?; + } + + // Load the filter + filter.load()?; + + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::skip_if_not_root; + use libc::{dup2, process_vm_readv, EPERM}; + use std::io::Error; + use std::ptr::null; + + macro_rules! syscall_assert { + ($e1: expr, $e2: expr) => { + let mut errno: i32 = 0; + if $e1 < 0 { + errno = -Error::last_os_error().raw_os_error().unwrap(); + } + assert_eq!(errno, $e2); + }; + } + + #[test] + fn test_get_filter_attr_from_flag() { + skip_if_not_root!(); + + assert_eq!( + get_filter_attr_from_flag("SECCOMP_FILTER_FLAG_TSYNC").unwrap(), + ScmpFilterAttr::CtlTsync + ); + + assert_eq!(get_filter_attr_from_flag("ERROR").is_err(), true); + } + + #[test] + fn test_init_seccomp() { + skip_if_not_root!(); + + let data = r#"{ + "defaultAction": "SCMP_ACT_ALLOW", + "architectures": [ + ], + "flags": [ + "SECCOMP_FILTER_FLAG_LOG" + ], + "syscalls": [ + { + "names": [ + "dup2" + ], + "action": "SCMP_ACT_ERRNO" + }, + { + "names": [ + "process_vm_readv" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 111, + "args": [ + { + "index": 0, + "value": 10, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "names": [ + "process_vm_readv" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 111, + "args": [ + { + "index": 0, + "value": 20, + "op": "SCMP_CMP_EQ" + } + ] + }, + { + "names": [ + "process_vm_readv" + ], + "action": "SCMP_ACT_ERRNO", + "errnoRet": 222, + "args": [ + { + "index": 0, + "value": 30, + "op": "SCMP_CMP_EQ" + }, + { + "index": 2, + "value": 40, + "op": "SCMP_CMP_EQ" + } + ] + } + ] + }"#; + + let mut scmp: oci::LinuxSeccomp = serde_json::from_str(data).unwrap(); + let mut arch: Vec; + + if cfg!(target_endian = "little") { + // For little-endian architectures + arch = vec![ + "SCMP_ARCH_X86".to_string(), + "SCMP_ARCH_X32".to_string(), + "SCMP_ARCH_X86_64".to_string(), + "SCMP_ARCH_AARCH64".to_string(), + "SCMP_ARCH_ARM".to_string(), + "SCMP_ARCH_PPC64LE".to_string(), + ]; + } else { + // For big-endian architectures + arch = vec!["SCMP_ARCH_S390X".to_string()]; + } + + scmp.architectures.append(&mut arch); + + init_seccomp(&scmp).unwrap(); + + // Basic syscall with simple rule + syscall_assert!(unsafe { dup2(0, 1) }, -EPERM); + + // Syscall with permitted arguments + syscall_assert!(unsafe { process_vm_readv(1, null(), 0, null(), 0, 0) }, 0); + + // Multiple arguments with OR rules with ERRNO + syscall_assert!( + unsafe { process_vm_readv(10, null(), 0, null(), 0, 0) }, + -111 + ); + syscall_assert!( + unsafe { process_vm_readv(20, null(), 0, null(), 0, 0) }, + -111 + ); + + // Multiple arguments with AND rules with ERRNO + syscall_assert!(unsafe { process_vm_readv(30, null(), 0, null(), 0, 0) }, 0); + syscall_assert!( + unsafe { process_vm_readv(30, null(), 40, null(), 0, 0) }, + -222 + ); + } +} diff --git a/src/agent/src/config.rs b/src/agent/src/config.rs index 9dd0264d2..bec90d401 100644 --- a/src/agent/src/config.rs +++ b/src/agent/src/config.rs @@ -2,6 +2,7 @@ // // SPDX-License-Identifier: Apache-2.0 // +use crate::rpc; use anyhow::{bail, ensure, Context, Result}; use serde::Deserialize; use std::collections::HashSet; @@ -74,6 +75,7 @@ pub struct AgentConfig { pub unified_cgroup_hierarchy: bool, pub tracing: bool, pub endpoints: AgentEndpoints, + pub supports_seccomp: bool, } #[derive(Debug, Deserialize)] @@ -149,6 +151,7 @@ impl Default for AgentConfig { unified_cgroup_hierarchy: false, tracing: false, endpoints: Default::default(), + supports_seccomp: rpc::have_seccomp(), } } } diff --git a/src/agent/src/rpc.rs b/src/agent/src/rpc.rs index e4665ac36..c9032d405 100644 --- a/src/agent/src/rpc.rs +++ b/src/agent/src/rpc.rs @@ -1334,11 +1334,19 @@ fn get_memory_info(block_size: bool, hotplug: bool) -> Result<(u64, bool)> { Ok((size, plug)) } +pub fn have_seccomp() -> bool { + if cfg!(feature = "seccomp") { + return true; + } + + false +} + fn get_agent_details() -> AgentDetails { let mut detail = AgentDetails::new(); detail.set_version(AGENT_VERSION.to_string()); - detail.set_supports_seccomp(false); + detail.set_supports_seccomp(have_seccomp()); detail.init_daemon = unistd::getpid() == Pid::from_raw(1); detail.device_handlers = RepeatedField::new(); diff --git a/src/runtime/virtcontainers/kata_agent.go b/src/runtime/virtcontainers/kata_agent.go index 57f7b0cde..b553159fb 100644 --- a/src/runtime/virtcontainers/kata_agent.go +++ b/src/runtime/virtcontainers/kata_agent.go @@ -1422,6 +1422,10 @@ func (k *kataAgent) createContainer(ctx context.Context, sandbox *Sandbox, c *Co sharedPidNs := k.handlePidNamespace(grpcSpec, sandbox) + if !sandbox.config.DisableGuestSeccomp && !sandbox.seccompSupported { + return nil, fmt.Errorf("Seccomp profiles are passed to the virtual machine, but the Kata agent does not support seccomp") + } + passSeccomp := !sandbox.config.DisableGuestSeccomp && sandbox.seccompSupported // We need to constrain the spec to make sure we're not From a3647e34865d7b2e1f16de90870775729d1f45b5 Mon Sep 17 00:00:00 2001 From: Manabu Sugimoto Date: Fri, 16 Jul 2021 15:04:33 +0900 Subject: [PATCH 2/5] osbuilder: Set up libseccomp library The osbuilder needs to set up libseccomp library to build the kata-agent because the kata-agent supports seccomp currently. The library is built from the sources to create a static library for musl libc. In addition, environment variables for the libseccomp crate are set to link the library statically. Fixes: #1476 Signed-off-by: Manabu Sugimoto --- ci/install_libseccomp.sh | 101 +++++++++++++++++++++++ tools/osbuilder/rootfs-builder/rootfs.sh | 25 +++++- 2 files changed, 122 insertions(+), 4 deletions(-) create mode 100755 ci/install_libseccomp.sh diff --git a/ci/install_libseccomp.sh b/ci/install_libseccomp.sh new file mode 100755 index 000000000..dbd7bac83 --- /dev/null +++ b/ci/install_libseccomp.sh @@ -0,0 +1,101 @@ +#!/bin/bash +# +# Copyright 2021 Sony Group Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +set -o errexit + +cidir=$(dirname "$0") +source "${cidir}/lib.sh" + +clone_tests_repo + +source "${tests_repo_dir}/.ci/lib.sh" + +arch=$(uname -m) + +# Variables for libseccomp +# Currently, specify the libseccomp version directly without using `versions.yaml` +# because the current Snap workflow is incomplete. +# After solving the issue, replace this code by using the `versions.yaml`. +# libseccomp_version=$(get_version "externals.libseccomp.version") +# libseccomp_url=$(get_version "externals.libseccomp.url") +libseccomp_version="2.5.1" +libseccomp_url="https://github.com/seccomp/libseccomp" +libseccomp_tarball="libseccomp-${libseccomp_version}.tar.gz" +libseccomp_tarball_url="${libseccomp_url}/releases/download/v${libseccomp_version}/${libseccomp_tarball}" +cflags="-O2" + +# Variables for gperf +# Currently, specify the gperf version directly without using `versions.yaml` +# because the current Snap workflow is incomplete. +# After solving the issue, replace this code by using the `versions.yaml`. +# gperf_version=$(get_version "externals.gperf.version") +# gperf_url=$(get_version "externals.gperf.url") +gperf_version="3.1" +gperf_url="https://ftp.gnu.org/gnu/gperf" +gperf_tarball="gperf-${gperf_version}.tar.gz" +gperf_tarball_url="${gperf_url}/${gperf_tarball}" + +# We need to build the libseccomp library from sources to create a static library for the musl libc. +# However, ppc64le and s390x have no musl targets in Rust. Hence, we do not set cflags for the musl libc. +if ([ "${arch}" != "ppc64le" ] && [ "${arch}" != "s390x" ]); then + # Set FORTIFY_SOURCE=1 because the musl-libc does not have some functions about FORTIFY_SOURCE=2 + cflags="-U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=1 -O2" +fi + +die() { + msg="$*" + echo "[Error] ${msg}" >&2 + exit 1 +} + +finish() { + rm -rf "${libseccomp_tarball}" "libseccomp-${libseccomp_version}" "${gperf_tarball}" "gperf-${gperf_version}" +} + +trap finish EXIT + +build_and_install_gperf() { + echo "Build and install gperf version ${gperf_version}" + mkdir -p "${gperf_install_dir}" + curl -sLO "${gperf_tarball_url}" + tar -xf "${gperf_tarball}" + pushd "gperf-${gperf_version}" + ./configure --prefix="${gperf_install_dir}" + make + make install + export PATH=$PATH:"${gperf_install_dir}"/bin + popd + echo "Gperf installed successfully" +} + +build_and_install_libseccomp() { + echo "Build and install libseccomp version ${libseccomp_version}" + mkdir -p "${libseccomp_install_dir}" + curl -sLO "${libseccomp_tarball_url}" + tar -xf "${libseccomp_tarball}" + pushd "libseccomp-${libseccomp_version}" + ./configure --prefix="${libseccomp_install_dir}" CFLAGS="${cflags}" --enable-static + make + make install + popd + echo "Libseccomp installed successfully" +} + +main() { + local libseccomp_install_dir="${1:-}" + local gperf_install_dir="${2:-}" + + if [ -z "${libseccomp_install_dir}" ] || [ -z "${gperf_install_dir}" ]; then + die "Usage: ${0} " + fi + + # gperf is required for building the libseccomp. + build_and_install_gperf + build_and_install_libseccomp +} + +main "$@" diff --git a/tools/osbuilder/rootfs-builder/rootfs.sh b/tools/osbuilder/rootfs-builder/rootfs.sh index ec3a1e6ca..bfdd4bb27 100755 --- a/tools/osbuilder/rootfs-builder/rootfs.sh +++ b/tools/osbuilder/rootfs-builder/rootfs.sh @@ -23,6 +23,9 @@ DOCKER_RUNTIME=${DOCKER_RUNTIME:-runc} # this GOPATH is for installing yq from install_yq.sh export GOPATH=${GOPATH:-${HOME}/go} LIBC=${LIBC:-musl} +# The kata agent enables seccomp feature. +# However, it is not enforced by default: you need to enable that in the main configuration file. +SECCOMP=${SECCOMP:-"yes"} lib_file="${script_dir}/../scripts/lib.sh" source "$lib_file" @@ -128,6 +131,9 @@ KERNEL_MODULES_DIR Path to a directory containing kernel modules to include in ROOTFS_DIR Path to the directory that is populated with the rootfs. Default value: <${script_name} path>/rootfs- +SECCOMP When set to "no", the kata-agent is built without seccomp capability. + Default value: "yes" + USE_DOCKER If set, build the rootfs inside a container (requires Docker). Default value: @@ -563,8 +569,16 @@ EOT [ "$ARCH" == "aarch64" ] && OLD_PATH=$PATH && export PATH=$PATH:/usr/local/musl/bin agent_dir="${script_dir}/../../../src/agent/" - # For now, rust-agent doesn't support seccomp yet. - SECCOMP="no" + + if [ "${SECCOMP}" == "yes" ]; then + info "Set up libseccomp" + libseccomp_install_dir=$(mktemp -d -t libseccomp.XXXXXXXXXX) + gperf_install_dir=$(mktemp -d -t gperf.XXXXXXXXXX) + bash ${script_dir}/../../../ci/install_libseccomp.sh "${libseccomp_install_dir}" "${gperf_install_dir}" + echo "Set environment variables for the libseccomp crate to link the libseccomp library statically" + export LIBSECCOMP_LINK_TYPE=static + export LIBSECCOMP_LIB_PATH="${libseccomp_install_dir}/lib" + fi info "Build agent" pushd "${agent_dir}" @@ -572,9 +586,12 @@ EOT git checkout "${AGENT_VERSION}" && OK "git checkout successful" || die "checkout agent ${AGENT_VERSION} failed!" fi make clean - make LIBC=${LIBC} INIT=${AGENT_INIT} - make install DESTDIR="${ROOTFS_DIR}" LIBC=${LIBC} INIT=${AGENT_INIT} SECCOMP=${SECCOMP} + make LIBC=${LIBC} INIT=${AGENT_INIT} SECCOMP=${SECCOMP} + make install DESTDIR="${ROOTFS_DIR}" LIBC=${LIBC} INIT=${AGENT_INIT} [ "$ARCH" == "aarch64" ] && export PATH=$OLD_PATH && rm -rf /usr/local/musl + if [ "${SECCOMP}" == "yes" ]; then + rm -rf "${libseccomp_install_dir}" "${gperf_install_dir}" + fi popd else mkdir -p ${AGENT_DIR} From 45e7c2cab13dfbbc41146bdf2317720d4c96fe78 Mon Sep 17 00:00:00 2001 From: Manabu Sugimoto Date: Fri, 16 Jul 2021 15:24:47 +0900 Subject: [PATCH 3/5] static-checks: Add step for installing libseccomp This adds a step for installing libseccomp because the kata-agent supports seccomp feature. Fixes: #1476 Signed-off-by: Manabu Sugimoto --- .github/workflows/static-checks.yaml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/static-checks.yaml b/.github/workflows/static-checks.yaml index 235e7f2f8..cb37e33b3 100644 --- a/.github/workflows/static-checks.yaml +++ b/.github/workflows/static-checks.yaml @@ -67,6 +67,14 @@ jobs: PATH=$PATH:"$HOME/.cargo/bin" rustup target add x86_64-unknown-linux-musl rustup component add rustfmt clippy + - name: Setup seccomp + run: | + libseccomp_install_dir=$(mktemp -d -t libseccomp.XXXXXXXXXX) + gperf_install_dir=$(mktemp -d -t gperf.XXXXXXXXXX) + cd ${GOPATH}/src/github.com/${{ github.repository }} && ./ci/install_libseccomp.sh "${libseccomp_install_dir}" "${gperf_install_dir}" + echo "Set environment variables for the libseccomp crate to link the libseccomp library statically" + echo "LIBSECCOMP_LINK_TYPE=static" >> $GITHUB_ENV + echo "LIBSECCOMP_LIB_PATH=${libseccomp_install_dir}/lib" >> $GITHUB_ENV # Check whether the vendored code is up-to-date & working as the first thing - name: Check vendored code if: ${{ !contains(github.event.pull_request.labels.*.name, 'force-skip-ci') }} From 5dfedc2b19d1eedb76a6b056b102ee62561a8ce1 Mon Sep 17 00:00:00 2001 From: Manabu Sugimoto Date: Fri, 16 Jul 2021 16:31:43 +0900 Subject: [PATCH 4/5] docs: Add explanation about seccomp This adds explanation about how to enable seccomp in the kata-runtime and build the kata-agent with seccomp capability. Fixes: #1476 Signed-off-by: Manabu Sugimoto --- docs/Developer-Guide.md | 55 +++++++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/docs/Developer-Guide.md b/docs/Developer-Guide.md index a17d67e09..f4ec4ff7b 100644 --- a/docs/Developer-Guide.md +++ b/docs/Developer-Guide.md @@ -86,6 +86,16 @@ One of the `initrd` and `image` options in Kata runtime config file **MUST** be The main difference between the options is that the size of `initrd`(10MB+) is significantly smaller than rootfs `image`(100MB+). +## Enable seccomp + +Enable seccomp as follows: + +``` +$ sudo sed -i '/^disable_guest_seccomp/ s/true/false/' /etc/kata-containers/configuration.toml +``` + +This will pass container seccomp profiles to the kata agent. + ## Enable full debug Enable full debug as follows: @@ -216,6 +226,18 @@ $ go get -d -u github.com/kata-containers/kata-containers $ cd $GOPATH/src/github.com/kata-containers/kata-containers/src/agent && make ``` +The agent is built with seccomp capability by default. +If you want to build the agent without the seccomp capability, you need to run `make` with `SECCOMP=no` as follows. + +``` +$ make -C $GOPATH/src/github.com/kata-containers/kata-containers/src/agent SECCOMP=no +``` + +> **Note:** +> +> - If you enable seccomp in the main configuration file but build the agent without seccomp capability, +> the runtime exits conservatively with an error message. + ## Get the osbuilder ``` @@ -234,9 +256,21 @@ the following example. $ export ROOTFS_DIR=${GOPATH}/src/github.com/kata-containers/kata-containers/tools/osbuilder/rootfs-builder/rootfs $ sudo rm -rf ${ROOTFS_DIR} $ cd $GOPATH/src/github.com/kata-containers/kata-containers/tools/osbuilder/rootfs-builder -$ script -fec 'sudo -E GOPATH=$GOPATH USE_DOCKER=true SECCOMP=no ./rootfs.sh ${distro}' +$ script -fec 'sudo -E GOPATH=$GOPATH USE_DOCKER=true ./rootfs.sh ${distro}' +``` + +You MUST choose a distribution (e.g., `ubuntu`) for `${distro}`. +You can get a supported distributions list in the Kata Containers by running the following. + +``` +$ ./rootfs.sh -l +``` + +If you want to build the agent without seccomp capability, you need to run the `rootfs.sh` script with `SECCOMP=no` as follows. + +``` +$ script -fec 'sudo -E GOPATH=$GOPATH AGENT_INIT=yes USE_DOCKER=true SECCOMP=no ./rootfs.sh ${distro}' ``` -You MUST choose one of `alpine`, `centos`, `clearlinux`, `debian`, `euleros`, `fedora`, `suse`, and `ubuntu` for `${distro}`. By default `seccomp` packages are not included in the rootfs image. Set `SECCOMP` to `yes` to include them. > **Note:** > @@ -291,12 +325,23 @@ $ (cd /usr/share/kata-containers && sudo ln -sf "$image" kata-containers.img) $ export ROOTFS_DIR="${GOPATH}/src/github.com/kata-containers/kata-containers/tools/osbuilder/rootfs-builder/rootfs" $ sudo rm -rf ${ROOTFS_DIR} $ cd $GOPATH/src/github.com/kata-containers/kata-containers/tools/osbuilder/rootfs-builder -$ script -fec 'sudo -E GOPATH=$GOPATH AGENT_INIT=yes USE_DOCKER=true SECCOMP=no ./rootfs.sh ${distro}' +$ script -fec 'sudo -E GOPATH=$GOPATH AGENT_INIT=yes USE_DOCKER=true ./rootfs.sh ${distro}' ``` `AGENT_INIT` controls if the guest image uses the Kata agent as the guest `init` process. When you create an initrd image, -always set `AGENT_INIT` to `yes`. By default `seccomp` packages are not included in the initrd image. Set `SECCOMP` to `yes` to include them. +always set `AGENT_INIT` to `yes`. -You MUST choose one of `alpine`, `centos`, `clearlinux`, `euleros`, and `fedora` for `${distro}`. +You MUST choose a distribution (e.g., `ubuntu`) for `${distro}`. +You can get a supported distributions list in the Kata Containers by running the following. + +``` +$ ./rootfs.sh -l +``` + +If you want to build the agent without seccomp capability, you need to run the `rootfs.sh` script with `SECCOMP=no` as follows. + +``` +$ script -fec 'sudo -E GOPATH=$GOPATH AGENT_INIT=yes USE_DOCKER=true SECCOMP=no ./rootfs.sh ${distro}' +``` > **Note:** > From 42add7f201cf082d0d06d676c5cf20e6088ac2fb Mon Sep 17 00:00:00 2001 From: Manabu Sugimoto Date: Wed, 27 Oct 2021 18:41:12 +0900 Subject: [PATCH 5/5] agent: Disable seccomp feature on aarch64 temporarily In order to pass CI test of aarch64, it is necessary to run `ci/install_libseccomp.sh` before ruuning unit tests in `jenkins_job_build.sh`. However, `ci/install_libseccomp.sh` is not available until PR #1788 including this commit is merged in the mainline. Therefore, we disable seccomp feature on aarch64 temporarily. After #1788 lands and CI is fixed, this commit will be reverted. Fixes: #1476 Signed-off-by: Manabu Sugimoto --- src/agent/rustjail/Cargo.toml | 3 +++ src/agent/rustjail/src/container.rs | 6 +++--- src/agent/rustjail/src/lib.rs | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/agent/rustjail/Cargo.toml b/src/agent/rustjail/Cargo.toml index 497a86210..e6d1d7ee6 100644 --- a/src/agent/rustjail/Cargo.toml +++ b/src/agent/rustjail/Cargo.toml @@ -30,6 +30,9 @@ tokio = { version = "1.2.0", features = ["sync", "io-util", "process", "time", " futures = "0.3" async-trait = "0.1.31" inotify = "0.9.2" + +# Disable libseccomp on aarch64 temporarily in order to pass CI +[target.'cfg(not(target_arch = "aarch64"))'.dependencies] libseccomp = { version = "0.1.3", optional = true } [dev-dependencies] diff --git a/src/agent/rustjail/src/container.rs b/src/agent/rustjail/src/container.rs index 6e71552ef..8e6e8d8d9 100644 --- a/src/agent/rustjail/src/container.rs +++ b/src/agent/rustjail/src/container.rs @@ -25,7 +25,7 @@ use crate::cgroups::mock::Manager as FsManager; use crate::cgroups::Manager; use crate::log_child; use crate::process::Process; -#[cfg(feature = "seccomp")] +#[cfg(all(not(target_arch = "aarch64"), feature = "seccomp"))] use crate::seccomp; use crate::specconv::CreateOpts; use crate::{mount, validator}; @@ -603,7 +603,7 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { // Without NoNewPrivileges, we need to set seccomp // before dropping capabilities because the calling thread // must have the CAP_SYS_ADMIN. - #[cfg(feature = "seccomp")] + #[cfg(all(not(target_arch = "aarch64"), feature = "seccomp"))] if !oci_process.no_new_privileges { if let Some(ref scmp) = linux.seccomp { seccomp::init_seccomp(scmp)?; @@ -685,7 +685,7 @@ fn do_init_child(cwfd: RawFd) -> Result<()> { // With NoNewPrivileges, we should set seccomp as close to // do_exec as possible in order to reduce the amount of // system calls in the seccomp profiles. - #[cfg(feature = "seccomp")] + #[cfg(all(not(target_arch = "aarch64"), feature = "seccomp"))] if oci_process.no_new_privileges { if let Some(ref scmp) = linux.seccomp { seccomp::init_seccomp(scmp)?; diff --git a/src/agent/rustjail/src/lib.rs b/src/agent/rustjail/src/lib.rs index 7535bf990..f9327dc3f 100644 --- a/src/agent/rustjail/src/lib.rs +++ b/src/agent/rustjail/src/lib.rs @@ -34,7 +34,7 @@ pub mod container; pub mod mount; pub mod pipestream; pub mod process; -#[cfg(feature = "seccomp")] +#[cfg(all(not(target_arch = "aarch64"), feature = "seccomp"))] pub mod seccomp; pub mod specconv; pub mod sync;