'Broken by design: systemd' is itself kind of broken

February 14, 2014

Recently, an article by Rich Felker called Broken by design: systemd has been making the rounds. While I am sympathetic with complaints about systemd, the problem is that this article is both more or less deliberately misleading and factually wrong in various of its sections. Normally I would pass over this (per the lesson of the famous xkcd strip), but not today for various reasons. I'll be quoting from the article to comment on specific issues I have with it.

(To hopefully avoid possible misunderstandings, I've written up my overall views of systemd and put them in a sidebar at the bottom of this entry.)

Felker more or less opens with:

My view is that this idea is wrong: systemd is broken by design, and despite offering highly enticing improvements over legacy init systems, it also brings major regressions in terms of many of the areas Linux is expected to excel: security, stability, and not having to reboot to upgrade your system.

To start with, when Felker talks about 'broken by design' and 'major regressions' he means both in a theoretical or philosophical sense; in other words he objects to how systemd is designed and feels that it is a bad idea. He does not point out anything that systemd fails at, can't do, or does wrong today in actual use. In practice systems running systemd have not been less secure or less stable and do not have to reboot to upgrade any more (or less) than non-systemd Linux systems do.

(Desktop Linux systems have increasingly been wanting to reboot after upgrades but this is driven by factors independent from systemd.)

On a hardened system without systemd, you have at most one root-privileged process with any exposed surface: sshd. Everything else is either running as unprivileged users or does not have any channel for providing it input except local input from root. Using systemd then more than doubles the attack surface.

Unfortunately this is false on a modern Linux system unless part of Felker's hardening involves disabling DBus and then fixing everything that stops working as a result of that. Any Linux system using DBus has a DBus daemon running as root, whether it is part of systemd or not, and that is a significant and user-accessible exposed surface (although only to local users). It may also expose DBus APIs for other root processes such as udev stuff.

(My understanding is that DBus has become essentially mandatory because udev wants to talk to it to broadcast hotplug events. Udev itself is deeply entwined in the modern Linux boot process to the point where removing it is less 'hardening your system' and more 'creating a new Linux distribution'.)

Update: I'm less and less confident of my understanding of how udev and DBus are linked to each other and how DBus runs. I may be wrong here about how necessary DBus is for udev and the security implications of DBus; this would mean that I'm wrong here and systemd offering DBus services is a real new exposure.

This increased and unreasonable risk is not inherent to systemd's goal of fixing legacy init. However it is inherent to the systemd design philosophy of putting everything into the init process.

I disagree with this view because I feel that a great deal of the increased attack surface systemd exposes is inherent in a number of core design decisions. Systemd is an active supervising init, so you must be able to somehow tell it to manipulate services (and load information about new ones). It holds service state in memory instead of trying to write status files on disk and keep them in sync; this implies you need a way of querying that service state. Systemd has further decided that unprivileged users can query that state, which means that unprivileged users can talk to it in general.

While systemd uses DBus for most or all of this I think that there is a serious argument that it is better to use a general core facility that a lot of people are paying a lot of attention to rather than reinvent the wheel on your own. A lot of people are worrying about the security and integrity of DBus and DBus libraries, many more than would be worrying about a systemd-specific protocol and set of message encoding and decoding code.

Unfortunately, by moving large amounts of functionality that's likely to need to be upgraded into PID 1, systemd makes it impossible to upgrade without rebooting. [...]

As Felker later admits, this is somewhere between 'factually incorrect' and 'aggressively misleading'. Systemd can and does serialize its state and re-exec itself during upgrades, and in practice this works reliably. My machines have upgraded systemd repeatedly without any kernel reboots involved (and this includes upgrades as drastic as Fedora version upgrades, eg from Fedora 19 to Fedora 20; yes I rebooted afterwards, but systemd was upgraded before then).

Yes, there are theoretical failure modes of this (as Felker agonizes about). I have a number of views on this but the simple version is that this problem exists in any other init system (most of which have been re-execing themselves on upgrades for years) and for any number of important system daemons as well as init. For example, if sshd fails to restart during an upgrade many servers are just as screwed as if init dies.

Felker also raises the issue of compatibility problems with the serialized state between an old and a new version. If it happened, this would be a distribution bug; when a distribution ships any upgrade it's that distribution's responsibility to make sure that the upgrade is compatible and won't make an upgraded system explode. Distributions have failed at this without systemd, but this is not a failure of what they are packaging, it is a failure of the distribution and their processes.

  • Many of the selling-point features of systemd are server-oriented. State-of-the-art transaction-style handling of daemon starting and stopping is not a feature that's useful on desktop systems. The intended audience for that sort of thing is clearly servers.

If you read the systemd design documents, this is clearly incorrect. One of systemd's explicit goals is to not start daemons on desktop systems until they're needed, especially heavyweight daemons like CUPS. If anything this is a drawback on servers, where people like me want to know right away on reboot if something is not going to work a day from now when someone tries to use it for the first time.

(Systemd's fast boot time due to starting services in parallel and various other tricks is also primarily a desktop advantage in my opinion, with perhaps a sideline in cloud virtual instances. Physical servers reboot infrequently and their boot is often drastically slowed down by the firmware's burning need to lovingly fondle ever bit of hardware in sight. Not that I'm grumpy about it or anything.)

  • The desktop is quickly becoming irrelevant. The future platform is going to be mobile and is going to be dealing with the reality of running untrusted applications. While the desktop made the unix distinction of local user accounts largely irrelevant, the coming of mobile app ecosystems full of potentially-malicious apps makes "local security" more important than ever.

The systemd developers disagree about the future irrelevance of the desktop, as do I. Beyond that, systemd has a significant amount of support for running services and other things in confined environments via use of Linux cgroups, something that is highly useful on both servers (for running daemons in lesser-privileged environments or with strong resource limits) and on desktops and other user machines for exactly this sort of untrusted applications.

None of the things systemd "does right" are at all revolutionary. They've been done many times before. DJB's daemontools, runit, and Supervisor, among others, have solved the "legacy init is broken" problem over and over again (though each with some of their own flaws). Their failure to displace legacy sysvinit in major distributions had nothing to do with whether they solved the problem, and everything to do with marketing. [...]

I disagree with this at sufficient length that I wrote an entire entry on why systemd is winning the init wars and other things aren't. The short version is that only Upstart has even been trying to do so.

If none of [of the alternate init systems] are ready for prime time, then the folks eager to replace legacy init in their favorite distributions need to step up and either polish one of the existing solutions or write a better implementation based on the same principles. Either of these options would be a lot less work than fixing what's wrong with systemd.

The final sentence is demonstrably false. Systemd works today on a great number of machines and the alternate init systems do not. Making the alternative init systems work would be a significant amount of effort, especially if you do as Felker advocates and completely replace the current init code to shove most of what init historically has done off to new programs. What might take 'a lot less work' for alternate init systems than systemd is changing them to fit Felker's vision of how init should work, a vision that is not how things work today even in System V init.

Felker does not make it clear if he thinks that legacy init even needs to be replaced (and there is certainly a contingent of people who feel that it doesn't need to be). I feel that System V init has a number of significant issues, issues that really do make a difference when managing systems. Other people seem to share this view given that major Linux distributions have moved to adopt other init systems (first with Upstart in Ubuntu, Fedora, and RHEL, and now with a move to systemd). And going outside of Linux, Solaris's SMF is the granddaddy of drastic modern init overhauls. Clearly this is an idea that has resonated with a lot of technical people over time.

(And as Felker forthrightly says, systemd offers 'highly enticing improvements over legacy init systems'.)

Sidebar: Smaller issues in Felker's article

Among the reasons systemd wants/needs to run as PID 1 is getting parenthood of badly-behaved daemons that orphan themselves, preventing their immediate parent from knowing their PID to signal or wait on them.

This is not the case. Systemd runs parts of itself as PID 1 because that is what an init system does. Systemd actually handles badly behaved daemon processes not through noticing when they are reparented to PID 1 but through Linux cgroups, which provide accurate tracking of what service a process belongs to.

In general inheriting the parentage of badly behaved daemon processes is useless for an init system because in standard Unix the init system has no way of figuring out what (abstract) service a random process it has just inherited is associated with or otherwise where it came from. In short, inits inherit random daemon processes only because they inherit all random processes.

(Why does PID 1 inherit orphan processes as opposed to something else happening to them? The ultimate answer is 'because that's how Unix works'.)

[...] While legacy init systems basically deal with no inputs except SIGCHLD from orphaned processes exiting and manual runlevel changes performed by the administrator, [...]

This is the case much of the time on modern servers but is not historically the case. One of init's major roles over time has been handling getty processes for the console and for serial connections, a role which involves a fair amount of complexity (for instance, most inits have had rate-limiting so that a broken getty or line wouldn't eat the system). And runlevel changes are actually a subset of the more general init-managed facilities exposed in /etc/inittab in System V init.

With that said, it's completely true that systemd deals with a lot more input sources than traditional System V init. Some of this is intrinsic in being an active supervision-based init system instead of a passive one like System V init, as an active init system must have some way of telling to manipulate services.

Sidebar: My overall views of systemd

I want to summarize my view of systemd to avoid misunderstandings. First, I feel that systemd is currently the best Linux init system from a sysadmin's perspective for reasons that I mostly covered in an earlier entry on things that systemd gets right. Second, I don't think that systemd is the ultimate init system (especially the ultimate Unixy init system). Instead I see it as part of Unix's necessary experimentation and growth. System V init is not flawless and systemd is one of a number of attempts to move the state of the art in init systems forward. We'll collectively learn from this over time and either improve systemd or come up with better solutions and replace it.

Comments on this page:

By Thomas at 2014-02-15 10:38:44:

Systemd actually handles badly behaved daemon processes not through noticing when they are reparented to PID 1 but through Linux cgroups, which provide accurate tracking of what service a process belongs to.

You're both wrong. You are mostly correct, but reparenting to PID1 is still essential: systemd wants to be notified about processes that exit, in particular, if the main process of a forking service exits, it wants to change the state of the associated unit (and kill all remaining other processes of that unit). The problem is: you can only get notifications about your children - since you are PID1, everything that double-forks is reparented to you, so there is one problem solved.

In fact, a systemd --user instance will not work properly with Type=forking. Systemd even warns you about it:

systemd[1609]: foobar.service: Supervising process 30718 which is not our child. We'll most likely not notice when it exits.

Well, most of the (headless) servers don't need dbus at all. Dbus is mostly used by desktop applications. And is usually on this kind of machines (servers) where you care more about security.

By Tom Gundersen at 2014-02-16 06:07:06:

Minor nit: udev does not use dbus, at least not directly, and at least not yet. It uses netlink.

By cks at 2014-02-17 00:25:08:

In light of comments here (and elsewhere) I've become convinced that I don't know enough about DBus's security and the degree that a normal machine is exposed to it. I've updated the entry accordingly.

In terms of removing DBus: Ubuntu 12.04 LTS definitely installs and runs DBus even on headless servers, although our headless servers have some amount of X programs installed (eg xterm). I haven't tried removing the package to see what happens and how much udev explodes. RHEL 5 similarly has DBus installed and running. Both are running the system DBus daemon as a non-root user. I don't know if they have any root processes that expose DBus interfaces in a normal server install.

(I suspect that the RHEL 5 system does since it has HAL. I suspect that the Ubuntu 12.04 one does as well for similar handling of removable devices.)

By Trizt at 2014-05-11 06:54:17:

In the early days of dbus it did run as root, but nowadays dbus runs as the user who has logged in into a session which requires dbus like KDE or Gnome3 (ctwm, ssh and such will not) and recently dbus seems to become a part of systemd and more tools become dependent of systemd like polikit, udev, gnome3 and so on, the list seems to get longer and longer and soon we ain't talking about GNU/Linux but Systemd/Linux.

From the security view of point, systemd seems to have had a lot of security issues, compare this to the old init system which has hardly had any security related issues and it's something like 10 years ago something had to be fixed. Atleast that's according to my colleague. He also said that there is a kernel version systemd version dependency which can break a system if you need to hold back kernel update and forget to stop the systemd update.

For me issues with systemd is that it does break a system, I have run into two times, different distributions that an update has cuased the system incapable to boot, the systemd can't be found by the kernel or that the systemd ain't valid from the kernels point of view. No one has been able to help me to fix this, this include the distribution maintainers and systemd developers. Also another issue for me is the lack of proper documentation and hidden dependencies between services which do not in reality exist.

Sure systemd works, when it works, I have a working system at work, but I'm quite afraid it will break each time I have kernel, systemd, udev updates. At home I have a half working systemd system and two broken ones and I have a handful openrc based systems which has no issues at all with booting. So for me, systemd is a no go and I think distributions like Funtoo and Slackware are doing the right thing.

Written on 14 February 2014.
« The good and bad of the System V init system
Why comments aren't immediately visible on entries here »

Page tools: View Source, View Normal, Add Comment.
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Fri Feb 14 22:10:55 2014
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.