mirror of
https://github.com/opencontainers/runc.git
synced 2026-06-30 19:58:25 +00:00
This adds support for WaitKillableRecv seccomp flag (also known as SCMP_FLTATR_CTL_WAITKILL in libseccomp and as SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV in the kernel). This requires: - libseccomp >= 2.6.0 - libseccomp-golang >= 0.11.0 - linux kernel >= 5.19 Note that this flag does not make sense without NEW_LISTENER, and the kernel returns EINVAL when SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV is set but SECCOMP_FILTER_FLAG_NEW_LISTENER is not set. For runc this means that .linux.seccomp.listenerPath should also be set, and some of the seccomp rules should have SCMP_ACT_NOTIFY action. This is why the flag is tested separately in seccomp-notify.bats. At the moment the only adequate CI environment for this functionality is Fedora 43. On all other platforms (including CentOS 10 and Ubuntu 24.04) it is skipped similar to this: > ok 251 runc run [seccomp] (SECCOMP_FILTER_FLAG_WAIT_KILLABLE_RECV) # skip requires libseccomp >= 2.6.0 and API level >= 7 (current version: 2.5.6, API level: 6) Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
206 lines
6.7 KiB
Bash
206 lines
6.7 KiB
Bash
#!/usr/bin/env bats
|
|
|
|
load helpers
|
|
|
|
function setup() {
|
|
setup_busybox
|
|
}
|
|
|
|
function teardown() {
|
|
teardown_bundle
|
|
}
|
|
|
|
@test "runc run [seccomp -ENOSYS handling]" {
|
|
TEST_NAME="seccomp_syscall_test1"
|
|
|
|
# Compile the test binary and update the config to run it.
|
|
gcc -static -o rootfs/seccomp_test "${TESTDATA}/${TEST_NAME}.c"
|
|
update_config ".linux.seccomp = $(<"${TESTDATA}/${TEST_NAME}.json")"
|
|
update_config '.process.args = ["/seccomp_test"]'
|
|
|
|
runc run test_busybox
|
|
[ "$status" -eq 0 ]
|
|
}
|
|
|
|
@test "runc run [seccomp defaultErrnoRet=ENXIO]" {
|
|
TEST_NAME="seccomp_syscall_test2"
|
|
|
|
# Compile the test binary and update the config to run it.
|
|
gcc -static -o rootfs/seccomp_test2 "${TESTDATA}/${TEST_NAME}.c"
|
|
update_config ".linux.seccomp = $(<"${TESTDATA}/${TEST_NAME}.json")"
|
|
update_config '.process.args = ["/seccomp_test2"]'
|
|
|
|
runc run test_busybox
|
|
[ "$status" -eq 0 ]
|
|
}
|
|
|
|
# TODO:
|
|
# - Test other actions like SCMP_ACT_TRAP, SCMP_ACT_TRACE, SCMP_ACT_LOG.
|
|
# - Test args (index, value, valueTwo, etc).
|
|
|
|
@test "runc run [seccomp] (SCMP_ACT_ERRNO default)" {
|
|
update_config ' .process.args = ["/bin/sh", "-c", "mkdir /dev/shm/foo"]
|
|
| .process.noNewPrivileges = false
|
|
| .linux.seccomp = {
|
|
"defaultAction":"SCMP_ACT_ALLOW",
|
|
"architectures":["SCMP_ARCH_X86","SCMP_ARCH_X32","SCMP_ARCH_X86_64","SCMP_ARCH_AARCH64","SCMP_ARCH_ARM"],
|
|
"syscalls":[{"names":["mkdir","mkdirat"], "action":"SCMP_ACT_ERRNO"}]
|
|
}'
|
|
|
|
runc run test_busybox
|
|
[ "$status" -ne 0 ]
|
|
[[ "$output" == *"mkdir:"*"/dev/shm/foo"*"Operation not permitted"* ]]
|
|
}
|
|
|
|
@test "runc run [seccomp] (SCMP_ACT_ERRNO explicit errno)" {
|
|
update_config ' .process.args = ["/bin/sh", "-c", "mkdir /dev/shm/foo"]
|
|
| .process.noNewPrivileges = false
|
|
| .linux.seccomp = {
|
|
"defaultAction":"SCMP_ACT_ALLOW",
|
|
"architectures":["SCMP_ARCH_X86","SCMP_ARCH_X32","SCMP_ARCH_X86_64","SCMP_ARCH_AARCH64","SCMP_ARCH_ARM"],
|
|
"syscalls":[{"names":["mkdir","mkdirat"], "action":"SCMP_ACT_ERRNO", "errnoRet": 100}]
|
|
}'
|
|
|
|
runc run test_busybox
|
|
[ "$status" -ne 0 ]
|
|
[[ "$output" == *"Network is down"* ]]
|
|
}
|
|
|
|
# Prints the numeric value of provided seccomp flags combination.
|
|
# The parameter is flags string, as supplied in OCI spec, for example
|
|
# '"SECCOMP_FILTER_FLAG_TSYNC","SECCOMP_FILTER_FLAG_LOG"'.
|
|
function flags_value() {
|
|
# Numeric values of seccomp flags.
|
|
declare -A values=(
|
|
['"SECCOMP_FILTER_FLAG_TSYNC"']=0 # Supported but ignored by runc, thus 0.
|
|
['"SECCOMP_FILTER_FLAG_LOG"']=2
|
|
['"SECCOMP_FILTER_FLAG_SPEC_ALLOW"']=4
|
|
# XXX: add new values above this line.
|
|
)
|
|
# Split the flags.
|
|
IFS=',' read -ra flags <<<"$1"
|
|
|
|
local flag v sum=0
|
|
for flag in "${flags[@]}"; do
|
|
# This will produce "values[$flag]: unbound variable"
|
|
# error for a new flag yet unknown to the test.
|
|
v=${values[$flag]}
|
|
((sum += v)) || true
|
|
done
|
|
|
|
echo $sum
|
|
}
|
|
|
|
@test "runc run [seccomp] (SECCOMP_FILTER_FLAG_*)" {
|
|
update_config ' .process.args = ["/bin/sh", "-c", "mkdir /dev/shm/foo"]
|
|
| .process.noNewPrivileges = false
|
|
| .linux.seccomp = {
|
|
"defaultAction":"SCMP_ACT_ALLOW",
|
|
"architectures":["SCMP_ARCH_X86","SCMP_ARCH_X32","SCMP_ARCH_X86_64","SCMP_ARCH_AARCH64","SCMP_ARCH_ARM"],
|
|
"syscalls":[{"names":["mkdir", "mkdirat"], "action":"SCMP_ACT_ERRNO"}]
|
|
}'
|
|
|
|
# Get the list of flags supported by runc/seccomp/kernel,
|
|
# or "null" if no flags are supported or runc is too old.
|
|
#
|
|
# Filter out WAIT_KILLABLE_RECV as it requires a listener,
|
|
# and thus tested separately in seccomp-notify.bats.
|
|
mapfile -t flags < <(__runc features | jq -c '.linux.seccomp.supportedFlags' |
|
|
tr -d '[]\n' | tr ',' '\n' | grep -v 'WAIT_KILLABLE_RECV')
|
|
|
|
# This is a set of all possible flag combinations to test.
|
|
declare -A TEST_CASES=(
|
|
['EMPTY']=0 # Special value: empty set of flags.
|
|
['REMOVE']=0 # Special value: no flags set.
|
|
)
|
|
|
|
# If supported, runc should set SPEC_ALLOW if no flags are set.
|
|
if [[ " ${flags[*]} " == *' "SECCOMP_FILTER_FLAG_SPEC_ALLOW" '* ]]; then
|
|
TEST_CASES['REMOVE']=$(flags_value '"SECCOMP_FILTER_FLAG_SPEC_ALLOW"')
|
|
fi
|
|
|
|
# Add all possible combinations of seccomp flags
|
|
# and their expected numeric values to TEST_CASES.
|
|
if [ "${flags[0]}" != "null" ]; then
|
|
# Use shell {a,}{b,}{c,} to generate the powerset.
|
|
for fc in $(eval echo "$(printf "{'%s,',}" "${flags[@]}")"); do
|
|
# Remove the last comma.
|
|
fc="${fc/%,/}"
|
|
TEST_CASES[$fc]=$(flags_value "$fc")
|
|
done
|
|
fi
|
|
|
|
# Finally, run the tests.
|
|
for key in "${!TEST_CASES[@]}"; do
|
|
case "$key" in
|
|
'REMOVE')
|
|
update_config ' del(.linux.seccomp.flags)'
|
|
;;
|
|
'EMPTY')
|
|
update_config ' .linux.seccomp.flags = []'
|
|
;;
|
|
*)
|
|
update_config ' .linux.seccomp.flags = [ '"${key}"' ]'
|
|
;;
|
|
esac
|
|
|
|
runc --debug run test_busybox
|
|
[ "$status" -ne 0 ]
|
|
[[ "$output" == *"mkdir:"*"/dev/shm/foo"*"Operation not permitted"* ]]
|
|
|
|
# Check the numeric flags value, as printed in the debug log, is as expected.
|
|
exp="\"seccomp filter flags: ${TEST_CASES[$key]}\""
|
|
echo "flags $key, expecting $exp"
|
|
[[ "$output" == *"$exp"* ]]
|
|
done
|
|
}
|
|
|
|
@test "runc run [seccomp] (SCMP_ACT_KILL)" {
|
|
update_config ' .process.args = ["/bin/sh", "-c", "mkdir /dev/shm/foo"]
|
|
| .process.noNewPrivileges = false
|
|
| .process.rlimits = [{"type": "RLIMIT_CORE", "soft": 0, "hard": 0}]
|
|
| .linux.seccomp = {
|
|
"defaultAction":"SCMP_ACT_ALLOW",
|
|
"architectures":["SCMP_ARCH_X86","SCMP_ARCH_X32","SCMP_ARCH_X86_64","SCMP_ARCH_AARCH64","SCMP_ARCH_ARM"],
|
|
"syscalls":[{"names":["mkdir","mkdirat"], "action":"SCMP_ACT_KILL"}]
|
|
}'
|
|
|
|
runc run test_busybox
|
|
[ "$status" -ne 0 ]
|
|
}
|
|
|
|
# check that a startContainer hook is run with the seccomp filters applied
|
|
@test "runc run [seccomp] (startContainer hook)" {
|
|
update_config ' .process.args = ["/bin/true"]
|
|
| .linux.seccomp = {
|
|
"defaultAction":"SCMP_ACT_ALLOW",
|
|
"architectures":["SCMP_ARCH_X86","SCMP_ARCH_X32","SCMP_ARCH_X86_64","SCMP_ARCH_AARCH64","SCMP_ARCH_ARM"],
|
|
"syscalls":[{"names":["mkdir","mkdirat"], "action":"SCMP_ACT_KILL"}]
|
|
}
|
|
| .process.rlimits = [{"type": "RLIMIT_CORE", "soft": 0, "hard": 0}]
|
|
| .hooks = {
|
|
"startContainer": [ {
|
|
"path": "/bin/sh",
|
|
"args": ["sh", "-c", "mkdir /dev/shm/foo"]
|
|
} ]
|
|
}'
|
|
|
|
runc run test_busybox
|
|
[ "$status" -ne 0 ]
|
|
[[ "$output" == *"error running startContainer hook"* ]]
|
|
[[ "$output" == *"bad system call"* ]]
|
|
}
|
|
|
|
@test "runc run [seccomp] (verify syscall compatibility after seccomp enforcement)" {
|
|
update_config ' .process.args = ["true"]
|
|
| .process.noNewPrivileges = false
|
|
| .linux.seccomp = {
|
|
"defaultAction":"SCMP_ACT_ALLOW",
|
|
"architectures":["SCMP_ARCH_X86","SCMP_ARCH_X32","SCMP_ARCH_X86_64","SCMP_ARCH_AARCH64","SCMP_ARCH_ARM"],
|
|
"syscalls":[{"names":["close_range", "fsopen", "fsconfig", "fspick", "openat2", "open_tree", "move_mount", "mount_setattr"], "action":"SCMP_ACT_ERRNO", "errnoRet": 38}]
|
|
}'
|
|
|
|
runc run test_busybox
|
|
[ "$status" -eq 0 ]
|
|
}
|