github.com/opencontainers/runc@v1.2.0-rc.1.0.20240520010911-492dc558cdd6/tests/rootless.sh (about)

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