]> git.ipfire.org Git - thirdparty/systemd.git/blob - test/units/testsuite-07.exec-context.sh
Merge pull request #30534 from yuwata/man-page-update-and-fix-typo
[thirdparty/systemd.git] / test / units / testsuite-07.exec-context.sh
1 #!/usr/bin/env bash
2 # SPDX-License-Identifier: LGPL-2.1-or-later
3 # shellcheck disable=SC2016
4 set -eux
5 set -o pipefail
6
7 # shellcheck source=test/units/util.sh
8 . "$(dirname "$0")"/util.sh
9
10 # Make sure the unit's exec context matches its configuration
11 # See: https://github.com/systemd/systemd/pull/29552
12
13 # Even though hidepid= was introduced in kernel 3.3, we support only
14 # the post 5.8 implementation that allows us to apply the option per-instance,
15 # instead of the whole namespace. To distinguish between these two implementations
16 # lets check if we can mount procfs with a named value (e.g. hidepid=off), since
17 # support for this was introduced in the same commit as the per-instance stuff
18 proc_supports_option() {
19 local option="${1:?}"
20 local proc_tmp ec
21
22 proc_tmp="$(mktemp -d)"
23 mount -t proc -o "$option" proc "$proc_tmp" && ec=0 || ec=$?
24 mountpoint -q "$proc_tmp" && umount -q "$proc_tmp"
25 rm -rf "$proc_tmp"
26
27 return $ec
28 }
29
30 # In coverage builds we disable ProtectSystem= and ProtectHome= via a service.d
31 # dropin in /etc. This dropin has, unfortunately, higher priority than
32 # the transient stuff from systemd-run. Let's just skip the following tests
33 # in that case instead of complicating the test setup even more */
34 if [[ -z "${COVERAGE_BUILD_DIR:-}" ]]; then
35 if ! systemd-detect-virt -cq && command -v bootctl >/dev/null; then
36 boot_path="$(bootctl --print-boot-path)"
37 esp_path="$(bootctl --print-esp-path)"
38
39 # If the mount points are handled by automount units, make sure we trigger
40 # them before proceeding further
41 ls -l "$boot_path" "$esp_path"
42 fi
43
44 systemd-run --wait --pipe -p ProtectSystem=yes \
45 bash -xec "test ! -w /usr; ${boot_path:+"test ! -w $boot_path; test ! -w $esp_path;"} test -w /etc; test -w /var"
46 systemd-run --wait --pipe -p ProtectSystem=full \
47 bash -xec "test ! -w /usr; ${boot_path:+"test ! -w $boot_path; test ! -w $esp_path;"} test ! -w /etc; test -w /var"
48 systemd-run --wait --pipe -p ProtectSystem=strict \
49 bash -xec "test ! -w /; test ! -w /etc; test ! -w /var; test -w /dev; test -w /proc"
50 systemd-run --wait --pipe -p ProtectSystem=no \
51 bash -xec "test -w /; test -w /etc; test -w /var; test -w /dev; test -w /proc"
52
53 MARK="$(mktemp /root/.exec-context.XXX)"
54 systemd-run --wait --pipe -p ProtectHome=yes \
55 bash -xec "test ! -w /home; test ! -w /root; test ! -w /run/user; test ! -e $MARK"
56 systemd-run --wait --pipe -p ProtectHome=read-only \
57 bash -xec "test ! -w /home; test ! -w /root; test ! -w /run/user; test -e $MARK"
58 systemd-run --wait --pipe -p ProtectHome=tmpfs \
59 bash -xec "test -w /home; test -w /root; test -w /run/user; test ! -e $MARK"
60 systemd-run --wait --pipe -p ProtectHome=no \
61 bash -xec "test -w /home; test -w /root; test -w /run/user; test -e $MARK"
62 rm -f "$MARK"
63 fi
64
65 if proc_supports_option "hidepid=off"; then
66 systemd-run --wait --pipe -p ProtectProc=noaccess -p User=testuser \
67 bash -xec 'test -e /proc/1; test ! -r /proc/1; test -r /proc/$$$$/comm'
68 systemd-run --wait --pipe -p ProtectProc=invisible -p User=testuser \
69 bash -xec 'test ! -e /proc/1; test -r /proc/$$$$/comm'
70 systemd-run --wait --pipe -p ProtectProc=ptraceable -p User=testuser \
71 bash -xec 'test ! -e /proc/1; test -r /proc/$$$$/comm'
72 systemd-run --wait --pipe -p ProtectProc=ptraceable -p User=testuser -p AmbientCapabilities=CAP_SYS_PTRACE \
73 bash -xec 'test -r /proc/1; test -r /proc/$$$$/comm'
74 systemd-run --wait --pipe -p ProtectProc=default -p User=testuser \
75 bash -xec 'test -r /proc/1; test -r /proc/$$$$/comm'
76 fi
77
78 if proc_supports_option "subset=pid"; then
79 systemd-run --wait --pipe -p ProcSubset=pid -p User=testuser \
80 bash -xec "test -r /proc/1/comm; test ! -e /proc/cpuinfo"
81 systemd-run --wait --pipe -p ProcSubset=all -p User=testuser \
82 bash -xec "test -r /proc/1/comm; test -r /proc/cpuinfo"
83 fi
84
85 if ! systemd-detect-virt -cq; then
86 systemd-run --wait --pipe -p ProtectKernelLogs=yes -p User=testuser \
87 bash -xec "test ! -r /dev/kmsg"
88 systemd-run --wait --pipe -p ProtectKernelLogs=no -p User=testuser \
89 bash -xec "test -r /dev/kmsg"
90 fi
91
92 systemd-run --wait --pipe -p BindPaths="/etc /home:/mnt:norbind -/foo/bar/baz:/usr:rbind" \
93 bash -xec "mountpoint /etc; test -d /etc/systemd; mountpoint /mnt; ! mountpoint /usr"
94 systemd-run --wait --pipe -p BindReadOnlyPaths="/etc /home:/mnt:norbind -/foo/bar/baz:/usr:rbind" \
95 bash -xec "test ! -w /etc; test ! -w /mnt; ! mountpoint /usr"
96
97 # Check if we correctly serialize, deserialize, and set directives that
98 # have more complex internal handling
99 if ! systemd-detect-virt -cq; then
100 # Funny detail: this originally used the underlying rootfs device, but that,
101 # for some reason, caused "divide error" in kernel, followed by a kernel panic
102 TEMPFILE="$(mktemp)"
103 LODEV="$(losetup --show -f "$TEMPFILE")"
104 ROOT_DEV_MAJ_MIN="$(lsblk -nro MAJ:MIN "$LODEV")"
105 EXPECTED_IO_MAX="$ROOT_DEV_MAJ_MIN rbps=1000 wbps=1000000000000 riops=2000000000 wiops=4000"
106 EXPECTED_IO_LATENCY="$ROOT_DEV_MAJ_MIN target=69000"
107 SERVICE_NAME="test-io-directives-$RANDOM.service"
108 CGROUP_PATH="/sys/fs/cgroup/system.slice/$SERVICE_NAME"
109
110 # IO*=
111 ARGUMENTS=(
112 # Throw in a couple of invalid entries just to test things out
113 -p IOReadBandwidthMax="/foo/bar 1M"
114 -p IOReadBandwidthMax="/foo/baz 1M"
115 -p IOReadBandwidthMax="$LODEV 1M"
116 -p IOReadBandwidthMax="$LODEV 1K"
117 -p IOWriteBandwidthMax="$LODEV 1G"
118 -p IOWriteBandwidthMax="$LODEV 1T"
119 -p IOReadIOPSMax="$LODEV 2G"
120 -p IOWriteIOPSMax="$LODEV 4K"
121 -p IODeviceLatencyTargetSec="$LODEV 666ms"
122 -p IODeviceLatencyTargetSec="/foo/bar 69ms"
123 -p IODeviceLatencyTargetSec="$LODEV 69ms"
124 -p IOReadBandwidthMax="/foo/bar 1M"
125 -p IOReadBandwidthMax="/foo/baz 1M"
126 # TODO: IODeviceWeight= doesn't work on loop devices and virtual disks
127 -p IODeviceWeight="$LODEV 999"
128 -p IODeviceWeight="/foo/bar 999"
129 )
130
131 systemctl set-property system.slice IOAccounting=yes
132 # io.latency not available by default on Debian stable
133 if [[ -e /sys/fs/cgroup/system.slice/io.latency ]]; then
134 systemd-run --wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \
135 bash -xec "diff <(echo $EXPECTED_IO_MAX) $CGROUP_PATH/io.max; diff <(echo $EXPECTED_IO_LATENCY) $CGROUP_PATH/io.latency"
136 fi
137
138 # CPUScheduling=
139 ARGUMENTS=(
140 -p CPUSchedulingPolicy=rr # ID: 2
141 -p CPUSchedulingPolicy=fifo # ID: 1
142 -p CPUSchedulingPriority=5 # Actual prio: 94 (99 - prio)
143 -p CPUSchedulingPriority=10 # Actual prio: 89 (99 - prio)
144 )
145
146 systemd-run --wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \
147 bash -xec 'grep -E "^policy\s*:\s*1$" /proc/self/sched; grep -E "^prio\s*:\s*89$" /proc/self/sched'
148
149 # Device*=
150 ARGUMENTS=(
151 -p DevicePolicy=closed
152 -p DevicePolicy=strict
153 -p DeviceAllow="char-mem rm" # Allow read & mknod for /dev/{null,zero,...}
154 -p DeviceAllow="/dev/loop0 rw"
155 -p DeviceAllow="/dev/loop0 w" # Allow write for /dev/loop0
156 # Everything else should be disallowed per the strict policy
157 )
158
159 systemd-run --wait --pipe --unit "$SERVICE_NAME" "${ARGUMENTS[@]}" \
160 bash -xec 'test -r /dev/null; test ! -w /dev/null; test ! -r /dev/loop0; test -w /dev/loop0; test ! -r /dev/tty; test ! -w /dev/tty'
161
162 if ! systemctl --version | grep -qF -- "-BPF_FRAMEWORK"; then
163 # SocketBind*=
164 ARGUMENTS=(
165 -p SocketBindAllow=
166 -p SocketBindAllow=1234
167 -p SocketBindAllow=ipv4:udp:any
168 -p SocketBindAllow=ipv6:6666
169 # Everything but the last assignment is superfluous, but it still exercises
170 # the parsing machinery
171 -p SocketBindDeny=
172 -p SocketBindDeny=1111
173 -p SocketBindDeny=ipv4:1111
174 -p SocketBindDeny=ipv4:any
175 -p SocketBindDeny=ipv4:tcp:any
176 -p SocketBindDeny=ipv4:udp:10000-11000
177 -p SocketBindDeny=ipv6:1111
178 -p SocketBindDeny=any
179 )
180
181 # We should fail with EPERM when trying to bind to a socket not on the allow list
182 # (nc exits with 2 in that case)
183 systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
184 bash -xec 'timeout 1s nc -l 127.0.0.1 9999; exit 42'
185 systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
186 bash -xec 'timeout 1s nc -l ::1 9999; exit 42'
187 systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
188 bash -xec 'timeout 1s nc -6 -u -l ::1 9999; exit 42'
189 systemd-run --wait -p SuccessExitStatus="1 2" --pipe "${ARGUMENTS[@]}" \
190 bash -xec 'timeout 1s nc -4 -l 127.0.0.1 6666; exit 42'
191 # Consequently, we should succeed when binding to a socket on the allow list
192 # and keep listening on it until we're killed by `timeout` (EC 124)
193 systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
194 bash -xec 'timeout 1s nc -4 -l 127.0.0.1 1234; exit 1'
195 systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
196 bash -xec 'timeout 1s nc -4 -u -l 127.0.0.1 5678; exit 1'
197 systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
198 bash -xec 'timeout 1s nc -6 -l ::1 1234; exit 1'
199 systemd-run --wait --pipe -p SuccessExitStatus=124 "${ARGUMENTS[@]}" \
200 bash -xec 'timeout 1s nc -6 -l ::1 6666; exit 1'
201 fi
202
203 losetup -d "$LODEV"
204 rm -f "$TEMPFILE"
205 fi
206
207 # {Cache,Configuration,Logs,Runtime,State}Directory=
208 ARGUMENTS=(
209 -p CacheDirectory="foo/bar/baz"
210 -p CacheDirectory="foo"
211 -p CacheDirectory="context"
212 -p CacheDirectoryMode="0123"
213 -p CacheDirectoryMode="0666"
214 -p ConfigurationDirectory="context/foo also_context/bar context/nested/baz"
215 -p ConfigurationDirectoryMode="0400"
216 -p LogsDirectory="context/foo"
217 -p LogsDirectory=""
218 -p LogsDirectory="context/a/very/nested/logs/dir"
219 -p RuntimeDirectory="context"
220 -p RuntimeDirectory="also_context"
221 -p RuntimeDirectoryPreserve=yes
222 -p StateDirectory="context"
223 -p StateDirectory="./././././././context context context"
224 -p StateDirectoryMode="0000"
225 )
226
227 rm -rf /run/context
228 systemd-run --wait --pipe "${ARGUMENTS[@]}" \
229 bash -xec '[[ $CACHE_DIRECTORY == /var/cache/context:/var/cache/foo:/var/cache/foo/bar/baz ]];
230 [[ $(stat -c "%a" ${CACHE_DIRECTORY##*:}) == 666 ]]'
231 systemd-run --wait --pipe "${ARGUMENTS[@]}" \
232 bash -xec '[[ $CONFIGURATION_DIRECTORY == /etc/also_context/bar:/etc/context/foo:/etc/context/nested/baz ]];
233 [[ $(stat -c "%a" ${CONFIGURATION_DIRECTORY##*:}) == 400 ]]'
234 systemd-run --wait --pipe "${ARGUMENTS[@]}" \
235 bash -xec '[[ $LOGS_DIRECTORY == /var/log/context/a/very/nested/logs/dir:/var/log/context/foo ]];
236 [[ $(stat -c "%a" ${LOGS_DIRECTORY##*:}) == 755 ]]'
237 systemd-run --wait --pipe "${ARGUMENTS[@]}" \
238 bash -xec '[[ $RUNTIME_DIRECTORY == /run/also_context:/run/context ]];
239 [[ $(stat -c "%a" ${RUNTIME_DIRECTORY##*:}) == 755 ]];
240 [[ $(stat -c "%a" ${RUNTIME_DIRECTORY%%:*}) == 755 ]]'
241 systemd-run --wait --pipe "${ARGUMENTS[@]}" \
242 bash -xec '[[ $STATE_DIRECTORY == /var/lib/context ]]; [[ $(stat -c "%a" $STATE_DIRECTORY) == 0 ]]'
243 test -d /run/context
244 rm -rf /var/{cache,lib,log}/context /etc/{also_,}context
245
246 # Limit*=
247 #
248 # Note: keep limits of LimitDATA= and LimitAS= unlimited, otherwise ASan (LSan)
249 # won't be able to mmap the shadow maps
250 ARGUMENTS=(
251 -p LimitCPU=15
252 -p LimitCPU=10:15 # ulimit -t
253 -p LimitFSIZE=96G # ulimit -f
254 -p LimitDATA=8T:infinity
255 -p LimitDATA=infinity # ulimit -d
256 -p LimitSTACK=8M # ulimit -s
257 -p LimitCORE=infinity
258 -p LimitCORE=17M # ulimit -c
259 -p LimitRSS=27G # ulimit -m
260 -p LimitNOFILE=7:127 # ulimit -n
261 -p LimitAS=infinity # ulimit -v
262 -p LimitNPROC=1
263 -p LimitNPROC=64:infinity # ulimit -u
264 -p LimitMEMLOCK=37M # ulimit -l
265 -p LimitLOCKS=19:1021 # ulimit -x
266 -p LimitSIGPENDING=21 # ulimit -i
267 -p LimitMSGQUEUE=666 # ulimit -q
268 -p LimitNICE=4 # ulimit -e
269 -p LimitRTPRIO=8 # ulimit -r
270 -p LimitRTTIME=666666 # ulimit -R
271 )
272 # Do all the checks in one giant inline shell blob to avoid the overhead of spawning
273 # a new service for each check
274 #
275 # Note: ulimit shows storage-related values in 1024-byte increments*
276 # Note2: ulimit -R requires bash >= 5.1
277 #
278 # * in POSIX mode -c a -f options show values in 512-byte increments; let's hope
279 # we never run in the POSIX mode
280 systemd-run --wait --pipe "${ARGUMENTS[@]}" \
281 bash -xec 'KB=1; MB=$((KB * 1024)); GB=$((MB * 1024)); TB=$((GB * 1024));
282 : CPU; [[ $(ulimit -St) -eq 10 ]]; [[ $(ulimit -Ht) -eq 15 ]];
283 : FSIZE; [[ $(ulimit -Sf) -eq $((96 * GB)) ]]; [[ $(ulimit -Hf) -eq $((96 * GB)) ]];
284 : DATA; [[ $(ulimit -Sd) == unlimited ]]; [[ $(ulimit -Hd) == unlimited ]];
285 : STACK; [[ $(ulimit -Ss) -eq $((8 * MB)) ]]; [[ $(ulimit -Hs) -eq $((8 * MB)) ]];
286 : CORE; [[ $(ulimit -Sc) -eq $((17 * MB)) ]]; [[ $(ulimit -Hc) -eq $((17 * MB)) ]];
287 : RSS; [[ $(ulimit -Sm) -eq $((27 * GB)) ]]; [[ $(ulimit -Hm) -eq $((27 * GB)) ]];
288 : NOFILE; [[ $(ulimit -Sn) -eq 7 ]]; [[ $(ulimit -Hn) -eq 127 ]];
289 : AS; [[ $(ulimit -Sv) == unlimited ]]; [[ $(ulimit -Hv) == unlimited ]];
290 : NPROC; [[ $(ulimit -Su) -eq 64 ]]; [[ $(ulimit -Hu) == unlimited ]];
291 : MEMLOCK; [[ $(ulimit -Sl) -eq $((37 * MB)) ]]; [[ $(ulimit -Hl) -eq $((37 * MB)) ]];
292 : LOCKS; [[ $(ulimit -Sx) -eq 19 ]]; [[ $(ulimit -Hx) -eq 1021 ]];
293 : SIGPENDING; [[ $(ulimit -Si) -eq 21 ]]; [[ $(ulimit -Hi) -eq 21 ]];
294 : MSGQUEUE; [[ $(ulimit -Sq) -eq 666 ]]; [[ $(ulimit -Hq) -eq 666 ]];
295 : NICE; [[ $(ulimit -Se) -eq 4 ]]; [[ $(ulimit -He) -eq 4 ]];
296 : RTPRIO; [[ $(ulimit -Sr) -eq 8 ]]; [[ $(ulimit -Hr) -eq 8 ]];
297 ulimit -R || exit 0;
298 : RTTIME; [[ $(ulimit -SR) -eq 666666 ]]; [[ $(ulimit -HR) -eq 666666 ]];'
299
300 # RestrictFileSystems=
301 #
302 # Note: running instrumented binaries requires at least /proc to be accessible, so let's
303 # skip the test when we're running under sanitizers
304 #
305 # Note: $GCOV_ERROR_LOG is used during coverage runs to suppress errors when creating *.gcda files,
306 # since gcov can't access the restricted filesystem (as expected)
307 if [[ ! -v ASAN_OPTIONS ]] && systemctl --version | grep "+BPF_FRAMEWORK" && kernel_supports_lsm bpf; then
308 ROOTFS="$(df --output=fstype /usr/bin | sed --quiet 2p)"
309 systemd-run --wait --pipe -p RestrictFileSystems="" ls /
310 systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS foo bar" ls /
311 (! systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS" ls /proc)
312 (! systemd-run --wait --pipe -p GCOV_ERROR_LOG=/dev/null -p RestrictFileSystems="foo" ls /)
313 systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS foo bar baz proc" ls /proc
314 systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS @foo @basic-api" ls /proc
315 systemd-run --wait --pipe -p RestrictFileSystems="$ROOTFS @foo @basic-api" ls /sys/fs/cgroup
316
317 systemd-run --wait --pipe -p RestrictFileSystems="~" ls /
318 systemd-run --wait --pipe -p RestrictFileSystems="~proc" ls /
319 systemd-run --wait --pipe -p RestrictFileSystems="~@basic-api" ls /
320 (! systemd-run --wait --pipe -p GCOV_ERROR_LOG=/dev/null -p RestrictFileSystems="~$ROOTFS" ls /)
321 (! systemd-run --wait --pipe -p RestrictFileSystems="~proc" ls /proc)
322 (! systemd-run --wait --pipe -p RestrictFileSystems="~@basic-api" ls /proc)
323 (! systemd-run --wait --pipe -p RestrictFileSystems="~proc foo @bar @basic-api" ls /proc)
324 (! systemd-run --wait --pipe -p RestrictFileSystems="~proc foo @bar @basic-api" ls /sys)
325 systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /
326 (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /proc)
327 (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /dev)
328 (! systemd-run --wait --pipe -p RestrictFileSystems="~proc devtmpfs sysfs" ls /sys)
329 fi
330
331 # Ensure that clean-up codepaths work correctly if activation ultimately fails
332 touch /run/not-a-directory
333 mkdir /tmp/root
334 touch /tmp/root/foo
335 chmod +x /tmp/root/foo
336 (! systemd-run --wait --pipe false)
337 (! systemd-run --wait --pipe --unit "test-dynamicuser-fail" -p DynamicUser=yes -p WorkingDirectory=/nonexistent true)
338 (! systemd-run --wait --pipe -p RuntimeDirectory=not-a-directory true)
339 (! systemd-run --wait --pipe -p RootDirectory=/tmp/root this-shouldnt-exist)
340 (! systemd-run --wait --pipe -p RootDirectory=/tmp/root /foo)
341 (! systemd-run --wait --pipe --service-type=oneshot -p ExecStartPre=-/foo/bar/baz -p ExecStart=-/foo/bar/baz -p RootDirectory=/tmp/root -- "- foo")