Skip to content

Commit c25bb2b

Browse files
tests: add functional tests for seccomp notify
Add functional test to check seccomp notify end-to-end. This test uses the sample seccomp agent from the contrib/cmd folder. Signed-off-by: Mauricio Vásquez <[email protected]>
1 parent 0a202ef commit c25bb2b

File tree

2 files changed

+234
-0
lines changed

2 files changed

+234
-0
lines changed

tests/integration/helpers.bash

+24
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ INTEGRATION_ROOT=$(dirname "$(readlink -f "$BASH_SOURCE")")
88
RUNC="${INTEGRATION_ROOT}/../../runc"
99
RECVTTY="${INTEGRATION_ROOT}/../../contrib/cmd/recvtty/recvtty"
1010
GOPATH="$(mktemp -d --tmpdir runc-integration-gopath.XXXXXX)"
11+
SECCOMPAGENT="${INTEGRATION_ROOT}/../../contrib/cmd/seccompagent/seccompagent"
1112

1213
# Test data path.
1314
TESTDATA="${INTEGRATION_ROOT}/testdata"
@@ -39,6 +40,9 @@ ROOT=$(mktemp -d "$BATS_TMPDIR/runc.XXXXXX")
3940
# Path to console socket.
4041
CONSOLE_SOCKET="$BATS_TMPDIR/console.sock"
4142

43+
# Seccomp agent socket.
44+
SECCCOMP_AGENT_SOCKET="$BATS_TMPDIR/seccomp-agent.sock"
45+
4246
# Check if we're in rootless mode.
4347
ROOTLESS=$(id -u)
4448

@@ -476,6 +480,18 @@ function teardown_recvtty() {
476480
rm -f "$CONSOLE_SOCKET"
477481
}
478482

