File: chroot.bats

package info (click to toggle)
golang-github-containers-buildah 1.41.4%2Bds1-2
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 8,148 kB
  • sloc: sh: 2,569; makefile: 241; perl: 187; asm: 16; awk: 12; ansic: 1
file content (184 lines) | stat: -rw-r--r-- 9,246 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
#!/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
}