File: rootless.sh

package info (click to toggle)
runc 1.3.3%2Bds1-3
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 3,136 kB
  • sloc: sh: 2,298; ansic: 1,125; makefile: 229
file content (193 lines) | stat: -rwxr-xr-x 7,831 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#!/bin/bash
# Copyright (C) 2017 SUSE LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# rootless.sh -- Runner for rootless container tests. The purpose of this
# script is to allow for the addition (and testing) of "opportunistic" features
# to rootless containers while still testing the base features. In order to add
# a new feature, please match the existing style. Add an entry to $ALL_FEATURES,
# and add an enable_* and disable_* hook.

set -e -u -o pipefail
: "${ROOTLESS_TESTPATH:=}"

ALL_FEATURES=("idmap" "cgroup")
# cgroup is managed by systemd when RUNC_USE_SYSTEMD is set.
if [ -v RUNC_USE_SYSTEMD ]; then
	ALL_FEATURES=("idmap")
fi
ROOT="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")/..")"

# FEATURE: Opportunistic new{uid,gid}map support, allowing a rootless container
#          to be set up with the usage of helper setuid binaries.

function enable_idmap() {
	export ROOTLESS_UIDMAP_START=100000 ROOTLESS_UIDMAP_LENGTH=65536
	export ROOTLESS_GIDMAP_START=200000 ROOTLESS_GIDMAP_LENGTH=65536

	# Set up sub{uid,gid} mappings.
	[ -e /etc/subuid.tmp ] && mv /etc/subuid{.tmp,}
	(
		grep -v '^rootless' /etc/subuid
		echo "rootless:$ROOTLESS_UIDMAP_START:$ROOTLESS_UIDMAP_LENGTH"
	) >/etc/subuid.tmp
	mv /etc/subuid{.tmp,}
	[ -e /etc/subgid.tmp ] && mv /etc/subgid{.tmp,}
	(
		grep -v '^rootless' /etc/subgid
		echo "rootless:$ROOTLESS_GIDMAP_START:$ROOTLESS_GIDMAP_LENGTH"
	) >/etc/subgid.tmp
	mv /etc/subgid{.tmp,}

	# Reactivate new{uid,gid}map helpers if applicable.
	[ -e /usr/bin/unused-newuidmap ] && mv /usr/bin/{unused-,}newuidmap
	[ -e /usr/bin/unused-newgidmap ] && mv /usr/bin/{unused-,}newgidmap

	# Create a directory owned by $AUX_UID inside container, to be used
	# by a test case in cwd.bats. This setup can't be done by the test itself,
	# as it needs root for chown.
	export AUX_UID=1024
	AUX_DIR="$(mktemp -d)"
	# 1000 is linux.uidMappings.containerID value,
	# as set by runc_rootless_idmap
	chown "$((ROOTLESS_UIDMAP_START - 1000 + AUX_UID))" "$AUX_DIR"
	export AUX_DIR
}

function disable_idmap() {
	export ROOTLESS_UIDMAP_START ROOTLESS_UIDMAP_LENGTH
	export ROOTLESS_GIDMAP_START ROOTLESS_GIDMAP_LENGTH

	# Deactivate sub{uid,gid} mappings.
	[ -e /etc/subuid ] && mv /etc/subuid{,.tmp}
	[ -e /etc/subgid ] && mv /etc/subgid{,.tmp}

	# Deactivate new{uid,gid}map helpers. setuid is preserved with mv(1).
	[ -e /usr/bin/newuidmap ] && mv /usr/bin/{,unused-}newuidmap
	[ -e /usr/bin/newgidmap ] && mv /usr/bin/{,unused-}newgidmap

	return 0
}

function cleanup() {
	if [ -v AUX_DIR ]; then
		rmdir "$AUX_DIR"
		unset AUX_DIX
	fi
}

# FEATURE: Opportunistic cgroups support, allowing a rootless container to set
#          resource limits on condition that cgroupsPath is set to a path the
#          rootless user has permissions on.

# List of cgroups. We handle name= cgroups as well as combined
# (comma-separated) cgroups and correctly split and/or strip them.
# shellcheck disable=SC2207
ALL_CGROUPS=($(cut -d: -f2 </proc/self/cgroup | sed -E '{s/^name=//;s/,/\n/;/^$/D}'))
CGROUP_MOUNT="/sys/fs/cgroup"
CGROUP_PATH="/runc-cgroups-integration-test"

