User=0day considered harmful in systemd
User=0day considered harmful in systemd
Posted Jul 14, 2017 12:05 UTC (Fri) by mchapman (subscriber, #66589)In reply to: User=0day considered harmful in systemd by dskoll
Parent article: User=0day considered harmful in systemd
That is actually what happens... sort of.
systemd does call getpwnam() between the fork() and execve() when launching a command. That is the precise point at which it maps a username to a UID, a GID, supplementary GIDs, etc.
But getpwnam() can *not* be called at the point at which the unit is loaded into systemd. It is perfectly fine for the user not to exist at that particular time. Consider what happens during a package installation, for instance, where the unit file is dropped into the filesystem and then, during some post-installation script, adds the user to the system. There's a window of time where the unit can be loaded without the user actually existing. Or consider the case of the user being removed from the system after the unit has been loaded into systemd... systemd has no way to know that the user no longer exists, and even if it did it can't drop the unit as the service may still be running!
And finally, there are cases where it is intended that the system user database not contain the username specified in the unit. The DynamicUser= directive and the nss-systemd module interact together to provide dynamic users that exist for the duration of a service's runtime only.
In short, systemd already does getpwnam(), and it does it precisely where it should do it.
The problem that started this whole shemozzle off is that there were certain User= values that cause the entire directive to be ignored altogether. This meant that the default value, User=root, was used instead. getpwnam() called in the child process would look that up, map it to UID 0, and run the command with that persona.
With Jędrzejewski-Szmek recently merged pull requests, the User= values that would have caused the directive to be ignored now cause the unit to fail to load completely.
Posted Jul 14, 2017 12:19 UTC (Fri)
by mchapman (subscriber, #66589)
[Link]
Actually, it'd probably be more correct to say that the default behaviour, in the absence of a User= directive, is that systemd doesn't bother changing persona at all. systemd does not assume that a "root" user exists.
Posted Jul 14, 2017 14:30 UTC (Fri)
by dskoll (subscriber, #1630)
[Link] (4 responses)
My understanding was that systemd would not call getpwnam() for a username that's numeric. Or maybe another interpretation is that it would interpret it as a UID iff getpwnam() failed. Was this understanding wrong?
Posted Jul 14, 2017 14:53 UTC (Fri)
by dskoll (subscriber, #1630)
[Link] (2 responses)
Indeed, the systemd.exec man page (Debian Stretch) says "Takes a single user or group name, or numeric ID as argument.
Well, it shouldn't. It should insist on a name and not an ID.
The use case someone mentioned for an ID (that the user name is in a remote database) is not persuasive. How does it make more sense to manage a bunch of machines with "User=4242" in the unit files rather than create a local user on each machine with that ID and use "User=foouser"? Changing the ID is just as onerous in each case.
Posted Jul 14, 2017 15:09 UTC (Fri)
by mchapman (subscriber, #66589)
[Link] (1 responses)
Well, the use case isn't supported anyway. The user must be in the system's user database -- i.e. must be able to be returned by a getpw*() call -- whether a username or a UID is used to look it up. systemd will not execute a command for a user that isn't in the user database.
> How does it make more sense to manage a bunch of machines with "User=4242" in the unit files
If I had my way I would have User= only take a username, never a UID. I still wouldn't have it allow all-digit usernames though, because that's just plain weird.
But that kind of change would be hard to introduce now. I don't know why anybody would do it, but there's probably somebody relying on User=4242 doing something useful on their systems.
Posted Jul 14, 2017 15:12 UTC (Fri)
by mchapman (subscriber, #66589)
[Link]
Ah sorry, I misread your comment.
systemd should work just fine with a remote database for users. It's just calling the standard libc functions, after all, and the user database can be in LDAP or whatever.
Posted Jul 14, 2017 15:00 UTC (Fri)
by mchapman (subscriber, #66589)
[Link]
Oh, you were talking about all-digit User= values specifically? systemd will assume that if the value in User= only consists of digits, then it should be interpreted as a UID. While spawning the child process it will attempt to get the credentials for that UID using getpwuid(). It never calls getpwnam() in this case, so if you have an all-digit username on your system the only way to reference this in User= would be to use that user's UID.
So only getpw*() call is chosen, and if it returns an error the command is not executed. It doesn't try one then the other, nor is there any "fallback" to root.
User=0day considered harmful in systemd
User=0day considered harmful in systemd
User=0day considered harmful in systemd
User=0day considered harmful in systemd
User=0day considered harmful in systemd
User=0day considered harmful in systemd