]> git.ipfire.org Git - thirdparty/systemd.git/blame - test/units/testsuite-13.nspawn.sh
nspawn: add support for owneridmap bind option
[thirdparty/systemd.git] / test / units / testsuite-13.nspawn.sh
CommitLineData
378db9e2
FS
1#!/usr/bin/env bash
2# SPDX-License-Identifier: LGPL-2.1-or-later
3# shellcheck disable=SC2016
28ed2326
FS
4#
5# Notes on coverage: when collecting coverage we need the $BUILD_DIR present
6# and writable in the container as well. To do this in the least intrusive way,
7# two things are going on in the background (only when built with -Db_coverage=true):
8# 1) the systemd-nspawn@.service is copied to /etc/systemd/system/ with
9# --bind=$BUILD_DIR appended to the ExecStart= line
10# 2) each create_dummy_container() call also creates an .nspawn file in /run/systemd/nspawn/
11# with the last fragment from the path used as a name
12#
13# The first change is quite self-contained and applies only to containers run
14# with machinectl. The second one might cause some unexpected side-effects, namely:
15# - nspawn config (setting) files don't support dropins, so tests that test
16# the config files might need some tweaking (as seen below with
17# the $COVERAGE_BUILD_DIR shenanigans) since they overwrite the .nspawn file
18# - also a note - if /etc/systemd/nspawn/cont-name.nspawn exists, it takes
19# precedence and /run/systemd/nspawn/cont-name.nspawn won't be read even
20# if it exists
928733cd 21# - also a note 2 - --bind= overrides any Bind= from a config file
28ed2326
FS
22# - in some cases we don't create a test container using create_dummy_container(),
23# so in that case an explicit call to coverage_create_nspawn_dropin() is needed
928733cd
FS
24#
25# However, even after jumping through all these hooks, there still might (and is)
26# some "incorrectly" missing coverage, especially in the window between spawning
27# the inner child process and bind-mounting the coverage $BUILD_DIR
378db9e2
FS
28set -eux
29set -o pipefail
30
b60d910d
FS
31# shellcheck source=test/units/test-control.sh
32. "$(dirname "$0")"/test-control.sh
5656759d
FS
33# shellcheck source=test/units/util.sh
34. "$(dirname "$0")"/util.sh
35
b60d910d 36
378db9e2
FS
37export SYSTEMD_LOG_LEVEL=debug
38export SYSTEMD_LOG_TARGET=journal
378db9e2
FS
39
40at_exit() {
41 set +e
42
d4317fe1 43 mountpoint -q /var/lib/machines && umount --recursive /var/lib/machines
28ed2326 44 rm -f /run/systemd/nspawn/*.nspawn
378db9e2
FS
45}
46
47trap at_exit EXIT
48
49# check cgroup-v2
50IS_CGROUPSV2_SUPPORTED=no
51mkdir -p /tmp/cgroup2
52if mount -t cgroup2 cgroup2 /tmp/cgroup2; then
53 IS_CGROUPSV2_SUPPORTED=yes
54 umount /tmp/cgroup2
55fi
56rmdir /tmp/cgroup2
57
58# check cgroup namespaces
59IS_CGNS_SUPPORTED=no
60if [[ -f /proc/1/ns/cgroup ]]; then
61 IS_CGNS_SUPPORTED=yes
62fi
63
64IS_USERNS_SUPPORTED=no
65# On some systems (e.g. CentOS 7) the default limit for user namespaces
66# is set to 0, which causes the following unshare syscall to fail, even
67# with enabled user namespaces support. By setting this value explicitly
68# we can ensure the user namespaces support to be detected correctly.
69sysctl -w user.max_user_namespaces=10000
5656759d 70if unshare -U bash -c :; then
378db9e2
FS
71 IS_USERNS_SUPPORTED=yes
72fi
73
74# Mount tmpfs over /var/lib/machines to not pollute the image
75mkdir -p /var/lib/machines
76mount -t tmpfs tmpfs /var/lib/machines
77
cf260f79 78testcase_sanity() {
0d5896a9 79 local template root image uuid tmpdir
70376640 80
806b1824 81 tmpdir="$(mktemp -d)"
70376640 82 template="$(mktemp -d /tmp/nspawn-template.XXX)"
5656759d 83 create_dummy_container "$template"
70376640
FS
84 # Create a simple image from the just created container template
85 image="$(mktemp /var/lib/machines/testsuite-13.image-XXX.img)"
256c1ac9 86 dd if=/dev/zero of="$image" bs=1M count=64
70376640
FS
87 mkfs.ext4 "$image"
88 mkdir -p /mnt
89 mount -o loop "$image" /mnt
90 cp -r "$template"/* /mnt/
91 umount /mnt
70376640
FS
92
93 systemd-nspawn --help --no-pager
94 systemd-nspawn --version
95
96 # --template=
97 root="$(mktemp -u -d /var/lib/machines/testsuite-13.sanity.XXX)"
28ed2326 98 coverage_create_nspawn_dropin "$root"
5656759d 99 (! systemd-nspawn --directory="$root" bash -xec 'echo hello')
70376640
FS
100 # Initialize $root from $template (the $root directory must not exist, hence
101 # the `mktemp -u` above)
5656759d
FS
102 systemd-nspawn --directory="$root" --template="$template" bash -xec 'echo hello'
103 systemd-nspawn --directory="$root" bash -xec 'echo hello; touch /initialized'
70376640
FS
104 test -e "$root/initialized"
105 # Check if the $root doesn't get re-initialized once it's not empty
5656759d 106 systemd-nspawn --directory="$root" --template="$template" bash -xec 'echo hello'
70376640
FS
107 test -e "$root/initialized"
108
5656759d 109 systemd-nspawn --directory="$root" --ephemeral bash -xec 'touch /ephemeral'
70376640 110 test ! -e "$root/ephemeral"
806b1824 111 (! systemd-nspawn --directory="$root" \
806b1824 112 --read-only \
5656759d 113 bash -xec 'touch /nope')
70376640 114 test ! -e "$root/nope"
5656759d 115 systemd-nspawn --image="$image" bash -xec 'echo hello'
70376640
FS
116
117 # --volatile=
118 touch "$root/usr/has-usr"
119 # volatile(=yes): rootfs is tmpfs, /usr/ from the OS tree is mounted read only
120 systemd-nspawn --directory="$root"\
121 --volatile \
5656759d 122 bash -xec 'test -e /usr/has-usr; touch /usr/read-only && exit 1; touch /nope'
70376640
FS
123 test ! -e "$root/nope"
124 test ! -e "$root/usr/read-only"
125 systemd-nspawn --directory="$root"\
126 --volatile=yes \
5656759d 127 bash -xec 'test -e /usr/has-usr; touch /usr/read-only && exit 1; touch /nope'
70376640
FS
128 test ! -e "$root/nope"
129 test ! -e "$root/usr/read-only"
130 # volatile=state: rootfs is read-only, /var/ is tmpfs
131 systemd-nspawn --directory="$root" \
132 --volatile=state \
5656759d 133 bash -xec 'test -e /usr/has-usr; mountpoint /var; touch /read-only && exit 1; touch /var/nope'
70376640
FS
134 test ! -e "$root/read-only"
135 test ! -e "$root/var/nope"
136 # volatile=state: tmpfs overlay is mounted over rootfs
137 systemd-nspawn --directory="$root" \
138 --volatile=overlay \
5656759d 139 bash -xec 'test -e /usr/has-usr; touch /nope; touch /var/also-nope; touch /usr/nope-too'
70376640
FS
140 test ! -e "$root/nope"
141 test ! -e "$root/var/also-nope"
142 test ! -e "$root/usr/nope-too"
143
144 # --machine=, --hostname=
145 systemd-nspawn --directory="$root" \
146 --machine="foo-bar.baz" \
5656759d 147 bash -xec '[[ $(hostname) == foo-bar.baz ]]'
70376640
FS
148 systemd-nspawn --directory="$root" \
149 --hostname="hello.world.tld" \
5656759d 150 bash -xec '[[ $(hostname) == hello.world.tld ]]'
70376640
FS
151 systemd-nspawn --directory="$root" \
152 --machine="foo-bar.baz" \
153 --hostname="hello.world.tld" \
5656759d 154 bash -xec '[[ $(hostname) == hello.world.tld ]]'
70376640
FS
155
156 # --uuid=
157 rm -f "$root/etc/machine-id"
158 uuid="deadbeef-dead-dead-beef-000000000000"
159 systemd-nspawn --directory="$root" \
160 --uuid="$uuid" \
5656759d 161 bash -xec "[[ \$container_uuid == $uuid ]]"
70376640
FS
162
163 # --as-pid2
5656759d
FS
164 systemd-nspawn --directory="$root" bash -xec '[[ $$ -eq 1 ]]'
165 systemd-nspawn --directory="$root" --as-pid2 bash -xec '[[ $$ -eq 2 ]]'
70376640
FS
166
167 # --user=
5656759d
FS
168 # "Fake" getent passwd's bare minimum, so we don't have to pull it in
169 # with all the DSO shenanigans
170 cat >"$root/bin/getent" <<\EOF
171#!/bin/bash
172
173if [[ $# -eq 0 ]]; then
174 :
175elif [[ $1 == passwd ]]; then
176 echo "testuser:x:1000:1000:testuser:/:/bin/sh"
177elif [[ $1 == initgroups ]]; then
178 echo "testuser"
179fi
180EOF
181 chmod +x "$root/bin/getent"
182 systemd-nspawn --directory="$root" bash -xec '[[ $USER == root ]]'
183 systemd-nspawn --directory="$root" --user=testuser bash -xec '[[ $USER == testuser ]]'
70376640
FS
184
185 # --settings= + .nspawn files
186 mkdir -p /run/systemd/nspawn/
187 uuid="deadbeef-dead-dead-beef-000000000000"
188 echo -ne "[Exec]\nMachineID=deadbeef-dead-dead-beef-111111111111" >/run/systemd/nspawn/foo-bar.nspawn
189 systemd-nspawn --directory="$root" \
190 --machine=foo-bar \
191 --settings=yes \
5656759d 192 bash -xec '[[ $container_uuid == deadbeef-dead-dead-beef-111111111111 ]]'
70376640
FS
193 systemd-nspawn --directory="$root" \
194 --machine=foo-bar \
195 --uuid="$uuid" \
196 --settings=yes \
5656759d 197 bash -xec "[[ \$container_uuid == $uuid ]]"
70376640
FS
198 systemd-nspawn --directory="$root" \
199 --machine=foo-bar \
200 --uuid="$uuid" \
201 --settings=override \
5656759d 202 bash -xec '[[ $container_uuid == deadbeef-dead-dead-beef-111111111111 ]]'
70376640
FS
203 systemd-nspawn --directory="$root" \
204 --machine=foo-bar \
205 --uuid="$uuid" \
206 --settings=trusted \
5656759d 207 bash -xec "[[ \$container_uuid == $uuid ]]"
70376640
FS
208
209 # Mounts
70376640
FS
210 mkdir "$tmpdir"/{1,2,3}
211 touch "$tmpdir/1/one" "$tmpdir/2/two" "$tmpdir/3/three"
212 touch "$tmpdir/foo"
213 # --bind=
214 systemd-nspawn --directory="$root" \
928733cd 215 ${COVERAGE_BUILD_DIR:+--bind="$COVERAGE_BUILD_DIR"} \
70376640 216 --bind="$tmpdir:/foo" \
928733cd
FS
217 --bind="$tmpdir:/also-foo:noidmap,norbind" \
218 bash -xec 'test -e /foo/foo; touch /foo/bar; test -e /also-foo/bar'
70376640
FS
219 test -e "$tmpdir/bar"
220 # --bind-ro=
221 systemd-nspawn --directory="$root" \
222 --bind-ro="$tmpdir:/foo" \
928733cd
FS
223 --bind-ro="$tmpdir:/bar:noidmap,norbind" \
224 bash -xec 'test -e /foo/foo; touch /foo/baz && exit 1; touch /bar && exit 1; true'
70376640
FS
225 # --inaccessible=
226 systemd-nspawn --directory="$root" \
227 --inaccessible=/var \
5656759d 228 bash -xec 'touch /var/foo && exit 1; true'
70376640
FS
229 # --tmpfs=
230 systemd-nspawn --directory="$root" \
231 --tmpfs=/var:rw,nosuid,noexec \
5656759d 232 bash -xec 'touch /var/nope'
70376640
FS
233 test ! -e "$root/var/nope"
234 # --overlay=
235 systemd-nspawn --directory="$root" \
236 --overlay="$tmpdir/1:$tmpdir/2:$tmpdir/3:/var" \
5656759d 237 bash -xec 'test -e /var/one; test -e /var/two; test -e /var/three; touch /var/foo'
70376640
FS
238 test -e "$tmpdir/3/foo"
239 # --overlay-ro=
240 systemd-nspawn --directory="$root" \
241 --overlay-ro="$tmpdir/1:$tmpdir/2:$tmpdir/3:/var" \
5656759d 242 bash -xec 'test -e /var/one; test -e /var/two; test -e /var/three; touch /var/nope && exit 1; true'
70376640
FS
243 test ! -e "$tmpdir/3/nope"
244 rm -fr "$tmpdir"
245
74696ba5
FS
246 # --port (sanity only)
247 systemd-nspawn --network-veth --directory="$root" --port=80 --port=90 true
248 systemd-nspawn --network-veth --directory="$root" --port=80:8080 true
249 systemd-nspawn --network-veth --directory="$root" --port=tcp:80 true
250 systemd-nspawn --network-veth --directory="$root" --port=tcp:80:8080 true
251 systemd-nspawn --network-veth --directory="$root" --port=udp:80 true
252 systemd-nspawn --network-veth --directory="$root" --port=udp:80:8080 --port=tcp:80:8080 true
253 (! systemd-nspawn --network-veth --directory="$root" --port= true)
254 (! systemd-nspawn --network-veth --directory="$root" --port=-1 true)
255 (! systemd-nspawn --network-veth --directory="$root" --port=: true)
256 (! systemd-nspawn --network-veth --directory="$root" --port=icmp:80:8080 true)
257 (! systemd-nspawn --network-veth --directory="$root" --port=tcp::8080 true)
258 (! systemd-nspawn --network-veth --directory="$root" --port=8080: true)
928733cd
FS
259 # Exercise adding/removing ports from an interface
260 systemd-nspawn --directory="$root" \
261 --network-veth \
262 --port=6667 \
263 --port=80:8080 \
264 --port=udp:53 \
265 --port=tcp:22:2222 \
266 bash -xec 'ip addr add dev host0 10.0.0.10/24; ip a; ip addr del dev host0 10.0.0.10/24'
267
268 # --load-credential=, --set-credential=
269 echo "foo bar" >/tmp/cred.path
270 systemd-nspawn --directory="$root" \
271 --load-credential=cred.path:/tmp/cred.path \
272 --set-credential="cred.set:hello world" \
273 bash -xec '[[ "$(</run/host/credentials/cred.path)" == "foo bar" ]]; [[ "$(</run/host/credentials/cred.set)" == "hello world" ]]'
274 rm -f /tmp/cred.path
74696ba5 275
70376640 276 # Assorted tests
5656759d 277 systemd-nspawn --directory="$root" --suppress-sync=yes bash -xec 'echo hello'
70376640
FS
278 systemd-nspawn --capability=help
279 systemd-nspawn --resolv-conf=help
280 systemd-nspawn --timezone=help
281
282 # Handling of invalid arguments
283 opts=(
284 bind
285 bind-ro
286 bind-user
287 chdir
288 console
289 inaccessible
290 kill-signal
291 link-journal
292 load-credential
293 network-{interface,macvlan,ipvlan,veth-extra,bridge,zone}
294 no-new-privileges
295 oom-score-adjust
296 overlay
297 overlay-ro
298 personality
299 pivot-root
300 port
301 private-users
302 private-users-ownership
303 register
304 resolv-conf
305 rlimit
306 root-hash
307 root-hash-sig
308 set-credential
309 settings
310 suppress-sync
311 timezone
312 tmpfs
313 uuid
314 )
315 for opt in "${opts[@]}"; do
316 (! systemd-nspawn "--$opt")
317 [[ "$opt" == network-zone ]] && continue
318 (! systemd-nspawn "--$opt=''")
319 (! systemd-nspawn "--$opt=%\$Å¡")
320 done
321 (! systemd-nspawn --volatile="")
322 (! systemd-nspawn --volatile=-1)
323 (! systemd-nspawn --rlimit==)
324}
325
cbd20ab4 326nspawn_settings_cleanup() {
e8736fb6 327 for dev in sd-host-only sd-shared{1,2,3} sd-macvlan{1,2} sd-ipvlan{1,2}; do
cbd20ab4
FS
328 ip link del "$dev" || :
329 done
330
331 return 0
332}
333
334testcase_nspawn_settings() {
d2e8dc78 335 local root container dev private_users wlan_names
cbd20ab4
FS
336
337 mkdir -p /run/systemd/nspawn
338 root="$(mktemp -d /var/lib/machines/testsuite-13.nspawn-settings.XXX)"
339 container="$(basename "$root")"
340 create_dummy_container "$root"
341 rm -f "/etc/systemd/nspawn/$container.nspawn"
342 mkdir -p "$root/tmp" "$root"/opt/{tmp,inaccessible,also-inaccessible}
343
e8736fb6
YW
344 # add virtual wlan interfaces
345 if modprobe mac80211_hwsim radios=2; then
d2e8dc78 346 wlan_names="wlan0 wlan1:wl-renamed1"
e8736fb6
YW
347 fi
348
349 for dev in sd-host-only sd-shared{1,2,3} sd-macvlan{1,2} sd-macvlanloong sd-ipvlan{1,2} sd-ipvlanlooong; do
cbd20ab4
FS
350 ip link add "$dev" type dummy
351 done
352 udevadm settle
e8736fb6 353 ip link property add dev sd-shared3 altname sd-altname3 altname sd-altname-tooooooooooooo-long
cbd20ab4
FS
354 ip link
355 trap nspawn_settings_cleanup RETURN
356
357 # Let's start with one huge config to test as much as we can at once
358 cat >"/run/systemd/nspawn/$container.nspawn" <<EOF
359[Exec]
360Boot=no
361Ephemeral=no
362ProcessTwo=no
363Parameters=bash /entrypoint.sh "foo bar" 'bar baz'
364Environment=FOO=bar
365Environment=BAZ="hello world"
366User=root
367WorkingDirectory=/tmp
368Capability=CAP_BLOCK_SUSPEND CAP_BPF CAP_CHOWN
369DropCapability=CAP_AUDIT_CONTROL CAP_AUDIT_WRITE
370AmbientCapability=CAP_BPF CAP_CHOWN
371NoNewPrivileges=no
372MachineID=f28f129b51874b1280a89421ec4b4ad4
373PrivateUsers=no
374NotifyReady=no
375SystemCallFilter=@basic-io @chown
376SystemCallFilter=~ @clock
377LimitNOFILE=1024:2048
378LimitRTPRIO=8:16
379OOMScoreAdjust=32
380CPUAffinity=0,0-5,1-5
381Hostname=nspawn-settings
382ResolvConf=copy-host
383Timezone=delete
384LinkJournal=no
385SuppressSync=no
386
387[Files]
388ReadOnly=no
389Volatile=no
390TemporaryFileSystem=/tmp
391TemporaryFileSystem=/opt/tmp
392Inaccessible=/opt/inaccessible
393Inaccessible=/opt/also-inaccessible
394PrivateUsersOwnership=auto
395Overlay=+/var::/var
396${COVERAGE_BUILD_DIR:+"Bind=$COVERAGE_BUILD_DIR"}
397
398[Network]
399Private=yes
400VirtualEthernet=yes
401VirtualEthernetExtra=my-fancy-veth1
402VirtualEthernetExtra=fancy-veth2:my-fancy-veth2
d2e8dc78 403Interface=sd-shared1 sd-shared2:sd-renamed2 sd-shared3:sd-altname3 ${wlan_names:-}
927e20fa
YW
404MACVLAN=sd-macvlan1 sd-macvlan2:my-macvlan2 sd-macvlanloong
405IPVLAN=sd-ipvlan1 sd-ipvlan2:my-ipvlan2 sd-ipvlanlooong
cbd20ab4
FS
406Zone=sd-zone0
407Port=80
408Port=81:8181
409Port=tcp:60
410Port=udp:60:61
411EOF
412 cat >"$root/entrypoint.sh" <<\EOF
7b1c2929
FS
413#!/bin/bash
414set -ex
cbd20ab4 415
8ee32f68
FS
416env
417
cbd20ab4
FS
418[[ "$1" == "foo bar" ]]
419[[ "$2" == "bar baz" ]]
420
421[[ "$USER" == root ]]
422[[ "$FOO" == bar ]]
423[[ "$BAZ" == "hello world" ]]
424[[ "$PWD" == /tmp ]]
8ee32f68 425[[ "$container_uuid" == f28f129b-5187-4b12-80a8-9421ec4b4ad4 ]]
cbd20ab4
FS
426[[ "$(ulimit -S -n)" -eq 1024 ]]
427[[ "$(ulimit -H -n)" -eq 2048 ]]
428[[ "$(ulimit -S -r)" -eq 8 ]]
429[[ "$(ulimit -H -r)" -eq 16 ]]
430[[ "$(</proc/self/oom_score_adj)" -eq 32 ]]
431[[ "$(hostname)" == nspawn-settings ]]
432[[ -e /etc/resolv.conf ]]
433[[ ! -e /etc/localtime ]]
434
435mountpoint /tmp
436touch /tmp/foo
437mountpoint /opt/tmp
438touch /opt/tmp/foo
439touch /opt/inaccessible/foo && exit 1
440touch /opt/also-inaccessible/foo && exit 1
441mountpoint /var
442
443ip link
444ip link | grep host-only && exit 1
445ip link | grep host0@
446ip link | grep my-fancy-veth1@
447ip link | grep my-fancy-veth2@
448ip link | grep sd-shared1
e8736fb6
YW
449ip link | grep sd-renamed2
450ip link | grep sd-shared3
451ip link | grep sd-altname3
452ip link | grep sd-altname-tooooooooooooo-long
cbd20ab4
FS
453ip link | grep mv-sd-macvlan1@
454ip link | grep my-macvlan2@
455ip link | grep iv-sd-ipvlan1@
456ip link | grep my-ipvlan2@
457EOF
d2e8dc78
FS
458 if [[ -n "${wlan_names:-}" ]]; then
459 cat >>"$root/entrypoint.sh" <<\EOF
460ip link | grep wlan0
461ip link | grep wl-renamed1
462EOF
463 fi
e8736fb6 464
cbd20ab4
FS
465 timeout 30 systemd-nspawn --directory="$root"
466
467 # And now for stuff that needs to run separately
468 #
469 # Note on the condition below: since our container tree is owned by root,
470 # both "yes" and "identity" private users settings will behave the same
471 # as PrivateUsers=0:65535, which makes BindUser= fail as the UID already
472 # exists there, so skip setting it in such case
473 for private_users in "131072:65536" yes identity pick; do
474 cat >"/run/systemd/nspawn/$container.nspawn" <<EOF
475[Exec]
476Hostname=private-users
477PrivateUsers=$private_users
478
479[Files]
480PrivateUsersOwnership=auto
481BindUser=
482$([[ "$private_users" =~ (yes|identity) ]] || echo "BindUser=testuser")
483${COVERAGE_BUILD_DIR:+"Bind=$COVERAGE_BUILD_DIR"}
484EOF
485 cat "/run/systemd/nspawn/$container.nspawn"
486 chown -R root:root "$root"
487 systemd-nspawn --directory="$root" bash -xec '[[ "$(hostname)" == private-users ]]'
488 done
489
490 rm -fr "$root" "/run/systemd/nspawn/$container.nspawn"
491}
492
d5a6ff8c
FS
493bind_user_cleanup() {
494 userdel --force --remove nspawn-bind-user-1
495 userdel --force --remove nspawn-bind-user-2
496}
497
498testcase_bind_user() {
499 local root
500
501 root="$(mktemp -d /var/lib/machines/testsuite-13.bind-user.XXX)"
502 create_dummy_container "$root"
503 useradd --create-home --user-group nspawn-bind-user-1
504 useradd --create-home --user-group nspawn-bind-user-2
505 trap bind_user_cleanup RETURN
506 touch /home/nspawn-bind-user-1/foo
507 touch /home/nspawn-bind-user-2/bar
508 # Add a couple of POSIX ACLs to test the patch-uid stuff
509 mkdir -p "$root/opt"
510 setfacl -R -m 'd:u:nspawn-bind-user-1:rwX' -m 'u:nspawn-bind-user-1:rwX' "$root/opt"
511 setfacl -R -m 'd:g:nspawn-bind-user-1:rwX' -m 'g:nspawn-bind-user-1:rwX' "$root/opt"
512
513 systemd-nspawn --directory="$root" \
514 --private-users=pick \
515 --bind-user=nspawn-bind-user-1 \
516 bash -xec 'test -e /run/host/home/nspawn-bind-user-1/foo'
517
518 systemd-nspawn --directory="$root" \
519 --private-users=pick \
520 --private-users-ownership=chown \
521 --bind-user=nspawn-bind-user-1 \
522 --bind-user=nspawn-bind-user-2 \
523 bash -xec 'test -e /run/host/home/nspawn-bind-user-1/foo; test -e /run/host/home/nspawn-bind-user-2/bar'
524 chown -R root:root "$root"
525
526 # User/group name collision
527 echo "nspawn-bind-user-2:x:1000:1000:nspawn-bind-user-2:/home/nspawn-bind-user-2:/bin/bash" >"$root/etc/passwd"
528 (! systemd-nspawn --directory="$root" \
529 --private-users=pick \
530 --bind-user=nspawn-bind-user-1 \
531 --bind-user=nspawn-bind-user-2 \
532 true)
533 rm -f "$root/etc/passwd"
534
535 echo "nspawn-bind-user-2:x:1000:" >"$root/etc/group"
536 (! systemd-nspawn --directory="$root" \
537 --private-users=pick \
538 --bind-user=nspawn-bind-user-1 \
539 --bind-user=nspawn-bind-user-2 \
540 true)
541 rm -f "$root/etc/group"
542
543 rm -fr "$root"
544}
545
cf260f79 546testcase_bind_tmp_path() {
378db9e2
FS
547 # https://github.com/systemd/systemd/issues/4789
548 local root
549
550 root="$(mktemp -d /var/lib/machines/testsuite-13.bind-tmp-path.XXX)"
5656759d 551 create_dummy_container "$root"
378db9e2
FS
552 : >/tmp/bind
553 systemd-nspawn --register=no \
554 --directory="$root" \
555 --bind=/tmp/bind \
5656759d 556 bash -c 'test -e /tmp/bind'
378db9e2
FS
557
558 rm -fr "$root" /tmp/bind
559}
560
cf260f79 561testcase_norbind() {
378db9e2
FS
562 # https://github.com/systemd/systemd/issues/13170
563 local root
564
565 root="$(mktemp -d /var/lib/machines/testsuite-13.norbind-path.XXX)"
566 mkdir -p /tmp/binddir/subdir
567 echo -n "outer" >/tmp/binddir/subdir/file
568 mount -t tmpfs tmpfs /tmp/binddir/subdir
569 echo -n "inner" >/tmp/binddir/subdir/file
5656759d 570 create_dummy_container "$root"
378db9e2
FS
571
572 systemd-nspawn --register=no \
573 --directory="$root" \
574 --bind=/tmp/binddir:/mnt:norbind \
5656759d 575 bash -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; exit 1; fi'
378db9e2
FS
576
577 umount /tmp/binddir/subdir
578 rm -fr "$root" /tmp/binddir/
579}
580
cf260f79 581rootidmap_cleanup() {
378db9e2
FS
582 local dir="${1:?}"
583
584 mountpoint -q "$dir/bind" && umount "$dir/bind"
585 rm -fr "$dir"
586}
587
cf260f79 588testcase_rootidmap() {
378db9e2
FS
589 local root cmd permissions
590 local owner=1000
591
592 root="$(mktemp -d /var/lib/machines/testsuite-13.rootidmap-path.XXX)"
593 # Create ext4 image, as ext4 supports idmapped-mounts.
594 mkdir -p /tmp/rootidmap/bind
595 dd if=/dev/zero of=/tmp/rootidmap/ext4.img bs=4k count=2048
596 mkfs.ext4 /tmp/rootidmap/ext4.img
597 mount /tmp/rootidmap/ext4.img /tmp/rootidmap/bind
cf260f79 598 trap "rootidmap_cleanup /tmp/rootidmap/" RETURN
378db9e2
FS
599
600 touch /tmp/rootidmap/bind/file
601 chown -R "$owner:$owner" /tmp/rootidmap/bind
602
5656759d 603 create_dummy_container "$root"
378db9e2
FS
604 cmd='PERMISSIONS=$(stat -c "%u:%g" /mnt/file); if [[ $PERMISSIONS != "0:0" ]]; then echo "*** wrong permissions: $PERMISSIONS"; return 1; fi; touch /mnt/other_file'
605 if ! SYSTEMD_LOG_TARGET=console \
606 systemd-nspawn --register=no \
607 --directory="$root" \
608 --bind=/tmp/rootidmap/bind:/mnt:rootidmap \
5656759d 609 bash -c "$cmd" |& tee nspawn.out; then
378db9e2
FS
610 if grep -q "Failed to map ids for bind mount.*: Function not implemented" nspawn.out; then
611 echo "idmapped mounts are not supported, skipping the test..."
612 return 0
613 fi
614
615 return 1
616 fi
617
618 permissions=$(stat -c "%u:%g" /tmp/rootidmap/bind/other_file)
619 if [[ $permissions != "$owner:$owner" ]]; then
614d09a3
BF
620 echo "*** wrong permissions: $permissions"
621 [[ "$IS_USERNS_SUPPORTED" == "yes" ]] && return 1
622 fi
623}
624
625owneridmap_cleanup() {
626 local dir="${1:?}"
627
628 mountpoint -q "$dir/bind" && umount "$dir/bind"
629 rm -fr "$dir"
630}
631
632testcase_owneridmap() {
633 local root cmd permissions
634 local owner=1000
635
636 root="$(mktemp -d /var/lib/machines/testsuite-13.owneridmap-path.XXX)"
637 # Create ext4 image, as ext4 supports idmapped-mounts.
638 mkdir -p /tmp/owneridmap/bind
639 dd if=/dev/zero of=/tmp/owneridmap/ext4.img bs=4k count=2048
640 mkfs.ext4 /tmp/owneridmap/ext4.img
641 mount /tmp/owneridmap/ext4.img /tmp/owneridmap/bind
642 trap "owneridmap_cleanup /tmp/owneridmap/" RETURN
643
644 touch /tmp/owneridmap/bind/file
645 chown -R "$owner:$owner" /tmp/owneridmap/bind
646
647 # Allow users to read and execute / in order to execute binaries
648 chmod o+rx "$root"
649
650 create_dummy_container "$root"
651
652 # --user=
653 # "Fake" getent passwd's bare minimum, so we don't have to pull it in
654 # with all the DSO shenanigans
655 cat >"$root/bin/getent" <<\EOF
656#!/bin/bash
657
658if [[ $# -eq 0 ]]; then
659 :
660elif [[ $1 == passwd ]]; then
661 echo "testuser:x:1010:1010:testuser:/:/bin/sh"
662elif [[ $1 == initgroups ]]; then
663 echo "testuser"
664fi
665EOF
666 chmod +x "$root/bin/getent"
667
668 mkdir -p "$root/home/testuser"
669 chown 1010:1010 "$root/home/testuser"
670
671 cmd='PERMISSIONS=$(stat -c "%u:%g" /home/testuser/file); if [[ $PERMISSIONS != "1010:1010" ]]; then echo "*** wrong permissions: $PERMISSIONS"; return 1; fi; touch /home/testuser/other_file'
672 if ! SYSTEMD_LOG_TARGET=console \
673 systemd-nspawn --register=no \
674 --directory="$root" \
675 -U \
676 --user=testuser \
677 --bind=/tmp/owneridmap/bind:/home/testuser:owneridmap \
678 /usr/bin/bash -c "$cmd" |& tee nspawn.out; then
679 if grep -q "Failed to map ids for bind mount.*: Function not implemented" nspawn.out; then
680 echo "idmapped mounts are not supported, skipping the test..."
681 return 0
682 fi
683
684 return 1
685 fi
686
687 permissions=$(stat -c "%u:%g" /tmp/owneridmap/bind/other_file)
688 if [[ $permissions != "$owner:$owner" ]]; then
378db9e2
FS
689 echo "*** wrong permissions: $permissions"
690 [[ "$IS_USERNS_SUPPORTED" == "yes" ]] && return 1
691 fi
692}
693
cf260f79 694testcase_notification_socket() {
378db9e2 695 # https://github.com/systemd/systemd/issues/4944
5656759d
FS
696 local root
697 local cmd='echo a | nc -U -u -w 1 /run/host/notify'
698
699 root="$(mktemp -d /var/lib/machines/testsuite-13.check_notification_socket.XXX)"
700 create_dummy_container "$root"
378db9e2 701
5656759d
FS
702 systemd-nspawn --register=no --directory="$root" bash -x -c "$cmd"
703 systemd-nspawn --register=no --directory="$root" -U bash -x -c "$cmd"
cbd20ab4
FS
704
705 rm -fr "$root"
378db9e2
FS
706}
707
cf260f79 708testcase_os_release() {
378db9e2
FS
709 local root entrypoint os_release_source
710
cf260f79 711 root="$(mktemp -d /var/lib/machines/testsuite-13.os-release.XXX)"
5656759d 712 create_dummy_container "$root"
378db9e2
FS
713 entrypoint="$root/entrypoint.sh"
714 cat >"$entrypoint" <<\EOF
5656759d 715#!/usr/bin/bash -ex
378db9e2
FS
716
717. /tmp/os-release
718[[ -n "${ID:-}" && "$ID" != "$container_host_id" ]] && exit 1
719[[ -n "${VERSION_ID:-}" && "$VERSION_ID" != "$container_host_version_id" ]] && exit 1
720[[ -n "${BUILD_ID:-}" && "$BUILD_ID" != "$container_host_build_id" ]] && exit 1
721[[ -n "${VARIANT_ID:-}" && "$VARIANT_ID" != "$container_host_variant_id" ]] && exit 1
722
723cd /tmp
724(cd /run/host && md5sum os-release) | md5sum -c
725EOF
726 chmod +x "$entrypoint"
727
728 os_release_source="/etc/os-release"
729 if [[ ! -r "$os_release_source" ]]; then
730 os_release_source="/usr/lib/os-release"
731 elif [[ -L "$os_release_source" ]]; then
732 # Ensure that /etc always wins if available
733 cp --remove-destination -fv /usr/lib/os-release /etc/os-release
734 echo MARKER=1 >>/etc/os-release
735 fi
736
737 systemd-nspawn --register=no \
738 --directory="$root" \
739 --bind="$os_release_source:/tmp/os-release" \
740 "${entrypoint##"$root"}"
741
742 if grep -q MARKER /etc/os-release; then
743 ln -svrf /usr/lib/os-release /etc/os-release
744 fi
745
746 rm -fr "$root"
747}
748
cf260f79 749testcase_machinectl_bind() {
378db9e2 750 local service_path service_name root container_name ec
5656759d 751 local cmd='for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; sleep .5; done; exit 1;'
378db9e2 752
cf260f79 753 root="$(mktemp -d /var/lib/machines/testsuite-13.machinectl-bind.XXX)"
5656759d
FS
754 create_dummy_container "$root"
755 container_name="$(basename "$root")"
378db9e2
FS
756
757 service_path="$(mktemp /run/systemd/system/nspawn-machinectl-bind-XXX.service)"
758 service_name="${service_path##*/}"
759 cat >"$service_path" <<EOF
760[Service]
761Type=notify
5656759d 762ExecStart=systemd-nspawn --directory="$root" --notify-ready=no /usr/bin/bash -xec "$cmd"
378db9e2
FS
763EOF
764
765 systemctl daemon-reload
766 systemctl start "$service_name"
767 touch /tmp/marker
768 machinectl bind --mkdir "$container_name" /tmp/marker
769
770 timeout 10 bash -c "while [[ '\$(systemctl show -P SubState $service_name)' == running ]]; do sleep .2; done"
771 ec="$(systemctl show -P ExecMainStatus "$service_name")"
5656759d 772 systemctl stop "$service_name"
378db9e2
FS
773
774 rm -fr "$root" "$service_path"
775
776 return "$ec"
777}
778
cf260f79 779testcase_selinux() {
378db9e2
FS
780 # Basic test coverage to avoid issues like https://github.com/systemd/systemd/issues/19976
781 if ! command -v selinuxenabled >/dev/null || ! selinuxenabled; then
782 echo >&2 "SELinux is not enabled, skipping SELinux-related tests"
783 return 0
784 fi
785
786 local root
787
cf260f79 788 root="$(mktemp -d /var/lib/machines/testsuite-13.selinux.XXX)"
5656759d 789 create_dummy_container "$root"
378db9e2
FS
790 chcon -R -t container_t "$root"
791
792 systemd-nspawn --register=no \
793 --boot \
794 --directory="$root" \
795 --selinux-apifs-context=system_u:object_r:container_file_t:s0:c0,c1 \
796 --selinux-context=system_u:system_r:container_t:s0:c0,c1
797
798 rm -fr "$root"
799}
800
cf260f79 801testcase_ephemeral_config() {
378db9e2
FS
802 # https://github.com/systemd/systemd/issues/13297
803 local root container_name
804
cf260f79 805 root="$(mktemp -d /var/lib/machines/testsuite-13.ephemeral-config.XXX)"
5656759d 806 create_dummy_container "$root"
cf260f79 807 container_name="$(basename "$root")"
378db9e2
FS
808
809 mkdir -p /run/systemd/nspawn/
28ed2326 810 rm -f "/etc/systemd/nspawn/$container_name.nspawn"
378db9e2
FS
811 cat >"/run/systemd/nspawn/$container_name.nspawn" <<EOF
812[Files]
28ed2326 813${COVERAGE_BUILD_DIR:+"Bind=$COVERAGE_BUILD_DIR"}
378db9e2
FS
814BindReadOnly=/tmp/ephemeral-config
815EOF
816 touch /tmp/ephemeral-config
817
818 systemd-nspawn --register=no \
819 --directory="$root" \
820 --ephemeral \
5656759d 821 bash -x -c "test -f /tmp/ephemeral-config"
378db9e2
FS
822
823 systemd-nspawn --register=no \
824 --directory="$root" \
825 --ephemeral \
826 --machine=foobar \
5656759d 827 bash -x -c "! test -f /tmp/ephemeral-config"
378db9e2 828
cbd20ab4 829 rm -fr "$root" "/run/systemd/nspawn/$container_name.nspawn"
378db9e2
FS
830}
831
832matrix_run_one() {
833 local cgroupsv2="${1:?}"
834 local use_cgns="${2:?}"
835 local api_vfs_writable="${3:?}"
836 local root
837
838 if [[ "$cgroupsv2" == "yes" && "$IS_CGROUPSV2_SUPPORTED" == "no" ]]; then
839 echo >&2 "Unified cgroup hierarchy is not supported, skipping..."
840 return 0
841 fi
842
843 if [[ "$use_cgns" == "yes" && "$IS_CGNS_SUPPORTED" == "no" ]]; then
844 echo >&2 "CGroup namespaces are not supported, skipping..."
845 return 0
846 fi
847
848 root="$(mktemp -d "/var/lib/machines/testsuite-13.unified-$1-cgns-$2-api-vfs-writable-$3.XXX")"
5656759d 849 create_dummy_container "$root"
378db9e2
FS
850
851 SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
852 systemd-nspawn --register=no \
853 --directory="$root" \
854 --boot
855
856 SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
857 systemd-nspawn --register=no \
858 --directory="$root" \
859 --private-network \
860 --boot
861
862 if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
863 systemd-nspawn --register=no \
864 --directory="$root" \
865 --private-users=pick \
866 --boot; then
867 [[ "$IS_USERNS_SUPPORTED" == "yes" && "$api_vfs_writable" == "network" ]] && return 1
868 else
869 [[ "$IS_USERNS_SUPPORTED" == "no" && "$api_vfs_writable" = "network" ]] && return 1
870 fi
871
872 if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
873 systemd-nspawn --register=no \
874 --directory="$root" \
875 --private-network \
876 --private-users=pick \
877 --boot; then
878 [[ "$IS_USERNS_SUPPORTED" == "yes" && "$api_vfs_writable" == "yes" ]] && return 1
879 else
880 [[ "$IS_USERNS_SUPPORTED" == "no" && "$api_vfs_writable" = "yes" ]] && return 1
881 fi
882
883 local netns_opt="--network-namespace-path=/proc/self/ns/net"
884 local net_opt
885 local net_opts=(
886 "--network-bridge=lo"
887 "--network-interface=lo"
888 "--network-ipvlan=lo"
889 "--network-macvlan=lo"
890 "--network-veth"
891 "--network-veth-extra=lo"
892 "--network-zone=zone"
893 )
894
895 # --network-namespace-path and network-related options cannot be used together
896 for net_opt in "${net_opts[@]}"; do
897 echo "$netns_opt in combination with $net_opt should fail"
898 if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
899 systemd-nspawn --register=no \
900 --directory="$root" \
901 --boot \
902 "$netns_opt" \
903 "$net_opt"; then
904 echo >&2 "unexpected pass"
905 return 1
906 fi
907 done
908
909 # allow combination of --network-namespace-path and --private-network
910 SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
911 systemd-nspawn --register=no \
912 --directory="$root" \
913 --boot \
914 --private-network \
915 "$netns_opt"
916
917 # test --network-namespace-path works with a network namespace created by "ip netns"
918 ip netns add nspawn_test
919 netns_opt="--network-namespace-path=/run/netns/nspawn_test"
920 SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \
921 systemd-nspawn --register=no \
922 --directory="$root" \
923 --network-namespace-path=/run/netns/nspawn_test \
5656759d 924 ip a | grep -v -E '^1: lo.*UP'
378db9e2
FS
925 ip netns del nspawn_test
926
927 rm -fr "$root"
928
929 return 0
930}
931
d4317fe1
FS
932testcase_check_os_release() {
933 # https://github.com/systemd/systemd/issues/29185
934 local base common_opts root
935
936 base="$(mktemp -d /var/lib/machines/testsuite-13.check_os_release_base.XXX)"
937 root="$(mktemp -d /var/lib/machines/testsuite-13.check_os_release.XXX)"
938 create_dummy_container "$base"
939 cp -d "$base"/{bin,sbin,lib,lib64} "$root/"
940 common_opts=(
941 --boot
942 --register=no
943 --directory="$root"
944 --bind-ro="$base/usr:/usr"
945 )
946
947 # Empty /etc/ & /usr/
948 (! systemd-nspawn "${common_opts[@]}")
949 (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=1 systemd-nspawn "${common_opts[@]}")
950 (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=foo systemd-nspawn "${common_opts[@]}")
951 SYSTEMD_NSPAWN_CHECK_OS_RELEASE=0 systemd-nspawn "${common_opts[@]}"
952
953 # Empty /usr/ + a broken /etc/os-release -> /usr/os-release symlink
954 ln -svrf "$root/etc/os-release" "$root/usr/os-release"
955 (! systemd-nspawn "${common_opts[@]}")
956 (! SYSTEMD_NSPAWN_CHECK_OS_RELEASE=1 systemd-nspawn "${common_opts[@]}")
957 SYSTEMD_NSPAWN_CHECK_OS_RELEASE=0 systemd-nspawn "${common_opts[@]}"
958
959 rm -fr "$root" "$base"
960}
961
b60d910d 962run_testcases
378db9e2
FS
963
964for api_vfs_writable in yes no network; do
965 matrix_run_one no no $api_vfs_writable
966 matrix_run_one yes no $api_vfs_writable
967 matrix_run_one no yes $api_vfs_writable
968 matrix_run_one yes yes $api_vfs_writable
969done