function enable_cgroup() {
	# Set up cgroups for use in rootless containers.
	for cg in "${ALL_CGROUPS[@]}"; do
		mkdir -p "$CGROUP_MOUNT/$cg$CGROUP_PATH"
		# We only need to allow write access to {cgroup.procs,tasks} and the
		# directory. Rather than changing the owner entirely, we just change
		# the group and then allow write access to the group (in order to
		# further limit the possible DAC permissions that runc could use).
		chown root:rootless "$CGROUP_MOUNT/$cg$CGROUP_PATH/"{,cgroup.procs,tasks}
		chmod g+rwx "$CGROUP_MOUNT/$cg$CGROUP_PATH/"{,cgroup.procs,tasks}
		# Due to cpuset's semantics we need to give extra permissions to allow
		# for runc to set up the hierarchy. XXX: This really shouldn't be
		# necessary, and might actually be a bug in our impl of cgroup
		# handling.
		[ "$cg" = "cpuset" ] && chown rootless:rootless "$CGROUP_MOUNT/$cg$CGROUP_PATH/cpuset."{cpus,mems}
		# The following is required by "update rt period and runtime".
		if [ "$cg" = "cpu" ]; then
			if [[ -e "$CGROUP_MOUNT/$cg$CGROUP_PATH/cpu.rt_period_us" ]]; then
				chown rootless:rootless "$CGROUP_MOUNT/$cg$CGROUP_PATH/cpu.rt_period_us"
			fi
			if [[ -e "$CGROUP_MOUNT/$cg$CGROUP_PATH/cpu.rt_runtime_us" ]]; then
				chown rootless:rootless "$CGROUP_MOUNT/$cg$CGROUP_PATH/cpu.rt_runtime_us"
			fi
		fi
	done
	# cgroup v2
	if [[ -e "$CGROUP_MOUNT/cgroup.controllers" ]]; then
		# Enable controllers. Some controller (e.g. memory) may fail on containerized environment.
		set -x
		# shellcheck disable=SC2013
		for f in $(cat "$CGROUP_MOUNT/cgroup.controllers"); do echo "+$f" >"$CGROUP_MOUNT/cgroup.subtree_control"; done
		set +x
		# Create the cgroup.
		mkdir -p "$CGROUP_MOUNT/$CGROUP_PATH"
		# chown/chmod dir + cgroup.subtree_control + cgroup.procs + parent's cgroup.procs.
		# See https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#delegation-containment
		chown root:rootless "$CGROUP_MOUNT/$CGROUP_PATH" "$CGROUP_MOUNT/$CGROUP_PATH/cgroup.subtree_control" "$CGROUP_MOUNT/$CGROUP_PATH/cgroup.procs" "$CGROUP_MOUNT/cgroup.procs"
		chmod g+rwx "$CGROUP_MOUNT/$CGROUP_PATH"
		chmod g+rw "$CGROUP_MOUNT/$CGROUP_PATH/cgroup.subtree_control" "$CGROUP_MOUNT/$CGROUP_PATH/cgroup.procs" "$CGROUP_MOUNT/cgroup.procs"
	fi
}

function disable_cgroup() {
	# Remove cgroups used in rootless containers.
	for cg in "${ALL_CGROUPS[@]}"; do
		[ -d "$CGROUP_MOUNT/$cg$CGROUP_PATH" ] && rmdir "$CGROUP_MOUNT/$cg$CGROUP_PATH"
	done
	# cgroup v2
	[ -d "$CGROUP_MOUNT/$CGROUP_PATH" ] && rmdir "$CGROUP_MOUNT/$CGROUP_PATH"

	return 0
}

# Create a powerset of $ALL_FEATURES (the set of all subsets of $ALL_FEATURES).
# We test all of the possible combinations (as long as we don't add too many
# feature knobs this shouldn't take too long -- but the number of tested
# combinations is O(2^n)).
function powerset() {
	eval printf '%s' "$(printf '{,%s+}' "$@")":
}
features_powerset="$(powerset "${ALL_FEATURES[@]}")"

# Make sure we have container images downloaded, as otherwise
# rootless user won't be able to write to $TESTDATA.
"$ROOT"/tests/integration/get-images.sh >/dev/null

# Iterate over the powerset of all features.
IFS=:
idx=0
for enabled_features in $features_powerset; do
	((++idx))
	printf "[%.2d] run rootless tests ... (${enabled_features%%+})\n" "$idx"

	unset IFS
	for feature in "${ALL_FEATURES[@]}"; do
		hook_func="disable_$feature"
		grep -E "(^|\+)$feature(\+|$)" <<<"$enabled_features" &>/dev/null && hook_func="enable_$feature"
		"$hook_func"
	done

	# Run the test suite!
	echo "path: $PATH"
	export ROOTLESS_FEATURES="$enabled_features"
	if [ -v RUNC_USE_SYSTEMD ]; then
		# We use `ssh rootless@localhost` instead of `sudo -u rootless` for creating systemd user session.
		# Alternatively we could use `machinectl shell`, but it is known not to work well on SELinux-enabled hosts as of April 2020:
		# https://bugzilla.redhat.com/show_bug.cgi?id=1788616
		ssh -t -t -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i "$HOME/.ssh/rootless.key" rootless@localhost -- PATH="$PATH" RUNC_USE_SYSTEMD="$RUNC_USE_SYSTEMD" bats -t "$ROOT/tests/integration$ROOTLESS_TESTPATH"
	else
		sudo -HE -u rootless PATH="$PATH" "$(which bats)" -t "$ROOT/tests/integration$ROOTLESS_TESTPATH"
	fi
	cleanup
done