No functional changes, only moving code that is only needed in
exec_invoke, and adding new dependencies for seccomp/selinux/apparmor/pam
in meson for the sd-executor binary.
Currently we spawn services by forking a child process, doing a bunch
of work, and then exec'ing the service executable.
There are some advantages to this approach:
- quick: we immediately have access to all the enourmous amount of
state simply by virtue of sharing the memory with the parent
- easy to refactor and add features
- part of the same binary, will never be out of sync
There are however significant drawbacks:
- doing work after fork and before exec is against glibc's supported
case for several APIs we call
- copy-on-write trap: anytime any memory is touched in either parent
or child, a copy of that page will be triggered
- memory footprint of the child process will be memory footprint of
PID1, but using the cgroup memory limits of the unit
The last issue is especially problematic on resource constrained
systems where hard memory caps are enforced and swap is not allowed.
As soon as PID1 is under load, with no page out due to no swap, and a
service with a low MemoryMax= tries to start, hilarity ensues.
Add a new systemd-executor binary, that is able to receive all the
required state via memfd, deserialize it, prepare the appropriate
data structures and call exec_child.
Use posix_spawn which uses CLONE_VM + CLONE_VFORK, to ensure there is
no copy-on-write (same address space will be used, and parent process
will be frozen, until exec).
The sd-executor binary is pinned by FD on startup, so that we can
guarantee there will be no incompatibilities during upgrades.
Before this commit, $USER, $HOME, $LOGNAME and $SHELL are only
set when User= is set for the unit. For system service, this
results in different behaviors depending on whether User=root is set.
$USER always makes sense on its own, so let's set it unconditionally.
Ideally $HOME should be set too, but it causes trouble when e.g. getty
passes '-p' to login(1), which then doesn't override $HOME. $LOGNAME and
$SHELL are more like "login environments", and are generally not
suitable for system services. Therefore, a new option SetLoginEnvironment=
is also added to control the latter three variables.
Fixes#23438
Replaces #8227
When we're starting early boot services such as systemd-userdbd.service,
/tmp might not yet be mounted, so let's use a directory in /run instead
which is guaranteed to be available.
This adds a new structure UnitDefaults which embedds the various default
settings for units we maintain. We so far maintained two sets of
variables for this, one in main.c as static variables and one in the
Manager structure. This moves them into a common structure.
This is most just search/replace, i.e. very dumb refactoring.
The fact that we now use a common structure for this allows us further
refactorings later.
Inspired by the discussions on #27890
This reverts commits
- 9ae3624889
"test-execute: add tests for credentials directory with mount namespace"↲
- 94fe4cf255
"core: do not leak mount for credentials directory if mount namespace is enabled",
- 7241b9cd72
"core/credential: make setup_credentials() return path to credentials directory",
- fbaf3b23ae
"core: set $CREDENTIALS_DIRECTORY only when we set up credentials"
Before the commits, credentials directory set up on ExecStart= was kept
on e.g. ExecStop=. But, with the changes, if a service requests a
private mount namespace, the credentials directory is discarded after
ExecStart= is finished.
Let's revert the change, and find better way later.
Addresses the post-merge comment
https://github.com/systemd/systemd/pull/28787#issuecomment-1690614202.
Since kernel v5.2, open_tree() and move_mount() are added. If a service
loads or sets credentials, then let's try to clone the mount that contains
credentials with open_tree(), then mount it after a (private) mount
namespace is initialized for the service. Then, we can setup a mount for
credentials directory without leaking it to the main shared mount
namespace.
With this change, the credentials for services that request their own
private mount namespace become much much safer. And, the number of mount
events triggered by setting up credential directories can be decreased.
Unfortunately, this does not 'fix' the original issue #25527, as the
reported service does not requests private mount namespace, but the
situation should be better now.
seccomp-util.h doesn't need ifdeffing, hence don't. It has worked since
quite a while with HAVE_SECCOMP is off, hence use it everywhere.
Also drop explicit seccomp.h inclusion everywhere (which needs
HAVE_SECCOMP ifdeffery everywhere). seccomp-util.h includes it anyway,
automatically, which we can just rely on, and it deals with HAVE_SECCOMP
at one central place.
If someone reads /run/host/os-release at the exact same time it is being updated, and it
is large enough, they might read a half-written file. This is very unlikely as
os-release is typically small and very rarely changes, but it is not
impossible.
Bind mount a staging directory instead of the file, and symlink the file
into into, so that we can do atomic file updates and close this gap.
Atomic replacement creates a new inode, so existing bind mounts would
continue to see the old file, and only new services would see the new file.
The indirection via the directory allows to work around this, as the
directory is fixed and never changes so the bind mount is always valid,
and its content is shared with all existing services.
Fixes https://github.com/systemd/systemd/issues/28794
Follow-up for 3f37a82545
exec_child() is supposed to set *exit_status when returning failure.
Unfortunately, we didn't do that in two cases. The result would be:
- a bogus error message "Failed at step SUCCESS spawning foo: …",
- a bogus success exit status.
Bugs introduced in 390902012c and
ad21e542b2.
The code is reworked to add some asserts and not set exit_status in the caller
so that it's clearer (also to the compiler) that it needs to be set.
For a userns root user to be able to access the credentials, both
the uid and gid of the credentials directory have to be mapped into
the userns. Currently, the credentials directory group is root, which
we obviously do not want to map in to a userns, so let's make sure
that the credentials directory and files are owned by the service
group instead, which can generally be safely mapped into the userns.
Since we use permissions mode 0600, this shouldn't cause any change
in who is able to access the credentials.
Fixes#28747
Given that ERRNO_IS_PRIVILEGE() also matches positive values,
make sure this macro is not called with arguments that do not have
errno semantics.
In this case the arguments passed to ERRNO_IS_PRIVILEGE() are the values
returned by set_oom_score_adjust() and set_coredump_filter() which are
not expected to return any positive values, but let's be consistent
anyway and move the ERRNO_IS_PRIVILEGE() invocations to the branches
where the return values are known to be negative.
Currently for portable services we automatically add a bind mount
os-release -> /run/host/os-release. This becomes problematic for the
soft-reboot case, as it's likely that portable services will be configured
to survive it, and thus would forever keep a reference to the old host's
os-release, which would be a problem because it becomes outdated, and also
it stops the old rootfs from being garbage collected.
Create a copy when the manager starts under /run/systemd/propagate instead,
and bind mount that for all services using RootDirectory=/RootImage=, so
that on soft-reboot the content gets updated (without creating a new file,
so the existing bind mounts will see the new content too).
This expands the /run/host/os-release protocol to more services, but I
think that's a nice thing to have too.
Closes https://github.com/systemd/systemd/issues/28023
When systemd-firstboot (or any other unit which uses the tty) is started,
systemd will reset the terminal. If systemd-vconsole-setup happens to be
running at that time, it'll error out when it tries to use the vconsole fd and
gets an EIO from ioctl.
e019ea738d was the first fix. It added an
implicit ordering between units using the tty and systemd-vconsole-setup.
(The commit title is wrong. The approach was generalized, but the commit title
wasn't updated.)
Then cea32691c3 was added to restart
systemd-vconsole-setup.service from systemd-firstboot.service. This was OK,
with the ordering in place, systemd-vconsole-setup.service would wait until
systemd-firstboot.service exited. But this wasn't enough, because we want the
key mappings to be loaded immediately after systemd-firstboot writes the
config. 8eb668b9ab implemented that, but actually
reintroduced the original issue. I had to drop the ordering between the two
units because otherwise we'd deadlock, waiting from firstboot for
vconsole-setup which wouldn't start while firstboot was running.
Restarting vconsole-setup.service from systemd-firstboot.service works just
fine, but when vconsole-setup.service is started earlier, it may be interrupted
by systemd-firstboot.service.
To resolve the issue, let's take a lock around the tty device. The reset is
performed after fork, so the (short) delay should not matter too much.
In xopenat_lock() the assert on <path> is dropped so that we can call
xopenat(fd, NULL) to get a copy of the original fd.
Fixes#26908.
This reverts commit e019ea738d.
In the new approach, a lock on /dev/console will be used. This lock will solve
the issue for services which run in early boot. Services which run later are
ordered after sysinit.target, so they'll run much later anyway so this
automatic dependency is not useful. Let's remove it again to make the code
simpler.
Let's add two new helpers: mount_credentials_fs() and
credentials_fs_mount_flags(). The former mounts a file system suitable
for storing of unencrypted credentials at runtime (i.e. a ramfs or
tmpfs). The latter determines the right mount flags to use for such a
mount.
Both functions mostly just take code from execute.c, but make two
changes:
1. If the kernel supports it we'll use a tmpfs with the new "noswap"
mount option instead of ramfs. Was added in kernel 6.4, hence is very
recent, but tmpfs is so much less crappy than ramfs, hence worth it.
2. We'll set MS_NOSYMFOLLOW on the mounts if supported. These file
systems should only contain regulra files, hence no need to allow
symlinks.
When the credential dir is backed by an fs that supports ACLs we must be
more careful with adjusting the 'x' bit of the directory, as any chmod()
call on the dir will reset the mask entry of the ACL entirely which we
don't want. Hence, do a manual set of ACL changes, that only add/drop
the 'x' bit but otherwise leave the ACL as it is.
This matters if we use tmpfs rather than ramfs to store credentials.
This adds support for the new XDG_STATE_HOME env var that was added to
the xdg basedir spec. Previously, because the basedir spec didn't know
the concept we'd alias the backing dir for StateDirectory= to the one
for ConfigurationDirectory= when runnin in --user mode. With this change
we'll make separate. This brings us various benefits, such as proper
"systemctl clean" support, where we can clear service state separately
from service configuration, now in user mode too.
This does not come without complications: retaining compatibility with
older setups is difficult, because we cannot possibly identitfy which
files in existing populated config dirs are actually "state" and which
one are true" configuration.
Hence let's deal with this pragmatically: if we detect that a service
that has both dirs configured only has the configuration dir existing,
then symlink the state dir to the configuration dir to retain
compatibility.
This is not great, but it's the only somewhat reasonable way out I can
see.
Fixes: #25739
Let's decouple execute.c a bit from the Manager object, let's pass the
runtime scope (i.e. the enum that discern invocation for user or system
context) as part of ExecParameters. This makes the scope available in
various functions without having to pass the Manager object in.
When resolving environment variables we currently silently resolve unset
and invalid environment variables to empty strings. Let's do this
slightly less silently: log about unset and invalid env vars, but still
resolve them to an empty string.
Fixes: #27036
Somehow the Linux kernel allows us to allocate a network namespace if we
possess CAP_SYS_ADMIN but doesn't allow us to configure it, unless we
also have CAP_NET_ADMIN.
Taking that into consideration let's avoid allocating a network
namespace we cannot even configure "lo" in.
This is common case if nspawn is invoked without userns and without
netns, because in that case it will have CAP_SYS_ADMIN but no
CAP_NET_ADMIN.
This also takes down a notch the messages about the automatic
downgrading. These have been LOG_WARNING so far, and I downgraded them
to LOG_NOTICE, since in an environment where CAP_NET_ADMIN is not
available this is really not something to be concerned about, but still
noticable. With that it's still more priorized than regular LOG_INFO.
Fixes: #27292