]>
Commit | Line | Data |
---|---|---|
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 |
28 | set -eux |
29 | set -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 |
37 | export SYSTEMD_LOG_LEVEL=debug |
38 | export SYSTEMD_LOG_TARGET=journal | |
378db9e2 FS |
39 | |
40 | at_exit() { | |
41 | set +e | |
42 | ||
43 | mountpoint -q /var/lib/machines && umount /var/lib/machines | |
28ed2326 | 44 | rm -f /run/systemd/nspawn/*.nspawn |
378db9e2 FS |
45 | } |
46 | ||
47 | trap at_exit EXIT | |
48 | ||
49 | # check cgroup-v2 | |
50 | IS_CGROUPSV2_SUPPORTED=no | |
51 | mkdir -p /tmp/cgroup2 | |
52 | if mount -t cgroup2 cgroup2 /tmp/cgroup2; then | |
53 | IS_CGROUPSV2_SUPPORTED=yes | |
54 | umount /tmp/cgroup2 | |
55 | fi | |
56 | rmdir /tmp/cgroup2 | |
57 | ||
58 | # check cgroup namespaces | |
59 | IS_CGNS_SUPPORTED=no | |
60 | if [[ -f /proc/1/ns/cgroup ]]; then | |
61 | IS_CGNS_SUPPORTED=yes | |
62 | fi | |
63 | ||
64 | IS_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. | |
69 | sysctl -w user.max_user_namespaces=10000 | |
5656759d | 70 | if unshare -U bash -c :; then |
378db9e2 FS |
71 | IS_USERNS_SUPPORTED=yes |
72 | fi | |
73 | ||
74 | # Mount tmpfs over /var/lib/machines to not pollute the image | |
75 | mkdir -p /var/lib/machines | |
76 | mount -t tmpfs tmpfs /var/lib/machines | |
77 | ||
cf260f79 | 78 | testcase_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 | ||
173 | if [[ $# -eq 0 ]]; then | |
174 | : | |
175 | elif [[ $1 == passwd ]]; then | |
176 | echo "testuser:x:1000:1000:testuser:/:/bin/sh" | |
177 | elif [[ $1 == initgroups ]]; then | |
178 | echo "testuser" | |
179 | fi | |
180 | EOF | |
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 FS |
326 | nspawn_settings_cleanup() { |
327 | for dev in sd-host-only sd-shared{1,2} sd-macvlan{1,2} sd-ipvlan{1,2}; do | |
328 | ip link del "$dev" || : | |
329 | done | |
330 | ||
331 | return 0 | |
332 | } | |
333 | ||
334 | testcase_nspawn_settings() { | |
335 | local root container dev private_users | |
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 | ||
927e20fa | 344 | for dev in sd-host-only sd-shared{1,2} sd-macvlan{1,2} sd-macvlanloong sd-ipvlan{1,2} sd-ipvlanlooong; do |
cbd20ab4 FS |
345 | ip link add "$dev" type dummy |
346 | done | |
347 | udevadm settle | |
348 | ip link | |
349 | trap nspawn_settings_cleanup RETURN | |
350 | ||
351 | # Let's start with one huge config to test as much as we can at once | |
352 | cat >"/run/systemd/nspawn/$container.nspawn" <<EOF | |
353 | [Exec] | |
354 | Boot=no | |
355 | Ephemeral=no | |
356 | ProcessTwo=no | |
357 | Parameters=bash /entrypoint.sh "foo bar" 'bar baz' | |
358 | Environment=FOO=bar | |
359 | Environment=BAZ="hello world" | |
360 | User=root | |
361 | WorkingDirectory=/tmp | |
362 | Capability=CAP_BLOCK_SUSPEND CAP_BPF CAP_CHOWN | |
363 | DropCapability=CAP_AUDIT_CONTROL CAP_AUDIT_WRITE | |
364 | AmbientCapability=CAP_BPF CAP_CHOWN | |
365 | NoNewPrivileges=no | |
366 | MachineID=f28f129b51874b1280a89421ec4b4ad4 | |
367 | PrivateUsers=no | |
368 | NotifyReady=no | |
369 | SystemCallFilter=@basic-io @chown | |
370 | SystemCallFilter=~ @clock | |
371 | LimitNOFILE=1024:2048 | |
372 | LimitRTPRIO=8:16 | |
373 | OOMScoreAdjust=32 | |
374 | CPUAffinity=0,0-5,1-5 | |
375 | Hostname=nspawn-settings | |
376 | ResolvConf=copy-host | |
377 | Timezone=delete | |
378 | LinkJournal=no | |
379 | SuppressSync=no | |
380 | ||
381 | [Files] | |
382 | ReadOnly=no | |
383 | Volatile=no | |
384 | TemporaryFileSystem=/tmp | |
385 | TemporaryFileSystem=/opt/tmp | |
386 | Inaccessible=/opt/inaccessible | |
387 | Inaccessible=/opt/also-inaccessible | |
388 | PrivateUsersOwnership=auto | |
389 | Overlay=+/var::/var | |
390 | ${COVERAGE_BUILD_DIR:+"Bind=$COVERAGE_BUILD_DIR"} | |
391 | ||
392 | [Network] | |
393 | Private=yes | |
394 | VirtualEthernet=yes | |
395 | VirtualEthernetExtra=my-fancy-veth1 | |
396 | VirtualEthernetExtra=fancy-veth2:my-fancy-veth2 | |
397 | Interface=sd-shared1 sd-shared2:sd-shared2 | |
927e20fa YW |
398 | MACVLAN=sd-macvlan1 sd-macvlan2:my-macvlan2 sd-macvlanloong |
399 | IPVLAN=sd-ipvlan1 sd-ipvlan2:my-ipvlan2 sd-ipvlanlooong | |
cbd20ab4 FS |
400 | Zone=sd-zone0 |
401 | Port=80 | |
402 | Port=81:8181 | |
403 | Port=tcp:60 | |
404 | Port=udp:60:61 | |
405 | EOF | |
406 | cat >"$root/entrypoint.sh" <<\EOF | |
407 | #!/bin/bash -ex | |
408 | ||
409 | [[ "$1" == "foo bar" ]] | |
410 | [[ "$2" == "bar baz" ]] | |
411 | ||
412 | [[ "$USER" == root ]] | |
413 | [[ "$FOO" == bar ]] | |
414 | [[ "$BAZ" == "hello world" ]] | |
415 | [[ "$PWD" == /tmp ]] | |
416 | [[ "$(</etc/machine-id)" == f28f129b51874b1280a89421ec4b4ad4 ]] | |
417 | [[ "$(ulimit -S -n)" -eq 1024 ]] | |
418 | [[ "$(ulimit -H -n)" -eq 2048 ]] | |
419 | [[ "$(ulimit -S -r)" -eq 8 ]] | |
420 | [[ "$(ulimit -H -r)" -eq 16 ]] | |
421 | [[ "$(</proc/self/oom_score_adj)" -eq 32 ]] | |
422 | [[ "$(hostname)" == nspawn-settings ]] | |
423 | [[ -e /etc/resolv.conf ]] | |
424 | [[ ! -e /etc/localtime ]] | |
425 | ||
426 | mountpoint /tmp | |
427 | touch /tmp/foo | |
428 | mountpoint /opt/tmp | |
429 | touch /opt/tmp/foo | |
430 | touch /opt/inaccessible/foo && exit 1 | |
431 | touch /opt/also-inaccessible/foo && exit 1 | |
432 | mountpoint /var | |
433 | ||
434 | ip link | |
435 | ip link | grep host-only && exit 1 | |
436 | ip link | grep host0@ | |
437 | ip link | grep my-fancy-veth1@ | |
438 | ip link | grep my-fancy-veth2@ | |
439 | ip link | grep sd-shared1 | |
440 | ip link | grep sd-shared2 | |
441 | ip link | grep mv-sd-macvlan1@ | |
442 | ip link | grep my-macvlan2@ | |
443 | ip link | grep iv-sd-ipvlan1@ | |
444 | ip link | grep my-ipvlan2@ | |
445 | EOF | |
446 | timeout 30 systemd-nspawn --directory="$root" | |
447 | ||
448 | # And now for stuff that needs to run separately | |
449 | # | |
450 | # Note on the condition below: since our container tree is owned by root, | |
451 | # both "yes" and "identity" private users settings will behave the same | |
452 | # as PrivateUsers=0:65535, which makes BindUser= fail as the UID already | |
453 | # exists there, so skip setting it in such case | |
454 | for private_users in "131072:65536" yes identity pick; do | |
455 | cat >"/run/systemd/nspawn/$container.nspawn" <<EOF | |
456 | [Exec] | |
457 | Hostname=private-users | |
458 | PrivateUsers=$private_users | |
459 | ||
460 | [Files] | |
461 | PrivateUsersOwnership=auto | |
462 | BindUser= | |
463 | $([[ "$private_users" =~ (yes|identity) ]] || echo "BindUser=testuser") | |
464 | ${COVERAGE_BUILD_DIR:+"Bind=$COVERAGE_BUILD_DIR"} | |
465 | EOF | |
466 | cat "/run/systemd/nspawn/$container.nspawn" | |
467 | chown -R root:root "$root" | |
468 | systemd-nspawn --directory="$root" bash -xec '[[ "$(hostname)" == private-users ]]' | |
469 | done | |
470 | ||
471 | rm -fr "$root" "/run/systemd/nspawn/$container.nspawn" | |
472 | } | |
473 | ||
d5a6ff8c FS |
474 | bind_user_cleanup() { |
475 | userdel --force --remove nspawn-bind-user-1 | |
476 | userdel --force --remove nspawn-bind-user-2 | |
477 | } | |
478 | ||
479 | testcase_bind_user() { | |
480 | local root | |
481 | ||
482 | root="$(mktemp -d /var/lib/machines/testsuite-13.bind-user.XXX)" | |
483 | create_dummy_container "$root" | |
484 | useradd --create-home --user-group nspawn-bind-user-1 | |
485 | useradd --create-home --user-group nspawn-bind-user-2 | |
486 | trap bind_user_cleanup RETURN | |
487 | touch /home/nspawn-bind-user-1/foo | |
488 | touch /home/nspawn-bind-user-2/bar | |
489 | # Add a couple of POSIX ACLs to test the patch-uid stuff | |
490 | mkdir -p "$root/opt" | |
491 | setfacl -R -m 'd:u:nspawn-bind-user-1:rwX' -m 'u:nspawn-bind-user-1:rwX' "$root/opt" | |
492 | setfacl -R -m 'd:g:nspawn-bind-user-1:rwX' -m 'g:nspawn-bind-user-1:rwX' "$root/opt" | |
493 | ||
494 | systemd-nspawn --directory="$root" \ | |
495 | --private-users=pick \ | |
496 | --bind-user=nspawn-bind-user-1 \ | |
497 | bash -xec 'test -e /run/host/home/nspawn-bind-user-1/foo' | |
498 | ||
499 | systemd-nspawn --directory="$root" \ | |
500 | --private-users=pick \ | |
501 | --private-users-ownership=chown \ | |
502 | --bind-user=nspawn-bind-user-1 \ | |
503 | --bind-user=nspawn-bind-user-2 \ | |
504 | bash -xec 'test -e /run/host/home/nspawn-bind-user-1/foo; test -e /run/host/home/nspawn-bind-user-2/bar' | |
505 | chown -R root:root "$root" | |
506 | ||
507 | # User/group name collision | |
508 | echo "nspawn-bind-user-2:x:1000:1000:nspawn-bind-user-2:/home/nspawn-bind-user-2:/bin/bash" >"$root/etc/passwd" | |
509 | (! systemd-nspawn --directory="$root" \ | |
510 | --private-users=pick \ | |
511 | --bind-user=nspawn-bind-user-1 \ | |
512 | --bind-user=nspawn-bind-user-2 \ | |
513 | true) | |
514 | rm -f "$root/etc/passwd" | |
515 | ||
516 | echo "nspawn-bind-user-2:x:1000:" >"$root/etc/group" | |
517 | (! systemd-nspawn --directory="$root" \ | |
518 | --private-users=pick \ | |
519 | --bind-user=nspawn-bind-user-1 \ | |
520 | --bind-user=nspawn-bind-user-2 \ | |
521 | true) | |
522 | rm -f "$root/etc/group" | |
523 | ||
524 | rm -fr "$root" | |
525 | } | |
526 | ||
cf260f79 | 527 | testcase_bind_tmp_path() { |
378db9e2 FS |
528 | # https://github.com/systemd/systemd/issues/4789 |
529 | local root | |
530 | ||
531 | root="$(mktemp -d /var/lib/machines/testsuite-13.bind-tmp-path.XXX)" | |
5656759d | 532 | create_dummy_container "$root" |
378db9e2 FS |
533 | : >/tmp/bind |
534 | systemd-nspawn --register=no \ | |
535 | --directory="$root" \ | |
536 | --bind=/tmp/bind \ | |
5656759d | 537 | bash -c 'test -e /tmp/bind' |
378db9e2 FS |
538 | |
539 | rm -fr "$root" /tmp/bind | |
540 | } | |
541 | ||
cf260f79 | 542 | testcase_norbind() { |
378db9e2 FS |
543 | # https://github.com/systemd/systemd/issues/13170 |
544 | local root | |
545 | ||
546 | root="$(mktemp -d /var/lib/machines/testsuite-13.norbind-path.XXX)" | |
547 | mkdir -p /tmp/binddir/subdir | |
548 | echo -n "outer" >/tmp/binddir/subdir/file | |
549 | mount -t tmpfs tmpfs /tmp/binddir/subdir | |
550 | echo -n "inner" >/tmp/binddir/subdir/file | |
5656759d | 551 | create_dummy_container "$root" |
378db9e2 FS |
552 | |
553 | systemd-nspawn --register=no \ | |
554 | --directory="$root" \ | |
555 | --bind=/tmp/binddir:/mnt:norbind \ | |
5656759d | 556 | bash -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; exit 1; fi' |
378db9e2 FS |
557 | |
558 | umount /tmp/binddir/subdir | |
559 | rm -fr "$root" /tmp/binddir/ | |
560 | } | |
561 | ||
cf260f79 | 562 | rootidmap_cleanup() { |
378db9e2 FS |
563 | local dir="${1:?}" |
564 | ||
565 | mountpoint -q "$dir/bind" && umount "$dir/bind" | |
566 | rm -fr "$dir" | |
567 | } | |
568 | ||
cf260f79 | 569 | testcase_rootidmap() { |
378db9e2 FS |
570 | local root cmd permissions |
571 | local owner=1000 | |
572 | ||
573 | root="$(mktemp -d /var/lib/machines/testsuite-13.rootidmap-path.XXX)" | |
574 | # Create ext4 image, as ext4 supports idmapped-mounts. | |
575 | mkdir -p /tmp/rootidmap/bind | |
576 | dd if=/dev/zero of=/tmp/rootidmap/ext4.img bs=4k count=2048 | |
577 | mkfs.ext4 /tmp/rootidmap/ext4.img | |
578 | mount /tmp/rootidmap/ext4.img /tmp/rootidmap/bind | |
cf260f79 | 579 | trap "rootidmap_cleanup /tmp/rootidmap/" RETURN |
378db9e2 FS |
580 | |
581 | touch /tmp/rootidmap/bind/file | |
582 | chown -R "$owner:$owner" /tmp/rootidmap/bind | |
583 | ||
5656759d | 584 | create_dummy_container "$root" |
378db9e2 FS |
585 | cmd='PERMISSIONS=$(stat -c "%u:%g" /mnt/file); if [[ $PERMISSIONS != "0:0" ]]; then echo "*** wrong permissions: $PERMISSIONS"; return 1; fi; touch /mnt/other_file' |
586 | if ! SYSTEMD_LOG_TARGET=console \ | |
587 | systemd-nspawn --register=no \ | |
588 | --directory="$root" \ | |
589 | --bind=/tmp/rootidmap/bind:/mnt:rootidmap \ | |
5656759d | 590 | bash -c "$cmd" |& tee nspawn.out; then |
378db9e2 FS |
591 | if grep -q "Failed to map ids for bind mount.*: Function not implemented" nspawn.out; then |
592 | echo "idmapped mounts are not supported, skipping the test..." | |
593 | return 0 | |
594 | fi | |
595 | ||
596 | return 1 | |
597 | fi | |
598 | ||
599 | permissions=$(stat -c "%u:%g" /tmp/rootidmap/bind/other_file) | |
600 | if [[ $permissions != "$owner:$owner" ]]; then | |
601 | echo "*** wrong permissions: $permissions" | |
602 | [[ "$IS_USERNS_SUPPORTED" == "yes" ]] && return 1 | |
603 | fi | |
604 | } | |
605 | ||
cf260f79 | 606 | testcase_notification_socket() { |
378db9e2 | 607 | # https://github.com/systemd/systemd/issues/4944 |
5656759d FS |
608 | local root |
609 | local cmd='echo a | nc -U -u -w 1 /run/host/notify' | |
610 | ||
611 | root="$(mktemp -d /var/lib/machines/testsuite-13.check_notification_socket.XXX)" | |
612 | create_dummy_container "$root" | |
378db9e2 | 613 | |
5656759d FS |
614 | systemd-nspawn --register=no --directory="$root" bash -x -c "$cmd" |
615 | systemd-nspawn --register=no --directory="$root" -U bash -x -c "$cmd" | |
cbd20ab4 FS |
616 | |
617 | rm -fr "$root" | |
378db9e2 FS |
618 | } |
619 | ||
cf260f79 | 620 | testcase_os_release() { |
378db9e2 FS |
621 | local root entrypoint os_release_source |
622 | ||
cf260f79 | 623 | root="$(mktemp -d /var/lib/machines/testsuite-13.os-release.XXX)" |
5656759d | 624 | create_dummy_container "$root" |
378db9e2 FS |
625 | entrypoint="$root/entrypoint.sh" |
626 | cat >"$entrypoint" <<\EOF | |
5656759d | 627 | #!/usr/bin/bash -ex |
378db9e2 FS |
628 | |
629 | . /tmp/os-release | |
630 | [[ -n "${ID:-}" && "$ID" != "$container_host_id" ]] && exit 1 | |
631 | [[ -n "${VERSION_ID:-}" && "$VERSION_ID" != "$container_host_version_id" ]] && exit 1 | |
632 | [[ -n "${BUILD_ID:-}" && "$BUILD_ID" != "$container_host_build_id" ]] && exit 1 | |
633 | [[ -n "${VARIANT_ID:-}" && "$VARIANT_ID" != "$container_host_variant_id" ]] && exit 1 | |
634 | ||
635 | cd /tmp | |
636 | (cd /run/host && md5sum os-release) | md5sum -c | |
637 | EOF | |
638 | chmod +x "$entrypoint" | |
639 | ||
640 | os_release_source="/etc/os-release" | |
641 | if [[ ! -r "$os_release_source" ]]; then | |
642 | os_release_source="/usr/lib/os-release" | |
643 | elif [[ -L "$os_release_source" ]]; then | |
644 | # Ensure that /etc always wins if available | |
645 | cp --remove-destination -fv /usr/lib/os-release /etc/os-release | |
646 | echo MARKER=1 >>/etc/os-release | |
647 | fi | |
648 | ||
649 | systemd-nspawn --register=no \ | |
650 | --directory="$root" \ | |
651 | --bind="$os_release_source:/tmp/os-release" \ | |
652 | "${entrypoint##"$root"}" | |
653 | ||
654 | if grep -q MARKER /etc/os-release; then | |
655 | ln -svrf /usr/lib/os-release /etc/os-release | |
656 | fi | |
657 | ||
658 | rm -fr "$root" | |
659 | } | |
660 | ||
cf260f79 | 661 | testcase_machinectl_bind() { |
378db9e2 | 662 | local service_path service_name root container_name ec |
5656759d | 663 | local cmd='for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; sleep .5; done; exit 1;' |
378db9e2 | 664 | |
cf260f79 | 665 | root="$(mktemp -d /var/lib/machines/testsuite-13.machinectl-bind.XXX)" |
5656759d FS |
666 | create_dummy_container "$root" |
667 | container_name="$(basename "$root")" | |
378db9e2 FS |
668 | |
669 | service_path="$(mktemp /run/systemd/system/nspawn-machinectl-bind-XXX.service)" | |
670 | service_name="${service_path##*/}" | |
671 | cat >"$service_path" <<EOF | |
672 | [Service] | |
673 | Type=notify | |
5656759d | 674 | ExecStart=systemd-nspawn --directory="$root" --notify-ready=no /usr/bin/bash -xec "$cmd" |
378db9e2 FS |
675 | EOF |
676 | ||
677 | systemctl daemon-reload | |
678 | systemctl start "$service_name" | |
679 | touch /tmp/marker | |
680 | machinectl bind --mkdir "$container_name" /tmp/marker | |
681 | ||
682 | timeout 10 bash -c "while [[ '\$(systemctl show -P SubState $service_name)' == running ]]; do sleep .2; done" | |
683 | ec="$(systemctl show -P ExecMainStatus "$service_name")" | |
5656759d | 684 | systemctl stop "$service_name" |
378db9e2 FS |
685 | |
686 | rm -fr "$root" "$service_path" | |
687 | ||
688 | return "$ec" | |
689 | } | |
690 | ||
cf260f79 | 691 | testcase_selinux() { |
378db9e2 FS |
692 | # Basic test coverage to avoid issues like https://github.com/systemd/systemd/issues/19976 |
693 | if ! command -v selinuxenabled >/dev/null || ! selinuxenabled; then | |
694 | echo >&2 "SELinux is not enabled, skipping SELinux-related tests" | |
695 | return 0 | |
696 | fi | |
697 | ||
698 | local root | |
699 | ||
cf260f79 | 700 | root="$(mktemp -d /var/lib/machines/testsuite-13.selinux.XXX)" |
5656759d | 701 | create_dummy_container "$root" |
378db9e2 FS |
702 | chcon -R -t container_t "$root" |
703 | ||
704 | systemd-nspawn --register=no \ | |
705 | --boot \ | |
706 | --directory="$root" \ | |
707 | --selinux-apifs-context=system_u:object_r:container_file_t:s0:c0,c1 \ | |
708 | --selinux-context=system_u:system_r:container_t:s0:c0,c1 | |
709 | ||
710 | rm -fr "$root" | |
711 | } | |
712 | ||
cf260f79 | 713 | testcase_ephemeral_config() { |
378db9e2 FS |
714 | # https://github.com/systemd/systemd/issues/13297 |
715 | local root container_name | |
716 | ||
cf260f79 | 717 | root="$(mktemp -d /var/lib/machines/testsuite-13.ephemeral-config.XXX)" |
5656759d | 718 | create_dummy_container "$root" |
cf260f79 | 719 | container_name="$(basename "$root")" |
378db9e2 FS |
720 | |
721 | mkdir -p /run/systemd/nspawn/ | |
28ed2326 | 722 | rm -f "/etc/systemd/nspawn/$container_name.nspawn" |
378db9e2 FS |
723 | cat >"/run/systemd/nspawn/$container_name.nspawn" <<EOF |
724 | [Files] | |
28ed2326 | 725 | ${COVERAGE_BUILD_DIR:+"Bind=$COVERAGE_BUILD_DIR"} |
378db9e2 FS |
726 | BindReadOnly=/tmp/ephemeral-config |
727 | EOF | |
728 | touch /tmp/ephemeral-config | |
729 | ||
730 | systemd-nspawn --register=no \ | |
731 | --directory="$root" \ | |
732 | --ephemeral \ | |
5656759d | 733 | bash -x -c "test -f /tmp/ephemeral-config" |
378db9e2 FS |
734 | |
735 | systemd-nspawn --register=no \ | |
736 | --directory="$root" \ | |
737 | --ephemeral \ | |
738 | --machine=foobar \ | |
5656759d | 739 | bash -x -c "! test -f /tmp/ephemeral-config" |
378db9e2 | 740 | |
cbd20ab4 | 741 | rm -fr "$root" "/run/systemd/nspawn/$container_name.nspawn" |
378db9e2 FS |
742 | } |
743 | ||
744 | matrix_run_one() { | |
745 | local cgroupsv2="${1:?}" | |
746 | local use_cgns="${2:?}" | |
747 | local api_vfs_writable="${3:?}" | |
748 | local root | |
749 | ||
750 | if [[ "$cgroupsv2" == "yes" && "$IS_CGROUPSV2_SUPPORTED" == "no" ]]; then | |
751 | echo >&2 "Unified cgroup hierarchy is not supported, skipping..." | |
752 | return 0 | |
753 | fi | |
754 | ||
755 | if [[ "$use_cgns" == "yes" && "$IS_CGNS_SUPPORTED" == "no" ]]; then | |
756 | echo >&2 "CGroup namespaces are not supported, skipping..." | |
757 | return 0 | |
758 | fi | |
759 | ||
760 | root="$(mktemp -d "/var/lib/machines/testsuite-13.unified-$1-cgns-$2-api-vfs-writable-$3.XXX")" | |
5656759d | 761 | create_dummy_container "$root" |
378db9e2 FS |
762 | |
763 | SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ | |
764 | systemd-nspawn --register=no \ | |
765 | --directory="$root" \ | |
766 | --boot | |
767 | ||
768 | SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ | |
769 | systemd-nspawn --register=no \ | |
770 | --directory="$root" \ | |
771 | --private-network \ | |
772 | --boot | |
773 | ||
774 | if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ | |
775 | systemd-nspawn --register=no \ | |
776 | --directory="$root" \ | |
777 | --private-users=pick \ | |
778 | --boot; then | |
779 | [[ "$IS_USERNS_SUPPORTED" == "yes" && "$api_vfs_writable" == "network" ]] && return 1 | |
780 | else | |
781 | [[ "$IS_USERNS_SUPPORTED" == "no" && "$api_vfs_writable" = "network" ]] && return 1 | |
782 | fi | |
783 | ||
784 | if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ | |
785 | systemd-nspawn --register=no \ | |
786 | --directory="$root" \ | |
787 | --private-network \ | |
788 | --private-users=pick \ | |
789 | --boot; then | |
790 | [[ "$IS_USERNS_SUPPORTED" == "yes" && "$api_vfs_writable" == "yes" ]] && return 1 | |
791 | else | |
792 | [[ "$IS_USERNS_SUPPORTED" == "no" && "$api_vfs_writable" = "yes" ]] && return 1 | |
793 | fi | |
794 | ||
795 | local netns_opt="--network-namespace-path=/proc/self/ns/net" | |
796 | local net_opt | |
797 | local net_opts=( | |
798 | "--network-bridge=lo" | |
799 | "--network-interface=lo" | |
800 | "--network-ipvlan=lo" | |
801 | "--network-macvlan=lo" | |
802 | "--network-veth" | |
803 | "--network-veth-extra=lo" | |
804 | "--network-zone=zone" | |
805 | ) | |
806 | ||
807 | # --network-namespace-path and network-related options cannot be used together | |
808 | for net_opt in "${net_opts[@]}"; do | |
809 | echo "$netns_opt in combination with $net_opt should fail" | |
810 | if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ | |
811 | systemd-nspawn --register=no \ | |
812 | --directory="$root" \ | |
813 | --boot \ | |
814 | "$netns_opt" \ | |
815 | "$net_opt"; then | |
816 | echo >&2 "unexpected pass" | |
817 | return 1 | |
818 | fi | |
819 | done | |
820 | ||
821 | # allow combination of --network-namespace-path and --private-network | |
822 | SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ | |
823 | systemd-nspawn --register=no \ | |
824 | --directory="$root" \ | |
825 | --boot \ | |
826 | --private-network \ | |
827 | "$netns_opt" | |
828 | ||
829 | # test --network-namespace-path works with a network namespace created by "ip netns" | |
830 | ip netns add nspawn_test | |
831 | netns_opt="--network-namespace-path=/run/netns/nspawn_test" | |
832 | SYSTEMD_NSPAWN_UNIFIED_HIERARCHY="$cgroupsv2" SYSTEMD_NSPAWN_USE_CGNS="$use_cgns" SYSTEMD_NSPAWN_API_VFS_WRITABLE="$api_vfs_writable" \ | |
833 | systemd-nspawn --register=no \ | |
834 | --directory="$root" \ | |
835 | --network-namespace-path=/run/netns/nspawn_test \ | |
5656759d | 836 | ip a | grep -v -E '^1: lo.*UP' |
378db9e2 FS |
837 | ip netns del nspawn_test |
838 | ||
839 | rm -fr "$root" | |
840 | ||
841 | return 0 | |
842 | } | |
843 | ||
b60d910d | 844 | run_testcases |
378db9e2 FS |
845 | |
846 | for api_vfs_writable in yes no network; do | |
847 | matrix_run_one no no $api_vfs_writable | |
848 | matrix_run_one yes no $api_vfs_writable | |
849 | matrix_run_one no yes $api_vfs_writable | |
850 | matrix_run_one yes yes $api_vfs_writable | |
851 | done |