mirror of
https://github.com/troglobit/finit.git
synced 2026-10-01 05:22:48 +07:00
Fix #492: add per-service directories, systemd RuntimeDirectory style
A service that drops privileges cannot create its own PID file in /run, root owns it. Finit can create the file with pidfile-create, but the daemon still cannot touch it to confirm a SIGHUP. Five new settings, block format only: runtime-dir, state-dir, cache-dir, logs-dir, and config-dir. The value is a directory name, resolved under /run, /var/lib, /var/cache, /var/log, and /etc, respectively. The directory is created before the service starts, mode 0755 owned by user/group, and the full path is exported to the process as RUNTIME_DIRECTORY, STATE_DIRECTORY, CACHE_DIRECTORY, LOGS_DIRECTORY, and CONFIGURATION_DIRECTORY. Mode and ownership are asserted at creation only, a daemon may tighten them afterwards. The runtime directory is removed when the unit stops, after any exec-stop-post script, like systemd with RuntimeDirectoryPreserve=no. A completed run/task counts as stopped unless remain-after-exit keeps it up. The other four persist across restarts. These are the first settings with no legacy token: they are validated by service_set_dir() and stored on the svc that service_register() now returns. systemd accepts a list of directories per setting; this is a single name for now, widening later is compatible since libconfuse accepts a bare value for a list option. The test sysroot gains libnss_files.so.2, which ldd cannot see, glibc dlopen()s it. Without it getpwnam() fails inside the chroot, so user/group settings never resolved and directory ownership could not be tested. Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
@@ -32,6 +32,7 @@ EXTRA_DIST += add-remove-dynamic-service-sub-config.sh
|
||||
EXTRA_DIST += bootstrap-crash.sh
|
||||
EXTRA_DIST += cond-start-task.sh
|
||||
EXTRA_DIST += conf-format.sh
|
||||
EXTRA_DIST += conf-dirs.sh
|
||||
EXTRA_DIST += conf-if.sh
|
||||
EXTRA_DIST += conf-template.sh
|
||||
EXTRA_DIST += script-timeout.sh
|
||||
@@ -80,6 +81,7 @@ TESTS += add-remove-dynamic-service-sub-config.sh
|
||||
TESTS += bootstrap-crash.sh
|
||||
TESTS += cond-start-task.sh
|
||||
TESTS += conf-format.sh
|
||||
TESTS += conf-dirs.sh
|
||||
TESTS += conf-if.sh
|
||||
TESTS += conf-template.sh
|
||||
TESTS += script-timeout.sh
|
||||
|
||||
Executable
+80
@@ -0,0 +1,80 @@
|
||||
#!/bin/sh
|
||||
# Verify the per-service directory settings: runtime-dir, state-dir,
|
||||
# cache-dir, logs-dir, and config-dir. The directory is created before
|
||||
# the service starts, owned by the service user, and exported to the
|
||||
# environment. The runtime directory is removed again when the service
|
||||
# stops, the others persist.
|
||||
set -eu
|
||||
|
||||
TEST_DIR=$(dirname "$0")
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
BOOTSTRAP="service owned {
|
||||
runlevel = \"S12345\"
|
||||
user = \"daemon\"
|
||||
group = \"daemon\"
|
||||
runtime-dir = \"owned\"
|
||||
state-dir = \"owned\"
|
||||
pidfile = \"/run/owned/serv.pid\"
|
||||
command = \"/sbin/serv -np -P /run/owned/serv.pid -i owned -e STATE_DIRECTORY:/var/lib/owned\"
|
||||
}
|
||||
task probe {
|
||||
runlevel = \"S12345\"
|
||||
runtime-dir = \"probe\"
|
||||
cache-dir = \"probe\"
|
||||
command = \"/sbin/serv -h -e RUNTIME_DIRECTORY:/run/probe -e CACHE_DIRECTORY:/var/cache/probe\"
|
||||
}
|
||||
service escape {
|
||||
runlevel = \"S12345\"
|
||||
runtime-dir = \"../escape\"
|
||||
command = \"serv -np -i escape\"
|
||||
}"
|
||||
|
||||
# ls output is empty for an empty directory, so test -d instead
|
||||
assert_dir()
|
||||
{
|
||||
assert "Directory $1 exists" "$(texec test -d "$1" && echo yes)" = "yes"
|
||||
}
|
||||
|
||||
assert_nodir()
|
||||
{
|
||||
assert "Directory $1 removed" "$(texec test -d "$1" || echo gone)" = "gone"
|
||||
}
|
||||
|
||||
assert_owner()
|
||||
{
|
||||
assert "$1 owned by $2" "$(texec stat -c %U:%G "$1")" = "$2"
|
||||
}
|
||||
|
||||
# shellcheck source=/dev/null
|
||||
. "$TEST_DIR/lib/setup.sh"
|
||||
|
||||
say 'Directory created before start, owned by the service user'
|
||||
retry 'assert_status owned running'
|
||||
assert_dir /run/owned
|
||||
assert_owner /run/owned daemon:daemon
|
||||
assert_dir /var/lib/owned
|
||||
assert_owner /var/lib/owned daemon:daemon
|
||||
|
||||
say 'The paths are exported to the process environment; both commands'
|
||||
say 'verify their own with serv -e, and refuse to run on a mismatch'
|
||||
|
||||
say 'A completed task no longer holds its runtime directory'
|
||||
retry 'assert_status probe done'
|
||||
assert_nodir /run/probe
|
||||
assert_dir /var/cache/probe
|
||||
|
||||
say 'Stopping the service removes the runtime directory, state persists'
|
||||
run "initctl stop owned"
|
||||
retry 'assert_status owned stopped'
|
||||
assert_nodir /run/owned
|
||||
assert_dir /var/lib/owned
|
||||
|
||||
say 'Starting again recreates it'
|
||||
run "initctl start owned"
|
||||
retry 'assert_status owned running'
|
||||
assert_dir /run/owned
|
||||
|
||||
say 'A path escaping the base directory is refused, service still runs'
|
||||
retry 'assert_status escape running'
|
||||
assert_nodir /escape
|
||||
+6
-1
@@ -34,7 +34,12 @@ BBBIN = busybox-$(ARCH)
|
||||
BBHOME ?= https://github.com/troglobit/busybox-builder/releases/download
|
||||
BBURL ?= $(BBHOME)/$(BBVER)/$(BBBIN)
|
||||
|
||||
_libs_src = $(shell ldd $(FINITBIN) | grep -Eo '/[^ ]+')
|
||||
# glibc dlopen()s NSS modules at runtime, so ldd does not list them, but
|
||||
# without libnss_files getpwnam() cannot resolve users inside the chroot
|
||||
_libs_nss = $(firstword $(wildcard /lib/$(ARCH)-linux-gnu/libnss_files.so.2 \
|
||||
/usr/lib/$(ARCH)-linux-gnu/libnss_files.so.2 \
|
||||
/lib64/libnss_files.so.2 /lib/libnss_files.so.2))
|
||||
_libs_src = $(shell ldd $(FINITBIN) | grep -Eo '/[^ ]+') $(_libs_nss)
|
||||
libs = $(foreach path,$(_libs_src),$(abspath $(DEST))$(path))
|
||||
|
||||
all: $(libs) $(DEST)/bin/$(BBBIN)
|
||||
|
||||
Reference in New Issue
Block a user