On Mon, 26 Jun 2023 at 15:03:51 +0900, Simon Richter wrote: > On 6/25/23 23:15, Mark Hindley wrote: > > The most recent proposal[6] for updating the Policy with a requirement to > > use > > tmpfiles.d(5) states > > > "Init systems other than ``systemd`` should allow providing the same > > functionality as appropriate for each system, for example managing the > > directories from the init script shipped by the package." > > The way I see it, we are getting a split between "daemons" and "services", > and it simply does not make sense to start a "service" directly from an init > script, because it requires the service orchestrator as its runtime > environment.
That's certainly not how I understand the difference between a service and a daemon. The LSB specification refers to the functionality provided by a LSB init script as a service: this is not a new term introduced by systemd, and daemons that need some one-time setup before the actual daemon can be started are not a new concept either. It's true that a lot of system services are a single daemon (long-running background process) or a wrapper around a daemon, maybe with some extra setup/teardown: for example /etc/init.d/dbus has always needed to ensure that the machine ID exists before it can start dbus-daemon, and various other init scripts need to create a directory in /run or whatever, owned by the user ID with which they are going to start the actual daemon. In systemd jargon, the init script is a "forking" service (it forks in order to run the daemon process in the background, or instructs the daemon process to use the double-fork pattern). Other system services have no daemons at all: /etc/init.d/sudo and /etc/init.d/apparmor do some one-time actions during boot, and then exit, leaving no processes running (assuming they're working correctly). In systemd jargon they're "oneshot" services. A somewhat rarer category of system services involves a top-level init script or systemd unit that results in multiple daemons being started, for example /etc/init.d/postfix. > "If it is present, it also needs to work." sounds like a reasonable > statement though. I'm fairly sure that's the intention here. The typical case where a daemon has a functional dependency on tmpfiles.d(5) is that the daemon needs some state directories, either on disk (typically /var/lib) or in RAM (typically /run), to be set up as root and chown'd to the appropriate user ID before it can do its work. For example /usr/sbin/foo might run as user ID _foo and require /run/foo and/or /var/lib/foo to be 0750 _foo:_foo. Historically a lot of daemons have required being started as root and then dropped privileges internally, allowing the daemon to set up those directories while it is still root. However, not all daemon maintainers will want their daemon implementation to be responsible for doing that: when touching a non-root-owned directory as root, it's necessary to be careful to avoid time-of-check/time-of-use attacks (perhaps involving symlinks or hard links) that could allow a local attacker to trick the daemon into changing the ownership or permissions of the wrong file. Not wanting to drop privileges internally at all is also a design choice for the daemon, and a valid one IMO: as well as the security impact of any bugs that might exist in the privilege-dropping code, if a process started as root and dropped privileges to some other uid, that permanently restricts its ability to read information out of its own /proc, which is not always desirable. If the daemon starts up unprivileged, then it can read its own /proc in the usual way, but obviously it can't create a new subdirectory of /run or /var/lib itself, so someone else needs to do that. If a daemon needs its state directories set up in advance like that, and if someone provides an init script, I hope it's uncontroversial to say that the init script needs to take responsibility for creating those state directories, either directly or by delegating it to a dependency. That's equally true regardless of whether the init script is provided by the same package as the daemon, or by some other package like orphan-sysvinit-scripts, or by a local sysadmin. One way to implement pre-creation of state directories is to use the declarative tmpfiles.d(5) files. On systemd systems, the package providing the service can rely on systemd to process those files. On non-systemd-booted systems, one option is to run on the systemd-tmpfiles executable (from the systemd-standalone-tmpfiles or systemd package) during boot and during postinst to achieve the same thing. Another option is to use a reimplementation of the tmpfiles.d(5) interface; a third option is to open-code the directory creation in the init script with some appropriate `mkdir -p`, `chmod` and `chown`, or `install -d`. Looking at reimplementations of tmpfiles.d(5), OpenRC's opentmpfiles exists as an upstream project, but was removed from Debian, and my understanding is that it has some security concerns: there are various time-of-check/time-of-use attacks available to a malicious local user when tmpfiles.d implementations act on the contents of a non-root-owned directory, which systemd-tmpfiles avoids by using the "at" family of syscalls (fchownat() and so on), but it's difficult for a shell script reimplementation to do the same. An implementation in terms of `install -d` or `mkdir -p` might well suffer from similar attacks in the general case, but hopefully the subset of the tmpfiles.d interface used by most services is sufficiently small that those attacks can be avoided for specific services. smcv