...
1#!/bin/bash -x
2# Copyright (C) 2017 SUSE LLC
3#
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15
16# rootless.sh -- Runner for rootless container tests. The purpose of this
17# script is to allow for the addition (and testing) of "opportunistic" features
18# to rootless containers while still testing the base features. In order to add
19# a new feature, please match the existing style. Add an entry to $ALL_FEATURES,
20# and add an enable_* and disable_* hook.
21
22set -e -u -o pipefail
23: "${RUNC_USE_SYSTEMD:=}"
24: "${ROOTLESS_TESTPATH:=}"
25
26ALL_FEATURES=("idmap" "cgroup")
27# cgroup is managed by systemd when RUNC_USE_SYSTEMD is set
28if [[ -n "${RUNC_USE_SYSTEMD}" ]]; then
29 ALL_FEATURES=("idmap")
30fi
31ROOT="$(readlink -f "$(dirname "${BASH_SOURCE[0]}")/..")"
32
33# FEATURE: Opportunistic new{uid,gid}map support, allowing a rootless container
34# to be set up with the usage of helper setuid binaries.
35
36function enable_idmap() {
37 export ROOTLESS_UIDMAP_START=100000 ROOTLESS_UIDMAP_LENGTH=65536
38 export ROOTLESS_GIDMAP_START=200000 ROOTLESS_GIDMAP_LENGTH=65536
39
40 # Set up sub{uid,gid} mappings.
41 [ -e /etc/subuid.tmp ] && mv /etc/subuid{.tmp,}
42 (
43 grep -v '^rootless' /etc/subuid
44 echo "rootless:$ROOTLESS_UIDMAP_START:$ROOTLESS_UIDMAP_LENGTH"
45 ) >/etc/subuid.tmp
46 mv /etc/subuid{.tmp,}
47 [ -e /etc/subgid.tmp ] && mv /etc/subgid{.tmp,}
48 (
49 grep -v '^rootless' /etc/subgid
50 echo "rootless:$ROOTLESS_GIDMAP_START:$ROOTLESS_GIDMAP_LENGTH"
51 ) >/etc/subgid.tmp
52 mv /etc/subgid{.tmp,}
53
54 # Reactivate new{uid,gid}map helpers if applicable.
55 [ -e /usr/bin/unused-newuidmap ] && mv /usr/bin/{unused-,}newuidmap
56 [ -e /usr/bin/unused-newgidmap ] && mv /usr/bin/{unused-,}newgidmap
57
58 # Create a directory owned by $AUX_UID inside container, to be used
59 # by a test case in cwd.bats. This setup can't be done by the test itself,
60 # as it needs root for chown.
61 export AUX_UID=1024
62 AUX_DIR="$(mktemp -d)"
63 # 1000 is linux.uidMappings.containerID value,
64 # as set by runc_rootless_idmap
65 chown "$((ROOTLESS_UIDMAP_START - 1000 + AUX_UID))" "$AUX_DIR"
66 export AUX_DIR
67}
68
69function disable_idmap() {
70 export ROOTLESS_UIDMAP_START ROOTLESS_UIDMAP_LENGTH
71 export ROOTLESS_GIDMAP_START ROOTLESS_GIDMAP_LENGTH
72
73 # Deactivate sub{uid,gid} mappings.
74 [ -e /etc/subuid ] && mv /etc/subuid{,.tmp}
75 [ -e /etc/subgid ] && mv /etc/subgid{,.tmp}
76
77 # Deactivate new{uid,gid}map helpers. setuid is preserved with mv(1).
78 [ -e /usr/bin/newuidmap ] && mv /usr/bin/{,unused-}newuidmap
79 [ -e /usr/bin/newgidmap ] && mv /usr/bin/{,unused-}newgidmap
80
81 return 0
82}
83
84function cleanup() {
85 if [ -v AUX_DIR ]; then
86 rmdir "$AUX_DIR"
87 unset AUX_DIX
88 fi
89}
90
91# FEATURE: Opportunistic cgroups support, allowing a rootless container to set
92# resource limits on condition that cgroupsPath is set to a path the
93# rootless user has permissions on.
94
95# List of cgroups. We handle name= cgroups as well as combined
96# (comma-separated) cgroups and correctly split and/or strip them.
97# shellcheck disable=SC2207
98ALL_CGROUPS=($(cut -d: -f2 </proc/self/cgroup | sed -E '{s/^name=//;s/,/\n/;/^$/D}'))
99CGROUP_MOUNT="/sys/fs/cgroup"
100CGROUP_PATH="/runc-cgroups-integration-test"
101
102function enable_cgroup() {
103 # Set up cgroups for use in rootless containers.
104 for cg in "${ALL_CGROUPS[@]}"; do
105 mkdir -p "$CGROUP_MOUNT/$cg$CGROUP_PATH"
106 # We only need to allow write access to {cgroup.procs,tasks} and the
107 # directory. Rather than changing the owner entirely, we just change
108 # the group and then allow write access to the group (in order to
109 # further limit the possible DAC permissions that runc could use).
110 chown root:rootless "$CGROUP_MOUNT/$cg$CGROUP_PATH/"{,cgroup.procs,tasks}
111 chmod g+rwx "$CGROUP_MOUNT/$cg$CGROUP_PATH/"{,cgroup.procs,tasks}
112 # Due to cpuset's semantics we need to give extra permissions to allow
113 # for runc to set up the hierarchy. XXX: This really shouldn't be
114 # necessary, and might actually be a bug in our impl of cgroup
115 # handling.
116 [[ "$cg" == "cpuset" ]] && chown rootless:rootless "$CGROUP_MOUNT/$cg$CGROUP_PATH/cpuset."{cpus,mems}
117 # The following is required by "update rt period and runtime".
118 if [[ "$cg" == "cpu" ]]; then
119 if [[ -e "$CGROUP_MOUNT/$cg$CGROUP_PATH/cpu.rt_period_us" ]]; then
120 chown rootless:rootless "$CGROUP_MOUNT/$cg$CGROUP_PATH/cpu.rt_period_us"
121 fi
122 if [[ -e "$CGROUP_MOUNT/$cg$CGROUP_PATH/cpu.rt_runtime_us" ]]; then
123 chown rootless:rootless "$CGROUP_MOUNT/$cg$CGROUP_PATH/cpu.rt_runtime_us"
124 fi
125 fi
126 done
127 # cgroup v2
128 if [[ -e "$CGROUP_MOUNT/cgroup.controllers" ]]; then
129 # Enable controllers. Some controller (e.g. memory) may fail on containerized environment.
130 set -x
131 # shellcheck disable=SC2013
132 for f in $(cat "$CGROUP_MOUNT/cgroup.controllers"); do echo "+$f" >"$CGROUP_MOUNT/cgroup.subtree_control"; done
133 set +x
134 # Create the cgroup.
135 mkdir -p "$CGROUP_MOUNT/$CGROUP_PATH"
136 # chown/chmod dir + cgroup.subtree_control + cgroup.procs + parent's cgroup.procs.
137 # See https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html#delegation-containment
138 chown root:rootless "$CGROUP_MOUNT/$CGROUP_PATH" "$CGROUP_MOUNT/$CGROUP_PATH/cgroup.subtree_control" "$CGROUP_MOUNT/$CGROUP_PATH/cgroup.procs" "$CGROUP_MOUNT/cgroup.procs"
139 chmod g+rwx "$CGROUP_MOUNT/$CGROUP_PATH"
140 chmod g+rw "$CGROUP_MOUNT/$CGROUP_PATH/cgroup.subtree_control" "$CGROUP_MOUNT/$CGROUP_PATH/cgroup.procs" "$CGROUP_MOUNT/cgroup.procs"
141 fi
142}
143
144function disable_cgroup() {
145 # Remove cgroups used in rootless containers.
146 for cg in "${ALL_CGROUPS[@]}"; do
147 [ -d "$CGROUP_MOUNT/$cg$CGROUP_PATH" ] && rmdir "$CGROUP_MOUNT/$cg$CGROUP_PATH"
148 done
149 # cgroup v2
150 [ -d "$CGROUP_MOUNT/$CGROUP_PATH" ] && rmdir "$CGROUP_MOUNT/$CGROUP_PATH"
151
152 return 0
153}
154
155# Create a powerset of $ALL_FEATURES (the set of all subsets of $ALL_FEATURES).
156# We test all of the possible combinations (as long as we don't add too many
157# feature knobs this shouldn't take too long -- but the number of tested
158# combinations is O(2^n)).
159function powerset() {
160 eval printf '%s' "$(printf '{,%s+}' "$@")":
161}
162features_powerset="$(powerset "${ALL_FEATURES[@]}")"
163
164# Make sure we have container images downloaded, as otherwise
165# rootless user won't be able to write to $TESTDATA.
166"$ROOT"/tests/integration/get-images.sh >/dev/null
167
168# Iterate over the powerset of all features.
169IFS=:
170idx=0
171for enabled_features in $features_powerset; do
172 ((++idx))
173 printf "[%.2d] run rootless tests ... (${enabled_features%%+})\n" "$idx"
174
175 unset IFS
176 for feature in "${ALL_FEATURES[@]}"; do
177 hook_func="disable_$feature"
178 grep -E "(^|\+)$feature(\+|$)" <<<"$enabled_features" &>/dev/null && hook_func="enable_$feature"
179 "$hook_func"
180 done
181
182 # Run the test suite!
183 echo "path: $PATH"
184 export ROOTLESS_FEATURES="$enabled_features"
185 if [[ -n "${RUNC_USE_SYSTEMD}" ]]; then
186 # We use `ssh rootless@localhost` instead of `sudo -u rootless` for creating systemd user session.
187 # Alternatively we could use `machinectl shell`, but it is known not to work well on SELinux-enabled hosts as of April 2020:
188 # https://bugzilla.redhat.com/show_bug.cgi?id=1788616
189 ssh -t -t -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i "$HOME/rootless.key" rootless@localhost -- PATH="$PATH" RUNC_USE_SYSTEMD="$RUNC_USE_SYSTEMD" bats -t "$ROOT/tests/integration$ROOTLESS_TESTPATH"
190 else
191 sudo -HE -u rootless PATH="$PATH" "$(which bats)" -t "$ROOT/tests/integration$ROOTLESS_TESTPATH"
192 fi
193 cleanup
194done
View as plain text