483+
function setup_seccompagent() {
484+
("$SECCOMPAGENT" -socketfile="$SECCCOMP_AGENT_SOCKET" -pid-file "$BATS_TMPDIR/seccompagent.pid" &) &
485+
}
486+
487+
function teardown_seccompagent() {
488+
if [ -f "$BATS_TMPDIR/seccompagent.pid" ]; then
489+
kill -9 $(cat "$BATS_TMPDIR/seccompagent.pid")
490+
fi
491+
rm -f "$BATS_TMPDIR/seccompagent.pid"
492+
rm -f "$SECCCOMP_AGENT_SOCKET"
493+
}
494+
479495
function setup_busybox() {
480496
setup_recvtty
481497
mkdir -p "$BUSYBOX_BUNDLE"/rootfs
@@ -548,3 +564,11 @@ function teardown_debian() {
548564
teardown_running_container test_debian
549565
rm -f -r "$DEBIAN_BUNDLE"
550566
}
567+
568+
function requires_kernel() {
569+
MAJOR_REQUIRED=$(echo "$1" | cut -d. -f1)
570+
MINOR_REQUIRED=$(echo "$1" | cut -d. -f2)
571+
if [[ "$KERNEL_MAJOR" -lt $MAJOR_REQUIRED || ("$KERNEL_MAJOR" -eq $MAJOR_REQUIRED && "$KERNEL_MINOR" -lt $MINOR_REQUIRED) ]]; then
572+
skip "requires kernel $1"
573+
fi
574+
}

tests/integration/seccomp-notify.bats

+210
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
#!/usr/bin/env bats
2+
3+
load helpers
4+
5+
function setup() {
6+
teardown_seccompagent
7+
setup_seccompagent
8+
9+
teardown_busybox
10+
setup_busybox
11+
}
12+
13+
function teardown() {
14+
teardown_seccompagent
15+
teardown_busybox
16+
}
17+
18+
# Create config.json template with SCMP_ACT_NOTIFY actions
19+
# $1: command to run
20+
# $2: noNewPrivileges (false/true)
21+
# $3: list of syscalls
22+
function scmp_act_notify_template() {
23+
# The agent intercepts mkdir syscalls and creates the folder appending
24+
# "-bar" (listenerMetadata below) to the name.
25+
update_config '.process.args = ["/bin/sh", "-c", "'"$1"'"] |
26+
.process.noNewPrivileges = '"$2"' |
27+
.linux.seccomp = {
28+
"defaultAction":"SCMP_ACT_ALLOW",
29+
"listenerPath": "'"$SECCCOMP_AGENT_SOCKET"'",
30+
"listenerMetadata": "bar",
31+
"architectures":["SCMP_ARCH_X86","SCMP_ARCH_X32"],
32+
"syscalls":[{"names":['"$3"'], "action":"SCMP_ACT_NOTIFY"}]
33+
}'
34+
}
35+
36+
# Support for seccomp notify requires Linux > 5.6 because
37+
# runc uses the pidfd_getfd system call to fetch the seccomp fd.
38+
# https://github.com/torvalds/linux/commit/8649c322f75c96e7ced2fec201e123b2b073bf09
39+
40+
# The call to seccomp is done at different places according to the value of
41+
# noNewPrivileges, for this reason many of the following cases are tested with
42+
# both values.
43+
44+
# check that SCMP_ACT_NOTIFY on older kernels fails with a clear error message.
45+
@test "runc run [seccomp] (SCMP_ACT_NOTIFY old kernel)" {
46+
if [[ "$KERNEL_MAJOR" -gt 5 || ("$KERNEL_MAJOR" -eq 5 && "$KERNEL_MINOR" -ge 6) ]]; then
47+
skip "requires kernel less than 5.6"
48+
fi
49+
50+
scmp_act_notify_template "/bin/true" false '"mkdir"'
51+
52+
runc run test_busybox
53+
[ "$status" -ne 0 ]
54+
}
55+
56+
@test "runc run [seccomp] (SCMP_ACT_NOTIFY noNewPrivileges false)" {
57+
requires_kernel 5.6
58+
59+
scmp_act_notify_template "mkdir /dev/shm/foo && stat /dev/shm/foo-bar" false '"mkdir"'
60+
61+
runc run test_busybox
62+
[ "$status" -eq 0 ]
63+
}
64+
65+
@test "runc run [seccomp] (SCMP_ACT_NOTIFY noNewPrivileges true)" {
66+
requires_kernel 5.6
67+
68+
scmp_act_notify_template "mkdir /dev/shm/foo && stat /dev/shm/foo-bar" true '"mkdir"'
69+
70+
runc run test_busybox
71+
[ "$status" -eq 0 ]
72+
}
73+
74+
@test "runc exec [seccomp] (SCMP_ACT_NOTIFY noNewPrivileges false)" {
75+
requires_kernel 5.6
76+
requires root
77+
78+
scmp_act_notify_template "sleep infinity" false '"mkdir"'
79+
80+
runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox
81+
[ "$status" -eq 0 ]
82+
83+
runc exec test_busybox /bin/sh -c "mkdir /dev/shm/foo && stat /dev/shm/foo-bar"
84+
[ "$status" -eq 0 ]
85+
}
86+
87+
@test "runc exec [seccomp] (SCMP_ACT_NOTIFY noNewPrivileges true)" {
88+
requires_kernel 5.6
89+
requires root
90+
91+
scmp_act_notify_template "sleep infinity" true '"mkdir"'
92+
93+
runc run -d --console-socket "$CONSOLE_SOCKET" test_busybox
94+
runc exec test_busybox /bin/sh -c "mkdir /dev/shm/foo && stat /dev/shm/foo-bar"
95+
[ "$status" -eq 0 ]
96+
}
97+
98+
@test "runc run [seccomp] (SCMP_ACT_NOTIFY important syscalls noNewPrivileges false)" {
99+
requires_kernel 5.6
100+
101+
scmp_act_notify_template "/bin/true" false '"execve","openat","open","read","close"'
102+
103+
runc run test_busybox
104+
[ "$status" -eq 0 ]
105+
}
106+
107+
@test "runc run [seccomp] (SCMP_ACT_NOTIFY important syscalls noNewPrivileges true)" {
108+
requires_kernel 5.6
109+
110+
scmp_act_notify_template "/bin/true" true '"execve","openat","open","read","close"'
111+
112+
runc run test_busybox
113+
[ "$status" -eq 0 ]
114+
}
115+
116+
@test "runc run [seccomp] (empty listener path)" {
117+
update_config '.process.args = ["/bin/sh", "-c", "mkdir /dev/shm/foo && stat /dev/shm/foo"] |
118+
.linux.seccomp = {
119+
"defaultAction":"SCMP_ACT_ALLOW",
120+
"listenerPath": "'"$SECCCOMP_AGENT_SOCKET"'",
121+
"listenerMetadata": "bar",
122+
}'
123+
124+
runc run test_busybox
125+
[ "$status" -eq 0 ]
126+
}
127+
128+
@test "runc run [seccomp] (SCMP_ACT_NOTIFY empty listener path)" {
129+
requires_kernel 5.6
130+
131+
scmp_act_notify_template "/bin/true" false '"mkdir"'
132+
update_config '.linux.seccomp.listenerPath = ""'
133+
134+
runc run test_busybox
135+
[ "$status" -ne 0 ]
136+
}
137+
138+
@test "runc run [seccomp] (SCMP_ACT_NOTIFY wrong listener path)" {
139+
requires_kernel 5.6
140+
141+
scmp_act_notify_template "/bin/true" false '"mkdir"'
142+
update_config '.linux.seccomp.listenerPath = "/some-non-existing-listener-path.sock"'
143+
144+
runc run test_busybox
145+
[ "$status" -ne 0 ]
146+
}
147+
148+
@test "runc run [seccomp] (SCMP_ACT_NOTIFY abstract listener path)" {
149+
requires_kernel 5.6
150+
151+
scmp_act_notify_template "/bin/true" false '"mkdir"'
152+
update_config '.linux.seccomp.listenerPath = "@mysocketishere"'
153+
154+
runc run test_busybox
155+
[ "$status" -ne 0 ]
156+
}
157+
158+
# Check that killing the seccompagent doesn't block syscalls in
159+
# the container. They should return ENOSYS instead.
160+
@test "runc run [seccomp] (SCMP_ACT_NOTIFY kill seccompagent)" {
161+
requires_kernel 5.6
162+
163+
scmp_act_notify_template "sleep 4 && mkdir /dev/shm/foo" false '"mkdir"'
164+
165+
sleep 2 && teardown_seccompagent &
166+
runc run test_busybox
167+
[ "$status" -ne 0 ]
168+
[[ "$output" == *"Function not implemented"* ]]
169+
}
170+
171+
# check that trying to use SCMP_ACT_NOTIFY with write() gives a meaningfull error.
172+
@test "runc run [seccomp] (SCMP_ACT_NOTIFY write)" {
173+
requires_kernel 5.6
174+
175+
scmp_act_notify_template "/bin/true" false '"write"'
176+
177+
runc run test_busybox
178+
[ "$status" -ne 0 ]
179+
[[ "$output" == *"SCMP_ACT_NOTIFY cannot be used for the write syscall"* ]]
180+
}
181+
182+
# check that a startContainer hook doesn't get any extra file descriptor.
183+
@test "runc run [seccomp] (SCMP_ACT_NOTIFY startContainer hook)" {
184+
requires_kernel 5.6
185+
186+
# shellcheck disable=SC2016
187+
# For some (unknown) reason shellcheck doesn't like L199 together with the "if" in L210.
188+
update_config '.process.args = ["/bin/true"] |
189+
.linux.seccomp = {
190+
"defaultAction":"SCMP_ACT_ALLOW",
191+
"listenerPath": "'"$SECCCOMP_AGENT_SOCKET"'",
192+
"architectures":["SCMP_ARCH_X86","SCMP_ARCH_X32"],
193+
"syscalls":[{"names":["mkdir"], "action":"SCMP_ACT_NOTIFY"}]
194+
} |
195+
.hooks = {
196+
"startContainer": [
197+
{
198+
"path": "/bin/sh",
199+
"args": [
200+
"sh",
201+
"-c",
202+
"if [ $(ls /proc/self/fd/ | wc -l) -ne 4 ]; then echo \"File descriptors is not 4\". && ls /proc/self/fd/ | wc -l && exit 1; fi"
203+
],
204+
}
205+
]
206+
}'
207+
208+
runc run test_busybox
209+
[ "$status" -eq 0 ]
210+
}

0 commit comments

Comments
 (0)