mirror of
https://github.com/opencontainers/runc.git
synced 2025-09-26 19:41:35 +08:00
265 lines
7.1 KiB
Bash
265 lines
7.1 KiB
Bash
#!/usr/bin/env bats
|
|
|
|
load helpers
|
|
|
|
function setup() {
|
|
setup_busybox
|
|
}
|
|
|
|
function teardown() {
|
|
teardown_bundle
|
|
}
|
|
|
|
# https://github.com/opencontainers/runc/security/advisories/GHSA-m8cg-xc2p-r3fc
|
|
#
|
|
# This needs to be placed at the top of the bats file to work around
|
|
# a shellcheck bug. See <https://github.com/koalaman/shellcheck/issues/2873>.
|
|
function test_ro_cgroup_mount() {
|
|
local lines status
|
|
# shellcheck disable=SC2016
|
|
update_config '.process.args |= ["sh", "-euc", "for f in `grep /sys/fs/cgroup /proc/mounts | awk \"{print \\\\$2}\"| uniq`; do test -e $f && grep -w $f /proc/mounts | tail -n1; done"]'
|
|
runc run test_busybox
|
|
[ "$status" -eq 0 ]
|
|
[ "${#lines[@]}" -ne 0 ]
|
|
for line in "${lines[@]}"; do [[ "${line}" == *'ro,'* ]]; done
|
|
}
|
|
|
|
# Parse an "optstring" of the form foo,bar into $is_foo and $is_bar variables.
|
|
# Usage: parse_optstring foo,bar foo bar baz
|
|
function parse_optstring() {
|
|
optstring="$1"
|
|
shift
|
|
|
|
for flag in "$@"; do
|
|
is_set=
|
|
if grep -wq "$flag" <<<"$optstring"; then
|
|
is_set=1
|
|
fi
|
|
eval "is_$flag=$is_set"
|
|
done
|
|
}
|
|
|
|
function config_add_bind_mount() {
|
|
src="$1"
|
|
dst="$2"
|
|
parse_optstring "${3:-}" rbind idmap
|
|
|
|
bindtype=bind
|
|
if [ -n "$is_rbind" ]; then
|
|
bindtype=rbind
|
|
fi
|
|
|
|
mappings=""
|
|
if [ -n "$is_idmap" ]; then
|
|
mappings='
|
|
"uidMappings": [{"containerID": 0, "hostID": 100000, "size": 65536}],
|
|
"gidMappings": [{"containerID": 0, "hostID": 100000, "size": 65536}],
|
|
'
|
|
fi
|
|
|
|
update_config '.mounts += [{
|
|
"source": "'"$src"'",
|
|
"destination": "'"$dst"'",
|
|
"type": "bind",
|
|
'"$mappings"'
|
|
"options": [ "'"$bindtype"'", "rprivate" ]
|
|
}]'
|
|
}
|
|
|
|
# This needs to be placed at the top of the bats file to work around
|
|
# a shellcheck bug. See <https://github.com/koalaman/shellcheck/issues/2873>.
|
|
function test_mount_order() {
|
|
parse_optstring "${1:-}" userns idmap
|
|
|
|
if [ -n "$is_userns" ]; then
|
|
requires root
|
|
|
|
update_config '.linux.namespaces += [{"type": "user"}]
|
|
| .linux.uidMappings += [{"containerID": 0, "hostID": 100000, "size": 65536}]
|
|
| .linux.gidMappings += [{"containerID": 0, "hostID": 100000, "size": 65536}]'
|
|
remap_rootfs
|
|
fi
|
|
|
|
ctr_src_opts="rbind"
|
|
if [ -n "$is_idmap" ]; then
|
|
requires root
|
|
requires_kernel 5.12
|
|
requires_idmap_fs .
|
|
|
|
ctr_src_opts+=",idmap"
|
|
fi
|
|
|
|
mkdir -p rootfs/{mnt,final}
|
|
# Create a set of directories we can create a mount tree with.
|
|
for subdir in a/x b/y c/z; do
|
|
dir="bind-src/$subdir"
|
|
mkdir -p "$dir"
|
|
echo "$subdir" >"$dir/file"
|
|
# Add a symlink to make sure
|
|
topdir="$(dirname "$subdir")"
|
|
ln -s "$topdir" "bind-src/sym-$topdir"
|
|
done
|
|
# In userns tests, make sure that the source directory cannot be accessed,
|
|
# to make sure we're exercising the bind-mount source fd logic.
|
|
chmod o-rwx bind-src
|
|
|
|
rootfs="$(pwd)/rootfs"
|
|
rm -rf rootfs/mnt
|
|
mkdir rootfs/mnt
|
|
|
|
# Create a bind-mount tree.
|
|
config_add_bind_mount "$PWD/bind-src/a" "/mnt"
|
|
config_add_bind_mount "$PWD/bind-src/sym-b" "/mnt/x"
|
|
config_add_bind_mount "$PWD/bind-src/c" "/mnt/x/y"
|
|
config_add_bind_mount "$PWD/bind-src/sym-a" "/mnt/x/y/z"
|
|
# Create a recursive bind-mount that uses part of the current tree in the
|
|
# container.
|
|
config_add_bind_mount "$rootfs/mnt/x" "$rootfs/mnt/x/y/z/x" "$ctr_src_opts"
|
|
config_add_bind_mount "$rootfs/mnt/x/y" "$rootfs/mnt/x/y/z" "$ctr_src_opts"
|
|
# Finally, bind-mount the whole thing on top of /final.
|
|
config_add_bind_mount "$rootfs/mnt" "$rootfs/final" "$ctr_src_opts"
|
|
|
|
# Check that the entire tree was copied and the mounts were done in the
|
|
# expected order.
|
|
update_config '.process.args = ["cat", "/final/x/y/z/z/x/y/z/x/file"]'
|
|
runc run test_busybox
|
|
[ "$status" -eq 0 ]
|
|
[[ "$output" == *"a/x"* ]] # the final "file" was from a/x.
|
|
}
|
|
|
|
# https://github.com/opencontainers/runc/issues/3991
|
|
@test "runc run [tmpcopyup]" {
|
|
mkdir -p rootfs/dir1/dir2
|
|
chmod 777 rootfs/dir1/dir2
|
|
update_config ' .mounts += [{
|
|
source: "tmpfs",
|
|
destination: "/dir1",
|
|
type: "tmpfs",
|
|
options: ["tmpcopyup"]
|
|
}]
|
|
| .process.args |= ["ls", "-ld", "/dir1/dir2"]'
|
|
|
|
umask 022
|
|
runc run test_busybox
|
|
[ "$status" -eq 0 ]
|
|
[[ "${lines[0]}" == *'drwxrwxrwx'* ]]
|
|
}
|
|
|
|
@test "runc run [bind mount]" {
|
|
update_config ' .mounts += [{
|
|
source: ".",
|
|
destination: "/tmp/bind",
|
|
options: ["bind"]
|
|
}]
|
|
| .process.args |= ["ls", "/tmp/bind/config.json"]'
|
|
|
|
runc run test_busybox
|
|
[ "$status" -eq 0 ]
|
|
[[ "${lines[0]}" == *'/tmp/bind/config.json'* ]]
|
|
}
|
|
|
|
# https://github.com/opencontainers/runc/issues/2246
|
|
@test "runc run [ro tmpfs mount]" {
|
|
update_config ' .mounts += [{
|
|
source: "tmpfs",
|
|
destination: "/mnt",
|
|
type: "tmpfs",
|
|
options: ["ro", "nodev", "nosuid", "mode=755"]
|
|
}]
|
|
| .process.args |= ["grep", "^tmpfs /mnt", "/proc/mounts"]'
|
|
|
|
runc run test_busybox
|
|
[ "$status" -eq 0 ]
|
|
[[ "${lines[0]}" == *'ro,'* ]]
|
|
}
|
|
|
|
# https://github.com/opencontainers/runc/issues/3248
|
|
@test "runc run [ro /dev mount]" {
|
|
update_config ' .mounts |= map((select(.destination == "/dev") | .options += ["ro"]) // .)
|
|
| .process.args |= ["grep", "^tmpfs /dev", "/proc/mounts"]'
|
|
|
|
runc run test_busybox
|
|
[ "$status" -eq 0 ]
|
|
[[ "${lines[0]}" == *'ro,'* ]]
|
|
}
|
|
|
|
# https://github.com/opencontainers/runc/issues/2683
|
|
@test "runc run [tmpfs mount with absolute symlink]" {
|
|
# in container, /conf -> /real/conf
|
|
mkdir -p rootfs/real/conf
|
|
ln -s /real/conf rootfs/conf
|
|
update_config ' .mounts += [{
|
|
type: "tmpfs",
|
|
source: "tmpfs",
|
|
destination: "/conf/stack",
|
|
options: ["ro", "nodev", "nosuid"]
|
|
}]
|
|
| .process.args |= ["true"]'
|
|
runc run test_busybox
|
|
[ "$status" -eq 0 ]
|
|
}
|
|
|
|
# CVE-2023-27561 CVE-2019-19921
|
|
@test "runc run [/proc is a symlink]" {
|
|
# Make /proc in the container a symlink.
|
|
rm -rf rootfs/proc
|
|
mkdir -p rootfs/bad-proc
|
|
ln -sf /bad-proc rootfs/proc
|
|
# This should fail.
|
|
runc run test_busybox
|
|
[ "$status" -ne 0 ]
|
|
[[ "$output" == *"must be mounted on ordinary directory"* ]]
|
|
}
|
|
|
|
# https://github.com/opencontainers/runc/issues/4401
|
|
@test "runc run [setgid / + mkdirall]" {
|
|
mkdir rootfs/setgid
|
|
chmod '=7755' rootfs/setgid
|
|
|
|
update_config '.mounts += [{
|
|
type: "tmpfs",
|
|
source: "tmpfs",
|
|
destination: "/setgid/a/b/c",
|
|
options: ["ro", "nodev", "nosuid"]
|
|
}]'
|
|
update_config '.process.args |= ["true"]'
|
|
|
|
runc run test_busybox
|
|
[ "$status" -eq 0 ]
|
|
|
|
# Verify that the setgid bit is inherited.
|
|
[[ "$(stat -c %a rootfs/setgid)" == 7755 ]]
|
|
[[ "$(stat -c %a rootfs/setgid/a)" == 2755 ]]
|
|
[[ "$(stat -c %a rootfs/setgid/a/b)" == 2755 ]]
|
|
[[ "$(stat -c %a rootfs/setgid/a/b/c)" == 2755 ]]
|
|
}
|
|
|
|
@test "runc run [ro /sys/fs/cgroup mounts]" {
|
|
# Without cgroup namespace.
|
|
update_config '.linux.namespaces -= [{"type": "cgroup"}]'
|
|
test_ro_cgroup_mount
|
|
}
|
|
|
|
@test "runc run [ro /sys/fs/cgroup mounts + cgroupns]" {
|
|
requires cgroupns
|
|
# With cgroup namespace.
|
|
update_config '.linux.namespaces |= if index({"type": "cgroup"}) then . else . + [{"type": "cgroup"}] end'
|
|
test_ro_cgroup_mount
|
|
}
|
|
|
|
@test "runc run [mount order, container bind-mount source]" {
|
|
test_mount_order
|
|
}
|
|
|
|
@test "runc run [mount order, container bind-mount source] (userns)" {
|
|
test_mount_order userns
|
|
}
|
|
|
|
@test "runc run [mount order, container idmap source]" {
|
|
test_mount_order idmap
|
|
}
|
|
|
|
@test "runc run [mount order, container idmap source] (userns)" {
|
|
test_mount_order userns,idmap
|
|
}
|