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
2024-08-22 02:29:10 +03:00
rm -fr /var/tmp/TEST-13-NSPAWN.*
rm -f /run/verity.d/test-13-nspawn-*.crt
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
2024-02-13 15:48:26 +03:00
# Mount temporary directory over /var/lib/machines to not pollute the image
2023-05-12 18:39:41 +03:00
mkdir -p /var/lib/machines
2024-02-13 15:48:26 +03:00
mount --bind " $( mktemp --tmpdir= /var/tmp -d) " /var/lib/machines
2023-05-12 18:39:41 +03:00
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
2024-05-11 20:17:13 +03:00
image = " $( mktemp /var/lib/machines/TEST-13-NSPAWN.image-XXX.img) "
2024-02-13 15:48:26 +03:00
dd if = /dev/zero of = " $image " bs = 1M count = 256
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=
2024-05-11 20:17:13 +03:00
root = " $( mktemp -u -d /var/lib/machines/TEST-13-NSPAWN.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 "
2024-09-06 06:39:58 +03:00
# volatile=overlay: tmpfs overlay is mounted over rootfs
2023-05-14 19:24:33 +03:00
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 "
2024-09-05 08:12:20 +03:00
# --volatile= with -U
touch " $root /usr/has-usr "
# volatile(=yes): rootfs is tmpfs, /usr/ from the OS tree is mounted read only
systemd-nspawn --directory= " $root " \
--volatile \
-U \
bash -xec 'test -e /usr/has-usr; touch /usr/read-only && exit 1; touch /nope'
test ! -e " $root /nope "
test ! -e " $root /usr/read-only "
systemd-nspawn --directory= " $root " \
--volatile= yes \
-U \
bash -xec 'test -e /usr/has-usr; touch /usr/read-only && exit 1; touch /nope'
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 \
-U \
bash -xec 'test -e /usr/has-usr; mountpoint /var; touch /read-only && exit 1; touch /var/nope'
test ! -e " $root /read-only "
test ! -e " $root /var/nope "
# volatile=overlay: tmpfs overlay is mounted over rootfs
systemd-nspawn --directory= " $root " \
--volatile= overlay \
-U \
bash -xec 'test -e /usr/has-usr; touch /nope; touch /var/also-nope; touch /usr/nope-too'
test ! -e " $root /nope "
test ! -e " $root /var/also-nope "
test ! -e " $root /usr/nope-too "
2023-05-14 19:24:33 +03:00
# --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 "
2024-07-16 00:07:52 +03:00
# The useradd is important here so the user is added to /etc/passwd. If the user is not in /etc/passwd,
# bash will end up loading libnss_systemd.so which breaks when libnss_systemd.so is built with sanitizers
# as bash isn't invoked with the necessary environment variables for that.
useradd --root= " $root " --uid 1000 --user-group --create-home testuser
2023-05-17 20:10:55 +03:00
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" ]]'
2024-03-15 02:44:20 +03:00
# Combine with --user to ensure creds are still readable
systemd-nspawn --directory= " $root " \
--user= testuser \
--no-new-privileges= yes \
--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" ]]'
2023-05-19 18:27:38 +03:00
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-26 20:09:09 +03:00
local root container dev private_users wlan_names
2023-05-17 23:36:07 +03:00
mkdir -p /run/systemd/nspawn
2024-05-11 20:17:13 +03:00
root = " $( mktemp -d /var/lib/machines/TEST-13-NSPAWN.nspawn-settings.XXX) "
2023-05-17 23:36:07 +03:00
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
2024-01-26 20:09:09 +03:00
wlan_names = "wlan0 wlan1:wl-renamed1"
2024-01-17 04:55:35 +03:00
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-26 20:09:09 +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
2024-01-26 17:29:49 +03:00
#!/bin/bash
set -ex
2023-05-17 23:36:07 +03:00
2024-01-26 17:44:39 +03:00
env
2023-05-17 23:36:07 +03:00
[ [ " $1 " = = "foo bar" ] ]
[ [ " $2 " = = "bar baz" ] ]
[ [ " $USER " = = root ] ]
[ [ " $FOO " = = bar ] ]
[ [ " $BAZ " = = "hello world" ] ]
[ [ " $PWD " = = /tmp ] ]
2024-01-26 17:44:39 +03:00
[ [ " $container_uuid " = = f28f129b-5187-4b12-80a8-9421ec4b4ad4 ] ]
2023-05-17 23:36:07 +03:00
[ [ " $( 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-26 20:09:09 +03:00
if [ [ -n " ${ wlan_names :- } " ] ] ; then
cat >>" $root /entrypoint.sh " <<\E OF
ip link | grep wlan0
ip link | grep wl-renamed1
EOF
fi
2024-01-17 04:55:35 +03:00
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
2024-05-11 20:17:13 +03:00
root = " $( mktemp -d /var/lib/machines/TEST-13-NSPAWN.bind-user.XXX) "
2023-05-17 16:35:10 +03:00
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
2024-05-11 20:17:13 +03:00
root = " $( mktemp -d /var/lib/machines/TEST-13-NSPAWN.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
2024-05-11 20:17:13 +03:00
root = " $( mktemp -d /var/lib/machines/TEST-13-NSPAWN.norbind-path.XXX) "
2023-05-12 18:39:41 +03:00
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
2024-05-11 20:17:13 +03:00
root = " $( mktemp -d /var/lib/machines/TEST-13-NSPAWN.rootidmap-path.XXX) "
2023-05-12 18:39:41 +03:00
# 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
2023-11-17 10:03:57 +03:00
echo " *** wrong permissions: $permissions "
[ [ " $IS_USERNS_SUPPORTED " = = "yes" ] ] && return 1
fi
}
owneridmap_cleanup( ) {
local dir = " ${ 1 : ? } "
mountpoint -q " $dir /bind " && umount " $dir /bind "
rm -fr " $dir "
}
testcase_owneridmap( ) {
local root cmd permissions
local owner = 1000
2024-05-11 20:17:13 +03:00
root = " $( mktemp -d /var/lib/machines/TEST-13-NSPAWN.owneridmap-path.XXX) "
2023-11-17 10:03:57 +03:00
# Create ext4 image, as ext4 supports idmapped-mounts.
mkdir -p /tmp/owneridmap/bind
dd if = /dev/zero of = /tmp/owneridmap/ext4.img bs = 4k count = 2048
mkfs.ext4 /tmp/owneridmap/ext4.img
mount /tmp/owneridmap/ext4.img /tmp/owneridmap/bind
trap "owneridmap_cleanup /tmp/owneridmap/" RETURN
touch /tmp/owneridmap/bind/file
chown -R " $owner : $owner " /tmp/owneridmap/bind
# Allow users to read and execute / in order to execute binaries
chmod o+rx " $root "
create_dummy_container " $root "
# --user=
# "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:1010:1010:testuser:/:/bin/sh"
elif [ [ $1 = = initgroups ] ] ; then
echo "testuser"
fi
EOF
chmod +x " $root /bin/getent "
2024-07-16 00:07:52 +03:00
# The useradd is important here so the user is added to /etc/passwd. If the user is not in /etc/passwd,
# bash will end up loading libnss_systemd.so which breaks when libnss_systemd.so is built with sanitizers
# as bash isn't invoked with the necessary environment variables for that.
useradd --root= " $root " --uid 1010 --user-group --create-home testuser
2023-11-17 10:03:57 +03:00
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'
if ! SYSTEMD_LOG_TARGET = console \
systemd-nspawn --register= no \
--directory= " $root " \
-U \
--user= testuser \
--bind= /tmp/owneridmap/bind:/home/testuser:owneridmap \
2024-02-18 12:25:56 +03:00
${ COVERAGE_BUILD_DIR : +--bind= " $COVERAGE_BUILD_DIR " } \
2023-11-17 10:03:57 +03:00
/usr/bin/bash -c " $cmd " | & tee nspawn.out; then
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/owneridmap/bind/other_file)
if [ [ $permissions != " $owner : $owner " ] ] ; then
2023-05-12 18:39:41 +03:00
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
2024-07-05 11:27:56 +03:00
local cmd = 'echo a | ncat -U -u -w 1 /run/host/notify'
2023-05-17 20:10:55 +03:00
2024-05-11 20:17:13 +03:00
root = " $( mktemp -d /var/lib/machines/TEST-13-NSPAWN.check_notification_socket.XXX) "
2023-05-17 20:10:55 +03:00
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
2024-05-11 20:17:13 +03:00
root = " $( mktemp -d /var/lib/machines/TEST-13-NSPAWN.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
2024-05-11 20:17:13 +03:00
root = " $( mktemp -d /var/lib/machines/TEST-13-NSPAWN.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
2024-05-11 20:17:13 +03:00
root = " $( mktemp -d /var/lib/machines/TEST-13-NSPAWN.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
2024-05-11 20:17:13 +03:00
root = " $( mktemp -d /var/lib/machines/TEST-13-NSPAWN.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
2024-05-11 20:17:13 +03:00
root = " $( mktemp -d " /var/lib/machines/TEST-13-NSPAWN.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
}
2024-08-16 19:47:33 +03:00
testcase_api_vfs( ) {
local api_vfs_writable
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
}
2023-11-03 11:17:48 +03:00
testcase_check_os_release( ) {
# https://github.com/systemd/systemd/issues/29185
local base common_opts root
2024-05-11 20:17:13 +03:00
base = " $( mktemp -d /var/lib/machines/TEST-13-NSPAWN.check_os_release_base.XXX) "
root = " $( mktemp -d /var/lib/machines/TEST-13-NSPAWN.check_os_release.XXX) "
2023-11-03 11:17:48 +03:00
create_dummy_container " $base "
cp -d " $base " /{ bin,sbin,lib,lib64} " $root / "
common_opts = (
--boot
--register= no
--directory= " $root "
--bind-ro= " $base /usr:/usr "
)
2024-08-29 00:08:33 +03:00
# Might be needed to find libraries
if [ -f " $base /etc/ld.so.cache " ] ; then
common_opts += ( " --bind-ro= $base /etc/ld.so.cache:/etc/ld.so.cache " )
fi
2023-11-03 11:17:48 +03:00
# 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 "
}
2024-08-16 19:48:50 +03:00
testcase_ip_masquerade( ) {
local root
if ! command -v networkctl >/dev/null; then
echo "This test requires systemd-networkd, skipping..."
return 0
fi
systemctl unmask systemd-networkd.service
systemctl edit --runtime --stdin systemd-networkd.service --drop-in= debug.conf <<EOF
[ Service]
Environment = SYSTEMD_LOG_LEVEL = debug
EOF
systemctl start systemd-networkd.service
root = " $( mktemp -d /var/lib/machines/TEST-13-NSPAWN.ip_masquerade.XXX) "
create_dummy_container " $root "
systemd-run --unit= nspawn-hoge.service \
systemd-nspawn \
--register= no \
--directory= " $root " \
--ephemeral \
--machine= hoge \
--network-veth \
bash -x -c "ip link set host0 up; sleep 30s"
/usr/lib/systemd/systemd-networkd-wait-online -i ve-hoge --timeout 30s
# Check IPMasquerade= for ve-* and friends enabled IP forwarding.
[ [ " $( cat /proc/sys/net/ipv4/conf/all/forwarding) " = = "1" ] ]
[ [ " $( cat /proc/sys/net/ipv4/conf/default/forwarding) " = = "1" ] ]
[ [ " $( cat /proc/sys/net/ipv6/conf/all/forwarding) " = = "1" ] ]
[ [ " $( cat /proc/sys/net/ipv6/conf/default/forwarding) " = = "1" ] ]
systemctl stop nspawn-hoge.service || :
systemctl stop systemd-networkd.service
systemctl mask systemd-networkd.service
rm -fr " $root "
}
2024-08-22 02:29:10 +03:00
can_do_rootless_nspawn( ) {
# Our create_dummy_ddi() uses squashfs and openssl.
command -v mksquashfs &&
command -v openssl &&
# mountfsd must be enabled...
[ [ -S /run/systemd/io.systemd.MountFileSystem ] ] &&
# ...and have pidfd support for unprivileged operation.
systemd-analyze compare-versions " $( uname -r) " ge 6.5 &&
systemd-analyze compare-versions " $( pkcheck --version | awk '{print $3}' ) " ge 124 &&
# nsresourced must be enabled...
[ [ -S /run/systemd/userdb/io.systemd.NamespaceResource ] ] &&
# ...and must support the UserNamespaceInterface.
! ( SYSTEMD_LOG_TARGET = console varlinkctl call \
/run/systemd/userdb/io.systemd.NamespaceResource \
io.systemd.NamespaceResource.AllocateUserRange \
'{"name":"test-supported","size":65536,"userNamespaceFileDescriptor":0}' \
2>& 1 || true ) |
grep -q "io.systemd.NamespaceResource.UserNamespaceInterfaceNotSupported"
}
create_dummy_ddi( ) {
local outdir = " ${ 1 : ? } "
local container_name = " ${ 2 : ? } "
cat >" $outdir " /openssl.conf <<EOF
[ req ]
prompt = no
distinguished_name = req_distinguished_name
[ req_distinguished_name ]
C = DE
ST = Test State
L = Test Locality
O = Org Name
OU = Org Unit Name
CN = Common Name
emailAddress = test@email.com
EOF
openssl req -config " $outdir " /openssl.conf -subj= "/CN=waldo" \
-x509 -sha256 -nodes -days 365 -newkey rsa:4096 \
-keyout " ${ outdir } / ${ container_name } .key " -out " ${ outdir } / ${ container_name } .crt "
mkdir -p /run/verity.d
cp " ${ outdir } / ${ container_name } .crt " " /run/verity.d/test-13-nspawn- ${ container_name } .crt "
SYSTEMD_REPART_OVERRIDE_FSTYPE = squashfs \
systemd-repart --make-ddi= portable \
--copy-source= /usr/share/TEST-13-NSPAWN-container-template \
--certificate= " ${ outdir } / ${ container_name } .crt " \
--private-key= " ${ outdir } / ${ container_name } .key " \
" ${ outdir } / ${ container_name } .raw "
}
testcase_unpriv( ) {
if ! can_do_rootless_nspawn; then
echo "Skipping rootless test..."
return 0
fi
local tmpdir name
tmpdir = " $( mktemp -d /var/tmp/TEST-13-NSPAWN.unpriv.XXX) "
name = " unpriv- ${ tmpdir ##*. } "
trap 'rm -fr ${tmpdir@Q} || true; rm -f /run/verity.d/test-13-nspawn-${name@Q} || true' RETURN ERR
create_dummy_ddi " $tmpdir " " $name "
chown --recursive testuser: " $tmpdir "
systemd-run \
--pipe \
--uid= testuser \
--property= Delegate = yes \
-- \
systemd-nspawn --pipe --private-network --register= no --keep-unit --image= " $tmpdir / $name .raw " echo hello >" $tmpdir /stdout.txt "
echo hello | cmp " $tmpdir /stdout.txt " -
}
nspawn: enable FUSE in containers
Linux kernel v4.18 (2018-08-12) added user-namespace support to FUSE, and
bumped the FUSE version to 7.27 (see: da315f6e0398 (Merge tag
'fuse-update-4.18' of
git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/fuse, Linus Torvalds,
2018-06-07). This means that on such kernels it is safe to enable FUSE in
nspawn containers.
In outer_child(), before calling copy_devnodes(), check the FUSE version to
decide whether enable (>=7.27) or disable (<7.27) FUSE in the container. We
look at the FUSE version instead of the kernel version in order to enable FUSE
support on older-versioned kernels that may have the mentioned patchset
backported ([as requested by @poettering][1]). However, I am not sure that
this is safe; user-namespace support is not a documented part of the FUSE
protocol, which is what FUSE_KERNEL_VERSION/FUSE_KERNEL_MINOR_VERSION are meant
to capture. While the same patchset
- added FUSE_ABORT_ERROR (which is all that the 7.27 version bump
is documented as including),
- bumped FUSE_KERNEL_MINOR_VERSION from 26 to 27, and
- added user-namespace support
these 3 things are not inseparable; it is conceivable to me that a backport
could include the first 2 of those things and exclude the 3rd; perhaps it would
be safer to check the kernel version.
Do note that our get_fuse_version() function uses the fsopen() family of
syscalls, which were not added until Linux kernel v5.2 (2019-07-07); so if
nothing has been backported, then the minimum kernel version for FUSE-in-nspawn
is actually v5.2, not v4.18.
Pass whether or not to enable FUSE to copy_devnodes(); have copy_devnodes()
copy in /dev/fuse if enabled.
Pass whether or not to enable FUSE back over fd_outer_socket to run_container()
so that it can pass that to append_machine_properties() (via either
register_machine() or allocate_scope()); have append_machine_properties()
append "DeviceAllow=/dev/fuse rw" if enabled.
For testing, simply check that /dev/fuse can be opened for reading and writing,
but that actually reading from it fails with EPERM. The test assumes that if
FUSE is supported (/dev/fuse exists), then the testsuite is running on a kernel
with FUSE >= 7.27; I am unsure how to go about writing a test that validates
that the version check disables FUSE on old kernels.
[1]: https://github.com/systemd/systemd/issues/17607#issuecomment-745418835
Closes #17607
2024-08-22 02:29:10 +03:00
testcase_fuse( ) {
if [ [ " $( cat <>/dev/fuse 2>& 1) " != 'cat: -: Operation not permitted' ] ] ; then
echo "FUSE is not supported, skipping the test..."
return 0
fi
# Assume that the tests are running on a kernel that is new enough for FUSE
# to have user-namespace support; and so we should expect that nspawn
# enables FUSE. This test does not validate that the version check
# disables FUSE on old kernels.
local root
root = " $( mktemp -d /var/lib/machines/TEST-13-NSPAWN.fuse.XXX) "
create_dummy_container " $root "
# To avoid adding any complex dependencies to the test, we simply check
# that /dev/fuse can be opened for reading and writing (O_RDWR), but that
# actually reading from it fails with EPERM. This can be done with a
# simple Bash script: run `cat <>/dev/fuse` and if the EPERM error message
# comes from "bash" then we know it couldn't be opened, while if it comes
# from "cat" then we know that it was opened but not read. If we are able
# to read from the file, then this indicates that it's not a real FUSE
# device (which requires us to mount a type="fuse" filesystem with the
# option string "fd=${num}" for /dev/fuse FD before reading from it will
# return anything other than EPERM); if this happens then most likely
# nspawn didn't create the file at all and Bash "<>" simply created a new
# normal file.
#
# "cat: -: Operation not permitted" # pass the test; opened but not read
# "bash: line 1: /dev/fuse: Operation not permitted" # fail the test; could not open
# "" # fail the test; reading worked
[ [ " $( systemd-nspawn --pipe --directory= " $root " \
bash -c 'cat <>/dev/fuse' 2>& 1) " == 'cat: -: Operation not permitted' ]]
rm -fr " $root "
}
testcase_unpriv_fuse( ) {
# Same as above, but for unprivileged operation.
if [ [ " $( cat <>/dev/fuse 2>& 1) " != 'cat: -: Operation not permitted' ] ] ; then
echo "FUSE is not supported, skipping the test..."
return 0
fi
if ! can_do_rootless_nspawn; then
echo "Skipping rootless test..."
return 0
fi
local tmpdir name
tmpdir = " $( mktemp -d /var/tmp/TEST-13-NSPAWN.unpriv-fuse.XXX) "
# $name must be such that len("ns-$(id -u testuser)-nspawn-${name}-65535")
# <= 31, or nsresourced will reject the request for a namespace.
# Therefore; len($name) <= 10 bytes.
name = " ufuse- ${ tmpdir ##*. } "
trap 'rm -fr ${tmpdir@Q} || true; rm -f /run/verity.d/test-13-nspawn-${name@Q} || true' RETURN ERR
create_dummy_ddi " $tmpdir " " $name "
chown --recursive testuser: " $tmpdir "
[ [ " $( systemd-run \
--pipe \
--uid= testuser \
--property= Delegate = yes \
--setenv= SYSTEMD_LOG_LEVEL \
--setenv= SYSTEMD_LOG_TARGET \
-- \
systemd-nspawn --pipe --private-network --register= no --keep-unit --image= " $tmpdir / $name .raw " \
bash -c 'cat <>/dev/fuse' 2>& 1) " == *'cat: -: Operation not permitted' ]]
}
2024-11-14 12:08:35 +03:00
test_tun( ) {
local expect = ${ 1 ? }
local exists = ${ 2 ? }
local command command_exists command_not_exists
shift 2
command_exists = '[[ -c /dev/net/tun ]]; [[ "$(stat /dev/net/tun --format=%u)" == 0 ]]; [[ "$(stat /dev/net/tun --format=%g)" == 0 ]]'
command_not_exists = '[[ ! -e /dev/net/tun ]]'
if [ [ " $exists " = = 0 ] ] ; then
command = " $command_not_exists "
else
command = " $command_exists "
fi
systemd-nspawn " $@ " bash -xec " $command_exists "
# check if the owner of the host device is unchanged, see issue #34243.
[ [ " $( stat /dev/net/tun --format= %u) " = = 0 ] ]
[ [ " $( stat /dev/net/tun --format= %g) " = = 0 ] ]
# Without DeviceAllow= for /dev/net/tun, see issue #35116.
assert_rc \
" $expect " \
2024-11-27 19:14:24 +03:00
systemd-run --wait -p Environment = SYSTEMD_LOG_LEVEL = debug -p DevicePolicy = closed -p DeviceAllow = "char-pts rw" \
2024-11-14 12:08:35 +03:00
systemd-nspawn " $@ " bash -xec " $command "
[ [ " $( stat /dev/net/tun --format= %u) " = = 0 ] ]
[ [ " $( stat /dev/net/tun --format= %g) " = = 0 ] ]
}
testcase_dev_net_tun( ) {
local root
if [ [ ! -c /dev/net/tun ] ] ; then
echo "/dev/net/tun does not exist, skipping tests"
return 0
fi
root = " $( mktemp -d /var/lib/machines/TEST-13-NSPAWN.tun.XXX) "
create_dummy_container " $root "
test_tun 0 1 --ephemeral --directory= " $root " --private-users= no
test_tun 0 1 --ephemeral --directory= " $root " --private-users= yes
test_tun 0 0 --ephemeral --directory= " $root " --private-users= pick
test_tun 0 1 --ephemeral --directory= " $root " --private-users= no --private-network
test_tun 0 1 --ephemeral --directory= " $root " --private-users= yes --private-network
test_tun 1 0 --ephemeral --directory= " $root " --private-users= pick --private-network
rm -fr " $root "
}
2023-05-22 13:39:25 +03:00
run_testcases