2023-05-12 18:39:41 +03:00
#!/usr/bin/env bash
# SPDX-License-Identifier: LGPL-2.1-or-later
# shellcheck disable=SC2016
2023-05-17 22:49:20 +03:00
#
# Notes on coverage: when collecting coverage we need the $BUILD_DIR present
# and writable in the container as well. To do this in the least intrusive way,
# two things are going on in the background (only when built with -Db_coverage=true):
# 1) the systemd-nspawn@.service is copied to /etc/systemd/system/ with
# --bind=$BUILD_DIR appended to the ExecStart= line
# 2) each create_dummy_container() call also creates an .nspawn file in /run/systemd/nspawn/
# with the last fragment from the path used as a name
#
# The first change is quite self-contained and applies only to containers run
# with machinectl. The second one might cause some unexpected side-effects, namely:
# - nspawn config (setting) files don't support dropins, so tests that test
# the config files might need some tweaking (as seen below with
# the $COVERAGE_BUILD_DIR shenanigans) since they overwrite the .nspawn file
# - also a note - if /etc/systemd/nspawn/cont-name.nspawn exists, it takes
# precedence and /run/systemd/nspawn/cont-name.nspawn won't be read even
# if it exists
2023-05-19 18:27:38 +03:00
# - also a note 2 - --bind= overrides any Bind= from a config file
2023-05-17 22:49:20 +03:00
# - in some cases we don't create a test container using create_dummy_container(),
# so in that case an explicit call to coverage_create_nspawn_dropin() is needed
2023-05-19 18:27:38 +03:00
#
# However, even after jumping through all these hooks, there still might (and is)
# some "incorrectly" missing coverage, especially in the window between spawning
# the inner child process and bind-mounting the coverage $BUILD_DIR
2023-05-12 18:39:41 +03:00
set -eux
set -o pipefail
2023-05-22 13:39:25 +03:00
# shellcheck source=test/units/test-control.sh
. " $( dirname " $0 " ) " /test-control.sh
2023-05-17 20:10:55 +03:00
# shellcheck source=test/units/util.sh
. " $( dirname " $0 " ) " /util.sh
2023-05-22 13:39:25 +03:00
2023-05-12 18:39:41 +03:00
export SYSTEMD_LOG_LEVEL = debug
export SYSTEMD_LOG_TARGET = journal
at_exit( ) {
set +e
2023-11-03 11:17:48 +03:00
mountpoint -q /var/lib/machines && umount --recursive /var/lib/machines
2023-05-17 22:49:20 +03:00
rm -f /run/systemd/nspawn/*.nspawn
2023-05-12 18:39:41 +03:00
}
trap at_exit EXIT
# check cgroup-v2
IS_CGROUPSV2_SUPPORTED = no
mkdir -p /tmp/cgroup2
if mount -t cgroup2 cgroup2 /tmp/cgroup2; then
IS_CGROUPSV2_SUPPORTED = yes
umount /tmp/cgroup2
fi
rmdir /tmp/cgroup2
# check cgroup namespaces
IS_CGNS_SUPPORTED = no
if [ [ -f /proc/1/ns/cgroup ] ] ; then
IS_CGNS_SUPPORTED = yes
fi
IS_USERNS_SUPPORTED = no
# On some systems (e.g. CentOS 7) the default limit for user namespaces
# is set to 0, which causes the following unshare syscall to fail, even
# with enabled user namespaces support. By setting this value explicitly
# we can ensure the user namespaces support to be detected correctly.
sysctl -w user.max_user_namespaces= 10000
2023-05-17 20:10:55 +03:00
if unshare -U bash -c :; then
2023-05-12 18:39:41 +03:00
IS_USERNS_SUPPORTED = yes
fi
# Mount tmpfs over /var/lib/machines to not pollute the image
mkdir -p /var/lib/machines
mount -t tmpfs tmpfs /var/lib/machines
2023-05-17 14:57:37 +03:00
testcase_sanity( ) {
2023-05-15 19:57:55 +03:00
local template root image uuid tmpdir
2023-05-14 19:24:33 +03:00
2023-05-15 11:15:24 +03:00
tmpdir = " $( mktemp -d) "
2023-05-14 19:24:33 +03:00
template = " $( mktemp -d /tmp/nspawn-template.XXX) "
2023-05-17 20:10:55 +03:00
create_dummy_container " $template "
2023-05-14 19:24:33 +03:00
# Create a simple image from the just created container template
image = " $( mktemp /var/lib/machines/testsuite-13.image-XXX.img) "
2023-06-24 21:25:04 +03:00
dd if = /dev/zero of = " $image " bs = 1M count = 64
2023-05-14 19:24:33 +03:00
mkfs.ext4 " $image "
mkdir -p /mnt
mount -o loop " $image " /mnt
cp -r " $template " /* /mnt/
umount /mnt
systemd-nspawn --help --no-pager
systemd-nspawn --version
# --template=
root = " $( mktemp -u -d /var/lib/machines/testsuite-13.sanity.XXX) "
2023-05-17 22:49:20 +03:00
coverage_create_nspawn_dropin " $root "
2023-05-17 20:10:55 +03:00
( ! systemd-nspawn --directory= " $root " bash -xec 'echo hello' )
2023-05-14 19:24:33 +03:00
# Initialize $root from $template (the $root directory must not exist, hence
# the `mktemp -u` above)
2023-05-17 20:10:55 +03:00
systemd-nspawn --directory= " $root " --template= " $template " bash -xec 'echo hello'
systemd-nspawn --directory= " $root " bash -xec 'echo hello; touch /initialized'
2023-05-14 19:24:33 +03:00
test -e " $root /initialized "
# Check if the $root doesn't get re-initialized once it's not empty
2023-05-17 20:10:55 +03:00
systemd-nspawn --directory= " $root " --template= " $template " bash -xec 'echo hello'
2023-05-14 19:24:33 +03:00
test -e " $root /initialized "
2023-05-17 20:10:55 +03:00
systemd-nspawn --directory= " $root " --ephemeral bash -xec 'touch /ephemeral'
2023-05-14 19:24:33 +03:00
test ! -e " $root /ephemeral "
2023-05-15 11:15:24 +03:00
( ! systemd-nspawn --directory= " $root " \
--read-only \
2023-05-17 20:10:55 +03:00
bash -xec 'touch /nope' )
2023-05-14 19:24:33 +03:00
test ! -e " $root /nope "
2023-05-17 20:10:55 +03:00
systemd-nspawn --image= " $image " bash -xec 'echo hello'
2023-05-14 19:24:33 +03:00
# --volatile=
touch " $root /usr/has-usr "
# volatile(=yes): rootfs is tmpfs, /usr/ from the OS tree is mounted read only
systemd-nspawn --directory= " $root " \
--volatile \
2023-05-17 20:10:55 +03:00
bash -xec 'test -e /usr/has-usr; touch /usr/read-only && exit 1; touch /nope'
2023-05-14 19:24:33 +03:00
test ! -e " $root /nope "
test ! -e " $root /usr/read-only "
systemd-nspawn --directory= " $root " \
--volatile= yes \
2023-05-17 20:10:55 +03:00
bash -xec 'test -e /usr/has-usr; touch /usr/read-only && exit 1; touch /nope'
2023-05-14 19:24:33 +03:00
test ! -e " $root /nope "
test ! -e " $root /usr/read-only "
# volatile=state: rootfs is read-only, /var/ is tmpfs
systemd-nspawn --directory= " $root " \
--volatile= state \
2023-05-17 20:10:55 +03:00
bash -xec 'test -e /usr/has-usr; mountpoint /var; touch /read-only && exit 1; touch /var/nope'
2023-05-14 19:24:33 +03:00
test ! -e " $root /read-only "
test ! -e " $root /var/nope "
# volatile=state: tmpfs overlay is mounted over rootfs
systemd-nspawn --directory= " $root " \
--volatile= overlay \
2023-05-17 20:10:55 +03:00
bash -xec 'test -e /usr/has-usr; touch /nope; touch /var/also-nope; touch /usr/nope-too'
2023-05-14 19:24:33 +03:00
test ! -e " $root /nope "
test ! -e " $root /var/also-nope "
test ! -e " $root /usr/nope-too "
# --machine=, --hostname=
systemd-nspawn --directory= " $root " \
--machine= "foo-bar.baz" \
2023-05-17 20:10:55 +03:00
bash -xec '[[ $(hostname) == foo-bar.baz ]]'
2023-05-14 19:24:33 +03:00
systemd-nspawn --directory= " $root " \
--hostname= "hello.world.tld" \
2023-05-17 20:10:55 +03:00
bash -xec '[[ $(hostname) == hello.world.tld ]]'
2023-05-14 19:24:33 +03:00
systemd-nspawn --directory= " $root " \
--machine= "foo-bar.baz" \
--hostname= "hello.world.tld" \
2023-05-17 20:10:55 +03:00
bash -xec '[[ $(hostname) == hello.world.tld ]]'
2023-05-14 19:24:33 +03:00
# --uuid=
rm -f " $root /etc/machine-id "
uuid = "deadbeef-dead-dead-beef-000000000000"
systemd-nspawn --directory= " $root " \
--uuid= " $uuid " \
2023-05-17 20:10:55 +03:00
bash -xec " [[ \$container_uuid == $uuid ]] "
2023-05-14 19:24:33 +03:00
# --as-pid2
2023-05-17 20:10:55 +03:00
systemd-nspawn --directory= " $root " bash -xec '[[ $$ -eq 1 ]]'
systemd-nspawn --directory= " $root " --as-pid2 bash -xec '[[ $$ -eq 2 ]]'
2023-05-14 19:24:33 +03:00
# --user=
2023-05-17 20:10:55 +03:00
# "Fake" getent passwd's bare minimum, so we don't have to pull it in
# with all the DSO shenanigans
cat >" $root /bin/getent " <<\E OF
#!/bin/bash
if [ [ $# -eq 0 ] ] ; then
:
elif [ [ $1 = = passwd ] ] ; then
echo "testuser:x:1000:1000:testuser:/:/bin/sh"
elif [ [ $1 = = initgroups ] ] ; then
echo "testuser"
fi
EOF
chmod +x " $root /bin/getent "
systemd-nspawn --directory= " $root " bash -xec '[[ $USER == root ]]'
systemd-nspawn --directory= " $root " --user= testuser bash -xec '[[ $USER == testuser ]]'
2023-05-14 19:24:33 +03:00
# --settings= + .nspawn files
mkdir -p /run/systemd/nspawn/
uuid = "deadbeef-dead-dead-beef-000000000000"
echo -ne "[Exec]\nMachineID=deadbeef-dead-dead-beef-111111111111" >/run/systemd/nspawn/foo-bar.nspawn
systemd-nspawn --directory= " $root " \
--machine= foo-bar \
--settings= yes \
2023-05-17 20:10:55 +03:00
bash -xec '[[ $container_uuid == deadbeef-dead-dead-beef-111111111111 ]]'
2023-05-14 19:24:33 +03:00
systemd-nspawn --directory= " $root " \
--machine= foo-bar \
--uuid= " $uuid " \
--settings= yes \
2023-05-17 20:10:55 +03:00
bash -xec " [[ \$container_uuid == $uuid ]] "
2023-05-14 19:24:33 +03:00
systemd-nspawn --directory= " $root " \
--machine= foo-bar \
--uuid= " $uuid " \
--settings= override \
2023-05-17 20:10:55 +03:00
bash -xec '[[ $container_uuid == deadbeef-dead-dead-beef-111111111111 ]]'
2023-05-14 19:24:33 +03:00
systemd-nspawn --directory= " $root " \
--machine= foo-bar \
--uuid= " $uuid " \
--settings= trusted \
2023-05-17 20:10:55 +03:00
bash -xec " [[ \$container_uuid == $uuid ]] "
2023-05-14 19:24:33 +03:00
# Mounts
mkdir " $tmpdir " /{ 1,2,3}
touch " $tmpdir /1/one " " $tmpdir /2/two " " $tmpdir /3/three "
touch " $tmpdir /foo "
# --bind=
systemd-nspawn --directory= " $root " \
2023-05-19 18:27:38 +03:00
${ COVERAGE_BUILD_DIR : +--bind= " $COVERAGE_BUILD_DIR " } \
2023-05-14 19:24:33 +03:00
--bind= " $tmpdir :/foo " \
2023-05-19 18:27:38 +03:00
--bind= " $tmpdir :/also-foo:noidmap,norbind " \
bash -xec 'test -e /foo/foo; touch /foo/bar; test -e /also-foo/bar'
2023-05-14 19:24:33 +03:00
test -e " $tmpdir /bar "
# --bind-ro=
systemd-nspawn --directory= " $root " \
--bind-ro= " $tmpdir :/foo " \
2023-05-19 18:27:38 +03:00
--bind-ro= " $tmpdir :/bar:noidmap,norbind " \
bash -xec 'test -e /foo/foo; touch /foo/baz && exit 1; touch /bar && exit 1; true'
2023-05-14 19:24:33 +03:00
# --inaccessible=
systemd-nspawn --directory= " $root " \
--inaccessible= /var \
2023-05-17 20:10:55 +03:00
bash -xec 'touch /var/foo && exit 1; true'
2023-05-14 19:24:33 +03:00
# --tmpfs=
systemd-nspawn --directory= " $root " \
--tmpfs= /var:rw,nosuid,noexec \
2023-05-17 20:10:55 +03:00
bash -xec 'touch /var/nope'
2023-05-14 19:24:33 +03:00
test ! -e " $root /var/nope "
# --overlay=
systemd-nspawn --directory= " $root " \
--overlay= " $tmpdir /1: $tmpdir /2: $tmpdir /3:/var " \
2023-05-17 20:10:55 +03:00
bash -xec 'test -e /var/one; test -e /var/two; test -e /var/three; touch /var/foo'
2023-05-14 19:24:33 +03:00
test -e " $tmpdir /3/foo "
# --overlay-ro=
systemd-nspawn --directory= " $root " \
--overlay-ro= " $tmpdir /1: $tmpdir /2: $tmpdir /3:/var " \
2023-05-17 20:10:55 +03:00
bash -xec 'test -e /var/one; test -e /var/two; test -e /var/three; touch /var/nope && exit 1; true'
2023-05-14 19:24:33 +03:00
test ! -e " $tmpdir /3/nope "
rm -fr " $tmpdir "
2023-05-17 15:57:50 +03:00
# --port (sanity only)
systemd-nspawn --network-veth --directory= " $root " --port= 80 --port= 90 true
systemd-nspawn --network-veth --directory= " $root " --port= 80:8080 true
systemd-nspawn --network-veth --directory= " $root " --port= tcp:80 true
systemd-nspawn --network-veth --directory= " $root " --port= tcp:80:8080 true
systemd-nspawn --network-veth --directory= " $root " --port= udp:80 true
systemd-nspawn --network-veth --directory= " $root " --port= udp:80:8080 --port= tcp:80:8080 true
( ! systemd-nspawn --network-veth --directory= " $root " --port= true )
( ! systemd-nspawn --network-veth --directory= " $root " --port= -1 true )
( ! systemd-nspawn --network-veth --directory= " $root " --port= : true )
( ! systemd-nspawn --network-veth --directory= " $root " --port= icmp:80:8080 true )
( ! systemd-nspawn --network-veth --directory= " $root " --port= tcp::8080 true )
( ! systemd-nspawn --network-veth --directory= " $root " --port= 8080: true )
2023-05-19 18:27:38 +03:00
# Exercise adding/removing ports from an interface
systemd-nspawn --directory= " $root " \
--network-veth \
--port= 6667 \
--port= 80:8080 \
--port= udp:53 \
--port= tcp:22:2222 \
bash -xec 'ip addr add dev host0 10.0.0.10/24; ip a; ip addr del dev host0 10.0.0.10/24'
# --load-credential=, --set-credential=
echo "foo bar" >/tmp/cred.path
systemd-nspawn --directory= " $root " \
--load-credential= cred.path:/tmp/cred.path \
--set-credential= "cred.set:hello world" \
bash -xec '[[ "$(</run/host/credentials/cred.path)" == "foo bar" ]]; [[ "$(</run/host/credentials/cred.set)" == "hello world" ]]'
rm -f /tmp/cred.path
2023-05-17 15:57:50 +03:00
2023-05-14 19:24:33 +03:00
# Assorted tests
2023-05-17 20:10:55 +03:00
systemd-nspawn --directory= " $root " --suppress-sync= yes bash -xec 'echo hello'
2023-05-14 19:24:33 +03:00
systemd-nspawn --capability= help
systemd-nspawn --resolv-conf= help
systemd-nspawn --timezone= help
# Handling of invalid arguments
opts = (
bind
bind-ro
bind-user
chdir
console
inaccessible
kill-signal
link-journal
load-credential
network-{ interface,macvlan,ipvlan,veth-extra,bridge,zone}
no-new-privileges
oom-score-adjust
overlay
overlay-ro
personality
pivot-root
port
private-users
private-users-ownership
register
resolv-conf
rlimit
root-hash
root-hash-sig
set-credential
settings
suppress-sync
timezone
tmpfs
uuid
)
for opt in " ${ opts [@] } " ; do
( ! systemd-nspawn " -- $opt " )
[ [ " $opt " = = network-zone ] ] && continue
( ! systemd-nspawn " -- $opt ='' " )
( ! systemd-nspawn " -- $opt =%\$š " )
done
( ! systemd-nspawn --volatile= "" )
( ! systemd-nspawn --volatile= -1)
( ! systemd-nspawn --rlimit= = )
}
2023-05-17 23:36:07 +03:00
nspawn_settings_cleanup( ) {
2024-01-17 04:55:35 +03:00
for dev in sd-host-only sd-shared{ 1,2,3} sd-macvlan{ 1,2} sd-ipvlan{ 1,2} ; do
2023-05-17 23:36:07 +03:00
ip link del " $dev " || :
done
return 0
}
testcase_nspawn_settings( ) {
2024-01-17 04:55:35 +03:00
local root container dev private_users wlan_names = '' wlan_checks = ''
2023-05-17 23:36:07 +03:00
mkdir -p /run/systemd/nspawn
root = " $( mktemp -d /var/lib/machines/testsuite-13.nspawn-settings.XXX) "
container = " $( basename " $root " ) "
create_dummy_container " $root "
rm -f " /etc/systemd/nspawn/ $container .nspawn "
mkdir -p " $root /tmp " " $root " /opt/{ tmp,inaccessible,also-inaccessible}
2024-01-17 04:55:35 +03:00
# add virtual wlan interfaces
if modprobe mac80211_hwsim radios = 2; then
wlan_names = 'wlan0 wlan1:wl-renamed1'
wlan_checks = 'ip link | grep wlan0\nip link | grep wl-renamed1'
fi
for dev in sd-host-only sd-shared{ 1,2,3} sd-macvlan{ 1,2} sd-macvlanloong sd-ipvlan{ 1,2} sd-ipvlanlooong; do
2023-05-17 23:36:07 +03:00
ip link add " $dev " type dummy
done
udevadm settle
2024-01-17 04:55:35 +03:00
ip link property add dev sd-shared3 altname sd-altname3 altname sd-altname-tooooooooooooo-long
2023-05-17 23:36:07 +03:00
ip link
trap nspawn_settings_cleanup RETURN
# Let's start with one huge config to test as much as we can at once
cat >" /run/systemd/nspawn/ $container .nspawn " <<EOF
[ Exec]
Boot = no
Ephemeral = no
ProcessTwo = no
Parameters = bash /entrypoint.sh "foo bar" 'bar baz'
Environment = FOO = bar
Environment = BAZ = "hello world"
User = root
WorkingDirectory = /tmp
Capability = CAP_BLOCK_SUSPEND CAP_BPF CAP_CHOWN
DropCapability = CAP_AUDIT_CONTROL CAP_AUDIT_WRITE
AmbientCapability = CAP_BPF CAP_CHOWN
NoNewPrivileges = no
MachineID = f28f129b51874b1280a89421ec4b4ad4
PrivateUsers = no
NotifyReady = no
SystemCallFilter = @basic-io @chown
SystemCallFilter = ~ @clock
LimitNOFILE = 1024:2048
LimitRTPRIO = 8:16
OOMScoreAdjust = 32
CPUAffinity = 0,0-5,1-5
Hostname = nspawn-settings
ResolvConf = copy-host
Timezone = delete
LinkJournal = no
SuppressSync = no
[ Files]
ReadOnly = no
Volatile = no
TemporaryFileSystem = /tmp
TemporaryFileSystem = /opt/tmp
Inaccessible = /opt/inaccessible
Inaccessible = /opt/also-inaccessible
PrivateUsersOwnership = auto
Overlay = +/var::/var
${ COVERAGE_BUILD_DIR : + " Bind= $COVERAGE_BUILD_DIR " }
[ Network]
Private = yes
VirtualEthernet = yes
VirtualEthernetExtra = my-fancy-veth1
VirtualEthernetExtra = fancy-veth2:my-fancy-veth2
2024-01-17 04:55:35 +03:00
Interface = sd-shared1 sd-shared2:sd-renamed2 sd-shared3:sd-altname3 ${ wlan_names }
2023-08-23 06:13:44 +03:00
MACVLAN = sd-macvlan1 sd-macvlan2:my-macvlan2 sd-macvlanloong
IPVLAN = sd-ipvlan1 sd-ipvlan2:my-ipvlan2 sd-ipvlanlooong
2023-05-17 23:36:07 +03:00
Zone = sd-zone0
Port = 80
Port = 81:8181
Port = tcp:60
Port = udp:60:61
EOF
cat >" $root /entrypoint.sh " <<\E OF
#!/bin/bash -ex
[ [ " $1 " = = "foo bar" ] ]
[ [ " $2 " = = "bar baz" ] ]
[ [ " $USER " = = root ] ]
[ [ " $FOO " = = bar ] ]
[ [ " $BAZ " = = "hello world" ] ]
[ [ " $PWD " = = /tmp ] ]
[ [ " $( </etc/machine-id) " = = f28f129b51874b1280a89421ec4b4ad4 ] ]
[ [ " $( ulimit -S -n) " -eq 1024 ] ]
[ [ " $( ulimit -H -n) " -eq 2048 ] ]
[ [ " $( ulimit -S -r) " -eq 8 ] ]
[ [ " $( ulimit -H -r) " -eq 16 ] ]
[ [ " $( </proc/self/oom_score_adj) " -eq 32 ] ]
[ [ " $( hostname) " = = nspawn-settings ] ]
[ [ -e /etc/resolv.conf ] ]
[ [ ! -e /etc/localtime ] ]
mountpoint /tmp
touch /tmp/foo
mountpoint /opt/tmp
touch /opt/tmp/foo
touch /opt/inaccessible/foo && exit 1
touch /opt/also-inaccessible/foo && exit 1
mountpoint /var
ip link
ip link | grep host-only && exit 1
ip link | grep host0@
ip link | grep my-fancy-veth1@
ip link | grep my-fancy-veth2@
ip link | grep sd-shared1
2024-01-17 04:55:35 +03:00
ip link | grep sd-renamed2
ip link | grep sd-shared3
ip link | grep sd-altname3
ip link | grep sd-altname-tooooooooooooo-long
2023-05-17 23:36:07 +03:00
ip link | grep mv-sd-macvlan1@
ip link | grep my-macvlan2@
ip link | grep iv-sd-ipvlan1@
ip link | grep my-ipvlan2@
EOF
2024-01-17 04:55:35 +03:00
echo -e " $wlan_checks " >>" $root /entrypoint.sh "
2023-05-17 23:36:07 +03:00
timeout 30 systemd-nspawn --directory= " $root "
# And now for stuff that needs to run separately
#
# Note on the condition below: since our container tree is owned by root,
# both "yes" and "identity" private users settings will behave the same
# as PrivateUsers=0:65535, which makes BindUser= fail as the UID already
# exists there, so skip setting it in such case
for private_users in "131072:65536" yes identity pick; do
cat >" /run/systemd/nspawn/ $container .nspawn " <<EOF
[ Exec]
Hostname = private-users
PrivateUsers = $private_users
[ Files]
PrivateUsersOwnership = auto
BindUser =
$( [ [ " $private_users " = ~ ( yes| identity) ] ] || echo "BindUser=testuser" )
${ COVERAGE_BUILD_DIR : + " Bind= $COVERAGE_BUILD_DIR " }
EOF
cat " /run/systemd/nspawn/ $container .nspawn "
chown -R root:root " $root "
systemd-nspawn --directory= " $root " bash -xec '[[ "$(hostname)" == private-users ]]'
done
rm -fr " $root " " /run/systemd/nspawn/ $container .nspawn "
}
2023-05-17 16:35:10 +03:00
bind_user_cleanup( ) {
userdel --force --remove nspawn-bind-user-1
userdel --force --remove nspawn-bind-user-2
}
testcase_bind_user( ) {
local root
root = " $( mktemp -d /var/lib/machines/testsuite-13.bind-user.XXX) "
create_dummy_container " $root "
useradd --create-home --user-group nspawn-bind-user-1
useradd --create-home --user-group nspawn-bind-user-2
trap bind_user_cleanup RETURN
touch /home/nspawn-bind-user-1/foo
touch /home/nspawn-bind-user-2/bar
# Add a couple of POSIX ACLs to test the patch-uid stuff
mkdir -p " $root /opt "
setfacl -R -m 'd:u:nspawn-bind-user-1:rwX' -m 'u:nspawn-bind-user-1:rwX' " $root /opt "
setfacl -R -m 'd:g:nspawn-bind-user-1:rwX' -m 'g:nspawn-bind-user-1:rwX' " $root /opt "
systemd-nspawn --directory= " $root " \
--private-users= pick \
--bind-user= nspawn-bind-user-1 \
bash -xec 'test -e /run/host/home/nspawn-bind-user-1/foo'
systemd-nspawn --directory= " $root " \
--private-users= pick \
--private-users-ownership= chown \
--bind-user= nspawn-bind-user-1 \
--bind-user= nspawn-bind-user-2 \
bash -xec 'test -e /run/host/home/nspawn-bind-user-1/foo; test -e /run/host/home/nspawn-bind-user-2/bar'
chown -R root:root " $root "
# User/group name collision
echo "nspawn-bind-user-2:x:1000:1000:nspawn-bind-user-2:/home/nspawn-bind-user-2:/bin/bash" >" $root /etc/passwd "
( ! systemd-nspawn --directory= " $root " \
--private-users= pick \
--bind-user= nspawn-bind-user-1 \
--bind-user= nspawn-bind-user-2 \
true )
rm -f " $root /etc/passwd "
echo "nspawn-bind-user-2:x:1000:" >" $root /etc/group "
( ! systemd-nspawn --directory= " $root " \
--private-users= pick \
--bind-user= nspawn-bind-user-1 \
--bind-user= nspawn-bind-user-2 \
true )
rm -f " $root /etc/group "
rm -fr " $root "
}
2023-05-17 14:57:37 +03:00
testcase_bind_tmp_path( ) {
2023-05-12 18:39:41 +03:00
# https://github.com/systemd/systemd/issues/4789
local root
root = " $( mktemp -d /var/lib/machines/testsuite-13.bind-tmp-path.XXX) "
2023-05-17 20:10:55 +03:00
create_dummy_container " $root "
2023-05-12 18:39:41 +03:00
: >/tmp/bind
systemd-nspawn --register= no \
--directory= " $root " \
--bind= /tmp/bind \
2023-05-17 20:10:55 +03:00
bash -c 'test -e /tmp/bind'
2023-05-12 18:39:41 +03:00
rm -fr " $root " /tmp/bind
}
2023-05-17 14:57:37 +03:00
testcase_norbind( ) {
2023-05-12 18:39:41 +03:00
# https://github.com/systemd/systemd/issues/13170
local root
root = " $( mktemp -d /var/lib/machines/testsuite-13.norbind-path.XXX) "
mkdir -p /tmp/binddir/subdir
echo -n "outer" >/tmp/binddir/subdir/file
mount -t tmpfs tmpfs /tmp/binddir/subdir
echo -n "inner" >/tmp/binddir/subdir/file
2023-05-17 20:10:55 +03:00
create_dummy_container " $root "
2023-05-12 18:39:41 +03:00
systemd-nspawn --register= no \
--directory= " $root " \
--bind= /tmp/binddir:/mnt:norbind \
2023-05-17 20:10:55 +03:00
bash -c 'CONTENT=$(cat /mnt/subdir/file); if [[ $CONTENT != "outer" ]]; then echo "*** unexpected content: $CONTENT"; exit 1; fi'
2023-05-12 18:39:41 +03:00
umount /tmp/binddir/subdir
rm -fr " $root " /tmp/binddir/
}
2023-05-17 14:57:37 +03:00
rootidmap_cleanup( ) {
2023-05-12 18:39:41 +03:00
local dir = " ${ 1 : ? } "
mountpoint -q " $dir /bind " && umount " $dir /bind "
rm -fr " $dir "
}
2023-05-17 14:57:37 +03:00
testcase_rootidmap( ) {
2023-05-12 18:39:41 +03:00
local root cmd permissions
local owner = 1000
root = " $( mktemp -d /var/lib/machines/testsuite-13.rootidmap-path.XXX) "
# Create ext4 image, as ext4 supports idmapped-mounts.
mkdir -p /tmp/rootidmap/bind
dd if = /dev/zero of = /tmp/rootidmap/ext4.img bs = 4k count = 2048
mkfs.ext4 /tmp/rootidmap/ext4.img
mount /tmp/rootidmap/ext4.img /tmp/rootidmap/bind
2023-05-17 14:57:37 +03:00
trap "rootidmap_cleanup /tmp/rootidmap/" RETURN
2023-05-12 18:39:41 +03:00
touch /tmp/rootidmap/bind/file
chown -R " $owner : $owner " /tmp/rootidmap/bind
2023-05-17 20:10:55 +03:00
create_dummy_container " $root "
2023-05-12 18:39:41 +03:00
cmd = 'PERMISSIONS=$(stat -c "%u:%g" /mnt/file); if [[ $PERMISSIONS != "0:0" ]]; then echo "*** wrong permissions: $PERMISSIONS"; return 1; fi; touch /mnt/other_file'
if ! SYSTEMD_LOG_TARGET = console \
systemd-nspawn --register= no \
--directory= " $root " \
--bind= /tmp/rootidmap/bind:/mnt:rootidmap \
2023-05-17 20:10:55 +03:00
bash -c " $cmd " | & tee nspawn.out; then
2023-05-12 18:39:41 +03:00
if grep -q "Failed to map ids for bind mount.*: Function not implemented" nspawn.out; then
echo "idmapped mounts are not supported, skipping the test..."
return 0
fi
return 1
fi
permissions = $( stat -c "%u:%g" /tmp/rootidmap/bind/other_file)
if [ [ $permissions != " $owner : $owner " ] ] ; then
echo " *** wrong permissions: $permissions "
[ [ " $IS_USERNS_SUPPORTED " = = "yes" ] ] && return 1
fi
}
2023-05-17 14:57:37 +03:00
testcase_notification_socket( ) {
2023-05-12 18:39:41 +03:00
# https://github.com/systemd/systemd/issues/4944
2023-05-17 20:10:55 +03:00
local root
local cmd = 'echo a | nc -U -u -w 1 /run/host/notify'
root = " $( mktemp -d /var/lib/machines/testsuite-13.check_notification_socket.XXX) "
create_dummy_container " $root "
2023-05-12 18:39:41 +03:00
2023-05-17 20:10:55 +03:00
systemd-nspawn --register= no --directory= " $root " bash -x -c " $cmd "
systemd-nspawn --register= no --directory= " $root " -U bash -x -c " $cmd "
2023-05-17 23:36:07 +03:00
rm -fr " $root "
2023-05-12 18:39:41 +03:00
}
2023-05-17 14:57:37 +03:00
testcase_os_release( ) {
2023-05-12 18:39:41 +03:00
local root entrypoint os_release_source
2023-05-17 14:57:37 +03:00
root = " $( mktemp -d /var/lib/machines/testsuite-13.os-release.XXX) "
2023-05-17 20:10:55 +03:00
create_dummy_container " $root "
2023-05-12 18:39:41 +03:00
entrypoint = " $root /entrypoint.sh "
cat >" $entrypoint " <<\E OF
2023-05-17 20:10:55 +03:00
#!/usr/bin/bash -ex
2023-05-12 18:39:41 +03:00
. /tmp/os-release
[ [ -n " ${ ID :- } " && " $ID " != " $container_host_id " ] ] && exit 1
[ [ -n " ${ VERSION_ID :- } " && " $VERSION_ID " != " $container_host_version_id " ] ] && exit 1
[ [ -n " ${ BUILD_ID :- } " && " $BUILD_ID " != " $container_host_build_id " ] ] && exit 1
[ [ -n " ${ VARIANT_ID :- } " && " $VARIANT_ID " != " $container_host_variant_id " ] ] && exit 1
cd /tmp
( cd /run/host && md5sum os-release) | md5sum -c
EOF
chmod +x " $entrypoint "
os_release_source = "/etc/os-release"
if [ [ ! -r " $os_release_source " ] ] ; then
os_release_source = "/usr/lib/os-release"
elif [ [ -L " $os_release_source " ] ] ; then
# Ensure that /etc always wins if available
cp --remove-destination -fv /usr/lib/os-release /etc/os-release
echo MARKER = 1 >>/etc/os-release
fi
systemd-nspawn --register= no \
--directory= " $root " \
--bind= " $os_release_source :/tmp/os-release " \
" ${ entrypoint ## " $root " } "
if grep -q MARKER /etc/os-release; then
ln -svrf /usr/lib/os-release /etc/os-release
fi
rm -fr " $root "
}
2023-05-17 14:57:37 +03:00
testcase_machinectl_bind( ) {
2023-05-12 18:39:41 +03:00
local service_path service_name root container_name ec
2023-05-17 20:10:55 +03:00
local cmd = 'for i in $(seq 1 20); do if test -f /tmp/marker; then exit 0; fi; sleep .5; done; exit 1;'
2023-05-12 18:39:41 +03:00
2023-05-17 14:57:37 +03:00
root = " $( mktemp -d /var/lib/machines/testsuite-13.machinectl-bind.XXX) "
2023-05-17 20:10:55 +03:00
create_dummy_container " $root "
container_name = " $( basename " $root " ) "
2023-05-12 18:39:41 +03:00
service_path = " $( mktemp /run/systemd/system/nspawn-machinectl-bind-XXX.service) "
service_name = " ${ service_path ##*/ } "
cat >" $service_path " <<EOF
[ Service]
Type = notify
2023-05-17 20:10:55 +03:00
ExecStart = systemd-nspawn --directory= " $root " --notify-ready= no /usr/bin/bash -xec " $cmd "
2023-05-12 18:39:41 +03:00
EOF
systemctl daemon-reload
systemctl start " $service_name "
touch /tmp/marker
machinectl bind --mkdir " $container_name " /tmp/marker
timeout 10 bash -c " while [[ '\$(systemctl show -P SubState $service_name )' == running ]]; do sleep .2; done "
ec = " $( systemctl show -P ExecMainStatus " $service_name " ) "
2023-05-17 20:10:55 +03:00
systemctl stop " $service_name "
2023-05-12 18:39:41 +03:00
rm -fr " $root " " $service_path "
return " $ec "
}
2023-05-17 14:57:37 +03:00
testcase_selinux( ) {
2023-05-12 18:39:41 +03:00
# Basic test coverage to avoid issues like https://github.com/systemd/systemd/issues/19976
if ! command -v selinuxenabled >/dev/null || ! selinuxenabled; then
echo >& 2 "SELinux is not enabled, skipping SELinux-related tests"
return 0
fi
local root
2023-05-17 14:57:37 +03:00
root = " $( mktemp -d /var/lib/machines/testsuite-13.selinux.XXX) "
2023-05-17 20:10:55 +03:00
create_dummy_container " $root "
2023-05-12 18:39:41 +03:00
chcon -R -t container_t " $root "
systemd-nspawn --register= no \
--boot \
--directory= " $root " \
--selinux-apifs-context= system_u:object_r:container_file_t:s0:c0,c1 \
--selinux-context= system_u:system_r:container_t:s0:c0,c1
rm -fr " $root "
}
2023-05-17 14:57:37 +03:00
testcase_ephemeral_config( ) {
2023-05-12 18:39:41 +03:00
# https://github.com/systemd/systemd/issues/13297
local root container_name
2023-05-17 14:57:37 +03:00
root = " $( mktemp -d /var/lib/machines/testsuite-13.ephemeral-config.XXX) "
2023-05-17 20:10:55 +03:00
create_dummy_container " $root "
2023-05-17 14:57:37 +03:00
container_name = " $( basename " $root " ) "
2023-05-12 18:39:41 +03:00
mkdir -p /run/systemd/nspawn/
2023-05-17 22:49:20 +03:00
rm -f " /etc/systemd/nspawn/ $container_name .nspawn "
2023-05-12 18:39:41 +03:00
cat >" /run/systemd/nspawn/ $container_name .nspawn " <<EOF
[ Files]
2023-05-17 22:49:20 +03:00
${ COVERAGE_BUILD_DIR : + " Bind= $COVERAGE_BUILD_DIR " }
2023-05-12 18:39:41 +03:00
BindReadOnly = /tmp/ephemeral-config
EOF
touch /tmp/ephemeral-config
systemd-nspawn --register= no \
--directory= " $root " \
--ephemeral \
2023-05-17 20:10:55 +03:00
bash -x -c "test -f /tmp/ephemeral-config"
2023-05-12 18:39:41 +03:00
systemd-nspawn --register= no \
--directory= " $root " \
--ephemeral \
--machine= foobar \
2023-05-17 20:10:55 +03:00
bash -x -c "! test -f /tmp/ephemeral-config"
2023-05-12 18:39:41 +03:00
2023-05-17 23:36:07 +03:00
rm -fr " $root " " /run/systemd/nspawn/ $container_name .nspawn "
2023-05-12 18:39:41 +03:00
}
matrix_run_one( ) {
local cgroupsv2 = " ${ 1 : ? } "
local use_cgns = " ${ 2 : ? } "
local api_vfs_writable = " ${ 3 : ? } "
local root
if [ [ " $cgroupsv2 " = = "yes" && " $IS_CGROUPSV2_SUPPORTED " = = "no" ] ] ; then
echo >& 2 "Unified cgroup hierarchy is not supported, skipping..."
return 0
fi
if [ [ " $use_cgns " = = "yes" && " $IS_CGNS_SUPPORTED " = = "no" ] ] ; then
echo >& 2 "CGroup namespaces are not supported, skipping..."
return 0
fi
root = " $( mktemp -d " /var/lib/machines/testsuite-13.unified- $1 -cgns- $2 -api-vfs-writable- $3 .XXX " ) "
2023-05-17 20:10:55 +03:00
create_dummy_container " $root "
2023-05-12 18:39:41 +03:00
SYSTEMD_NSPAWN_UNIFIED_HIERARCHY = " $cgroupsv2 " SYSTEMD_NSPAWN_USE_CGNS = " $use_cgns " SYSTEMD_NSPAWN_API_VFS_WRITABLE = " $api_vfs_writable " \
systemd-nspawn --register= no \
--directory= " $root " \
--boot
SYSTEMD_NSPAWN_UNIFIED_HIERARCHY = " $cgroupsv2 " SYSTEMD_NSPAWN_USE_CGNS = " $use_cgns " SYSTEMD_NSPAWN_API_VFS_WRITABLE = " $api_vfs_writable " \
systemd-nspawn --register= no \
--directory= " $root " \
--private-network \
--boot
if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY = " $cgroupsv2 " SYSTEMD_NSPAWN_USE_CGNS = " $use_cgns " SYSTEMD_NSPAWN_API_VFS_WRITABLE = " $api_vfs_writable " \
systemd-nspawn --register= no \
--directory= " $root " \
--private-users= pick \
--boot; then
[ [ " $IS_USERNS_SUPPORTED " = = "yes" && " $api_vfs_writable " = = "network" ] ] && return 1
else
[ [ " $IS_USERNS_SUPPORTED " = = "no" && " $api_vfs_writable " = "network" ] ] && return 1
fi
if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY = " $cgroupsv2 " SYSTEMD_NSPAWN_USE_CGNS = " $use_cgns " SYSTEMD_NSPAWN_API_VFS_WRITABLE = " $api_vfs_writable " \
systemd-nspawn --register= no \
--directory= " $root " \
--private-network \
--private-users= pick \
--boot; then
[ [ " $IS_USERNS_SUPPORTED " = = "yes" && " $api_vfs_writable " = = "yes" ] ] && return 1
else
[ [ " $IS_USERNS_SUPPORTED " = = "no" && " $api_vfs_writable " = "yes" ] ] && return 1
fi
local netns_opt = "--network-namespace-path=/proc/self/ns/net"
local net_opt
local net_opts = (
"--network-bridge=lo"
"--network-interface=lo"
"--network-ipvlan=lo"
"--network-macvlan=lo"
"--network-veth"
"--network-veth-extra=lo"
"--network-zone=zone"
)
# --network-namespace-path and network-related options cannot be used together
for net_opt in " ${ net_opts [@] } " ; do
echo " $netns_opt in combination with $net_opt should fail "
if SYSTEMD_NSPAWN_UNIFIED_HIERARCHY = " $cgroupsv2 " SYSTEMD_NSPAWN_USE_CGNS = " $use_cgns " SYSTEMD_NSPAWN_API_VFS_WRITABLE = " $api_vfs_writable " \
systemd-nspawn --register= no \
--directory= " $root " \
--boot \
" $netns_opt " \
" $net_opt " ; then
echo >& 2 "unexpected pass"
return 1
fi
done
# allow combination of --network-namespace-path and --private-network
SYSTEMD_NSPAWN_UNIFIED_HIERARCHY = " $cgroupsv2 " SYSTEMD_NSPAWN_USE_CGNS = " $use_cgns " SYSTEMD_NSPAWN_API_VFS_WRITABLE = " $api_vfs_writable " \
systemd-nspawn --register= no \
--directory= " $root " \
--boot \
--private-network \
" $netns_opt "
# test --network-namespace-path works with a network namespace created by "ip netns"
ip netns add nspawn_test
netns_opt = "--network-namespace-path=/run/netns/nspawn_test"
SYSTEMD_NSPAWN_UNIFIED_HIERARCHY = " $cgroupsv2 " SYSTEMD_NSPAWN_USE_CGNS = " $use_cgns " SYSTEMD_NSPAWN_API_VFS_WRITABLE = " $api_vfs_writable " \
systemd-nspawn --register= no \
--directory= " $root " \
--network-namespace-path= /run/netns/nspawn_test \
2023-05-17 20:10:55 +03:00
ip a | grep -v -E '^1: lo.*UP'
2023-05-12 18:39:41 +03:00
ip netns del nspawn_test
rm -fr " $root "
return 0
}
2023-11-03 11:17:48 +03:00
testcase_check_os_release( ) {
# https://github.com/systemd/systemd/issues/29185
local base common_opts root
base = " $( mktemp -d /var/lib/machines/testsuite-13.check_os_release_base.XXX) "
root = " $( mktemp -d /var/lib/machines/testsuite-13.check_os_release.XXX) "
create_dummy_container " $base "
cp -d " $base " /{ bin,sbin,lib,lib64} " $root / "
common_opts = (
--boot
--register= no
--directory= " $root "
--bind-ro= " $base /usr:/usr "
)
# Empty /etc/ & /usr/
( ! systemd-nspawn " ${ common_opts [@] } " )
( ! SYSTEMD_NSPAWN_CHECK_OS_RELEASE = 1 systemd-nspawn " ${ common_opts [@] } " )
( ! SYSTEMD_NSPAWN_CHECK_OS_RELEASE = foo systemd-nspawn " ${ common_opts [@] } " )
SYSTEMD_NSPAWN_CHECK_OS_RELEASE = 0 systemd-nspawn " ${ common_opts [@] } "
# Empty /usr/ + a broken /etc/os-release -> /usr/os-release symlink
ln -svrf " $root /etc/os-release " " $root /usr/os-release "
( ! systemd-nspawn " ${ common_opts [@] } " )
( ! SYSTEMD_NSPAWN_CHECK_OS_RELEASE = 1 systemd-nspawn " ${ common_opts [@] } " )
SYSTEMD_NSPAWN_CHECK_OS_RELEASE = 0 systemd-nspawn " ${ common_opts [@] } "
rm -fr " $root " " $base "
}
2023-05-22 13:39:25 +03:00
run_testcases
2023-05-12 18:39:41 +03:00
for api_vfs_writable in yes no network; do
matrix_run_one no no $api_vfs_writable
matrix_run_one yes no $api_vfs_writable
matrix_run_one no yes $api_vfs_writable
matrix_run_one yes yes $api_vfs_writable
done