[systemd-devel] Design patterns for privilege separating systemd services?
Lennart Poettering
lennart at poettering.net
Thu Feb 18 21:33:53 UTC 2021
On Mi, 17.02.21 12:50, Colin Walters (walters at verbum.org) wrote:
> I'm having a debate with the SSSD team over here around multiple
> systemd units and privilege separation:
> https://github.com/SSSD/sssd/issues/3412
>
> And we also had a related topic come up in Fedora CoreOS where we
> have a privileged service (rpm-ostreed.service) and we want a
> separate unprivileged management service (zincati.service) to be
> able to perform privileged operations; right now we install a polkit
> rule to authorize the `zincati` user, but I think that's quite
> hacky. I'd actually like to use DynamicUser=yes for zincati.
>
> I think what I want is a way to tell systemd to create something
> like a private socketpair() between the two services dynamically.
Interesting idea. Some thoughts:
1. So we have another RFE which I am very sympathetic to which is to
add an Open= setting to service unit files, which could be used to
open any kind of file at activation time and pass it via our usual
socket activation fd passing protocol over. (Usecase for that was
giving access to privileged files to unprivileged processes)
2. Now, we recently reworked a good chunk of our code that reads user
provided files off disk, so that when an AF_UNIX/SOCK_STREAM socket
in the file system is specified we detect that and automatically
connect() to it, thus having a very simple way to attach some other
service to our file reading logic, simply by placing an
AF_UNIX/SOCK_STREAM in place of the file. (example where this is
used: the password file column in /etc/crypttab, i.e. you can now
provide paspshrases easily via an IPC service that way). Now, the
idea for the Open= thing would be to include exactly this behaviour
here: if you specify Open=/run/some/unix/socket in the service unit
file, we'd connect to that path, and pass it over as any other fd.
3. Now, to circle back to your idea: how about beefing up .socket
units so that you can do "ListenStream=anonymous" or so, in which
case we'd listen on an "anonymous" AF_UNIX/SOCK_STREAM socket
(which would probably be one which we bind in some dir and
immediately delete the inode of, but keep an O_PATH fd to, so that
it may still be referenced by /proc/self/fd/ but not
otherwise). This sounds pretty useless at first, but then:
4. Beef up the Open= idea for .service units as described in step 2
above, but now in addition to absolute paths in the file system
accept a syntax where we reference arbitrary socket unit
files. Which would work both for regular sockets/fifos in the file
system, but also work for the new anonymous kind, as proposed in
step 3 above.
To put these four ideas together to implement what you want:
myserver.socket:
[Socket]
ListenStream=anyonymous
myserver.service:
[Service]
ExecStart=/usr/lib/myserverd
myclient.service:
[Service]
Open=@myserver.socket
ExecStart=/usr/bin/myclient
The above sounds simple enough, no?
Of course, there's one thing missing still: as I understand you'd
prefer a socketpair() (i.e. connected socket), instead of a listening
one to be passed to both sides. But that would be an easy extension,
i.e. add ListenStream=socketpair or so as another value, which would
then do the right thing.
I kinda like this model, I must say.
Lennart
--
Lennart Poettering, Berlin
More information about the systemd-devel
mailing list