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
|
#!/usr/bin/env bats
load helpers
@test "chroot mount flags" {
skip_if_no_unshare
if ! test -e /etc/subuid ; then
skip "we can't bind mount over /etc/subuid during the test if there is no /etc/subuid file"
fi
if ! test -e /etc/subgid ; then
skip "we can't bind mount over /etc/subgid during the test if there is no /etc/subgid file"
fi
# whom should we map to root in a nested namespace?
if is_rootless ; then
subid=128
rangesize=1024
else
subid=1048576
rangesize=16384
fi
# we're going to have to prefetch into storage used by someone else image
# chosen because its rootfs doesn't have any uid/gid ownership above
# $rangesize, because the nested namespace needs to be able to represent all
# of them
baseimage=registry.access.redhat.com/ubi8-micro:latest
_prefetch $baseimage
baseimagef=$(tr -c a-zA-Z0-9.- - <<< "$baseimage")
# create the directories that we need
tmpfs=${TEST_SCRATCH_DIR}/tmpfs
mkdir $tmpfs
context=${TEST_SCRATCH_DIR}/context
mkdir $context
storagedir=${TEST_SCRATCH_DIR}/storage
mkdir $storagedir
rootdir=${storagedir}/rootdir
mkdir $rootdir
runrootdir=${storagedir}/runrootdir
mkdir $runrootdir
xdgruntimedir=${storagedir}/xdgruntime
mkdir $xdgruntimedir
xdgconfighome=${storagedir}/xdgconfighome
mkdir $xdgconfighome
xdgdatahome=${storagedir}/xdgdatahome
mkdir $xdgdatahome
storageopts="--storage-driver vfs --root $rootdir --runroot $runrootdir"
# our temporary parent directory might not be world-searchable, which will
# cause someone in the nested user namespace to hit permissions issues even
# looking for $storagedir, so tweak perms to let them do at least that much
fixupdir=$storagedir
while test $(stat -c %d:%i $fixupdir) != $(stat -c %d:%i /) ; do
# walk up to root, or the first parent that we don't own
if test $(stat -c %u $fixupdir) -ne $(id -u) ; then
break
fi
chmod +x $fixupdir
fixupdir=$fixupdir/..
done
# start writing the script to run in the nested user namespace
cp -v ${TEST_SOURCES}/containers.conf ${TEST_SCRATCH_DIR}/containers.conf
chmod ugo+r ${TEST_SCRATCH_DIR}/containers.conf
echo set -e > ${TEST_SCRATCH_DIR}/script.sh
echo export XDG_RUNTIME_DIR=$xdgruntimedir >> ${TEST_SCRATCH_DIR}/script.sh
echo export XDG_CONFIG_HOME=$xdgconfighome >> ${TEST_SCRATCH_DIR}/script.sh
echo export XDG_DATA_HOME=$xdgdatahome >> ${TEST_SCRATCH_DIR}/script.sh
echo export CONTAINERS_CONF=${TEST_SCRATCH_DIR}/containers.conf >> ${TEST_SCRATCH_DIR}/script.sh
# give our would-be user ownership of that directory
echo chown --recursive ${subid}:${subid} ${storagedir} >> ${TEST_SCRATCH_DIR}/script.sh
# make newuidmap/newgidmap, invoked by unshare even for uid=0, happy
echo root:0:4294967295 > ${TEST_SCRATCH_DIR}/subid
echo mount --bind -r ${TEST_SCRATCH_DIR}/subid /etc/subuid >> ${TEST_SCRATCH_DIR}/script.sh
echo mount --bind -r ${TEST_SCRATCH_DIR}/subid /etc/subgid >> ${TEST_SCRATCH_DIR}/script.sh
# don't get tripped up by ${TEST_SCRATCH_DIR} potentially being on a filesystem with non-default mount flags
echo mount -t tmpfs -o size=256K tmpfs $tmpfs >> ${TEST_SCRATCH_DIR}/script.sh
# mount a small tmpfs with every mount flag combination that concerns us, and
# be ready to tell buildah to mount everything conservatively, to mirror the
# TransientMounts API being used to nodev/noexec/nosuid/ro bind in a source
# that doesn't necessarily have those flags already set on it
for d in dev nodev ; do
for e in exec noexec ; do
for s in suid nosuid ; do
for r in ro rw ; do
subdir=$tmpfs/d-$d-$e-$s-$r
echo mkdir ${subdir} >> ${TEST_SCRATCH_DIR}/script.sh
echo mount -t tmpfs -o size=256K,$d,$e,$s,$r tmpfs ${subdir} >> ${TEST_SCRATCH_DIR}/script.sh
mounts="${mounts:+${mounts} }--volume ${subdir}:/mounts/d-$d-$e-$s-$r:nodev,noexec,nosuid,ro"
done
done
done
done
# copy binaries to a location where parent directory permissions are less
# likely to interfere with running them from a different UID
cp ${COPY_BINARY} ${TEST_SCRATCH_DIR}/copy
cp ${BUILDAH_BINARY} ${TEST_SCRATCH_DIR}/buildah
# make sure that RUN doesn't just break when we try to use volume mounts with
# flags set that we're not allowed to modify
echo FROM $baseimage > $context/Dockerfile
echo RUN cat /proc/mounts >> $context/Dockerfile
# copy in the prefetched image
# unshare from util-linux 2.39 also accepts INNER:OUTER:SIZE for --map-users
# and --map-groups, but fedora 37's is too old, so the older OUTER,INNER,SIZE
# (using commas instead of colons as field separators) will have to do
echo "env | sort" >> ${TEST_SCRATCH_DIR}/script.sh
echo "env _CONTAINERS_USERNS_CONFIGURED=done unshare -Umpf --mount-proc --setuid 0 --setgid 0 --map-users=${subid},0,${rangesize} --map-groups=${subid},0,${rangesize} ${TEST_SCRATCH_DIR}/copy ${storageopts} dir:$_BUILDAH_IMAGE_CACHEDIR/$baseimagef containers-storage:$baseimage" >> ${TEST_SCRATCH_DIR}/script.sh
# try to do a build with all of the volume mounts
echo "env _CONTAINERS_USERNS_CONFIGURED=done unshare -Umpf --mount-proc --setuid 0 --setgid 0 --map-users=${subid},0,${rangesize} --map-groups=${subid},0,${rangesize} ${TEST_SCRATCH_DIR}/buildah ${BUILDAH_REGISTRY_OPTS} ${storageopts} build --isolation chroot --pull=never $mounts $context" >> ${TEST_SCRATCH_DIR}/script.sh
# run that whole script in a nested mount namespace with no $XDG_...
# variables leaked into it
if is_rootless ; then
run_buildah unshare env -i bash -x ${TEST_SCRATCH_DIR}/script.sh
else
unshare -mpf --mount-proc env -i bash -x ${TEST_SCRATCH_DIR}/script.sh
fi
}
@test "chroot with overlay root" {
if test `uname` != Linux ; then
skip "not meaningful except on Linux"
fi
skip_if_no_unshare
if [ "$(id -u)" -ne 0 ]; then
skip "expects to already be root"
fi
_prefetch docker.io/library/busybox
cp -v ${TEST_SOURCES}/containers.conf ${TEST_SCRATCH_DIR}/containers.conf
chmod ugo+r ${TEST_SCRATCH_DIR}/containers.conf
mkdir -p ${TEST_SCRATCH_DIR}/chroot
chown -R 1:1 ${TEST_SCRATCH_DIR}/root ${TEST_SCRATCH_DIR}/runroot ${TEST_SCRATCH_DIR}/chroot
cat > ${TEST_SCRATCH_DIR}/script1 <<- EOF
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin${PATH:+:$PATH}
set -e
set -x
mkdir -p ${TEST_SCRATCH_DIR}/chroot/workdir
mkdir -p ${TEST_SCRATCH_DIR}/chroot/upperdir
mkdir -p ${TEST_SCRATCH_DIR}/chroot/merged
mount -t overlay overlay -o upperdir=${TEST_SCRATCH_DIR}/chroot/upperdir,workdir=${TEST_SCRATCH_DIR}/chroot/workdir,lowerdir=/ ${TEST_SCRATCH_DIR}/chroot/merged
mount -t proc proc ${TEST_SCRATCH_DIR}/chroot/merged/proc
mount -t sysfs sysfs ${TEST_SCRATCH_DIR}/chroot/merged/sys
mount --bind /dev ${TEST_SCRATCH_DIR}/chroot/merged/dev
mount --bind /etc ${TEST_SCRATCH_DIR}/chroot/merged/etc
echo build > ${TEST_SCRATCH_DIR}/chroot/hostname
chmod 644 ${TEST_SCRATCH_DIR}/chroot/hostname
mount --bind ${TEST_SCRATCH_DIR}/chroot/hostname ${TEST_SCRATCH_DIR}/chroot/merged/etc/hostname
touch ${TEST_SCRATCH_DIR}/chroot/hosts
chmod 644 ${TEST_SCRATCH_DIR}/chroot/hosts
mount --bind ${TEST_SCRATCH_DIR}/chroot/hosts ${TEST_SCRATCH_DIR}/chroot/merged/etc/hosts
touch ${TEST_SCRATCH_DIR}/chroot/resolv.conf
chmod 644 ${TEST_SCRATCH_DIR}/chroot/resolv.conf
mount --bind ${TEST_SCRATCH_DIR}/chroot/resolv.conf ${TEST_SCRATCH_DIR}/chroot/merged/etc/resolv.conf
mount --bind /tmp ${TEST_SCRATCH_DIR}/chroot/merged/tmp
mkdir -p ${TEST_SCRATCH_DIR}/chroot/merged/var/tmp
chmod 1777 ${TEST_SCRATCH_DIR}/chroot/merged/var/tmp
if test -d /var/tmp; then
mount --bind /var/tmp ${TEST_SCRATCH_DIR}/chroot/merged/var/tmp
fi
mount --bind ${TEST_SCRATCH_DIR} ${TEST_SCRATCH_DIR}/chroot/merged/${TEST_SCRATCH_DIR}
mkdir -p ${TEST_SCRATCH_DIR}/chroot/merged/usr/local/bin
touch ${TEST_SCRATCH_DIR}/chroot/merged/usr/local/bin/buildah
mount --bind ${BUILDAH_BINARY:-$TEST_SOURCES/../bin/buildah} ${TEST_SCRATCH_DIR}/chroot/merged/usr/local/bin/buildah
cd ${TEST_SCRATCH_DIR}/chroot/merged
pivot_root . tmp
mount --make-rslave tmp
umount -f -l tmp
mount -o remount,ro --make-rshared /
grep ' / / ' /proc/self/mountinfo
# unshare from util-linux 2.39 also accepts INNER:OUTER:SIZE for --map-users
# and --map-groups, but fedora 37's is too old, so the older OUTER,INNER,SIZE
# (using commas instead of colons as field separators) will have to do
unshare --setuid 0 --setgid 0 --map-users=1,0,1024 --map-groups=1,0,1024 -UinCfpm bash ${TEST_SCRATCH_DIR}/script2
EOF
cat > ${TEST_SCRATCH_DIR}/script2 <<- EOF
set -e
set -x
export _CONTAINERS_USERNS_CONFIGURED=done
export CONTAINERS_CONF=${TEST_SCRATCH_DIR}/containers.conf
cat /proc/self/uid_map
cat /proc/self/gid_map
mount --make-shared /
/usr/local/bin/buildah ${BUILDAH_REGISTRY_OPTS} ${ROOTDIR_OPTS} from --name ctrid --pull=never --quiet docker.io/library/busybox
/usr/local/bin/buildah ${BUILDAH_REGISTRY_OPTS} ${ROOTDIR_OPTS} run --isolation=chroot ctrid pwd
EOF
chmod +x ${TEST_SCRATCH_DIR}
chmod +rx ${TEST_SCRATCH_DIR}/script1 ${TEST_SCRATCH_DIR}/script2
env -i unshare -inCfpm bash ${TEST_SCRATCH_DIR}/script1
}
|