Re: Using s6 and s6-rc tools with an unprivileged user

From: Guillermo <gdiazhartusch_at_gmail.com>
Date: Sun, 2 Feb 2020 23:12:10 -0300

El dom., 2 feb. 2020 a las 17:14, Laurent Bercot escribi├│:
>
> >* s6-rc-db: [...]
> There shouldn't be any drawbacks. Honestly, the lock file in a
> database is only necessary because of s6-rc-bundle, which is the only
> program that can modify a compiled db at the same time it is being read;
> the intent is not for it to be a roadblock for anything. So, change its
> group to your heart's content; I should add an option to s6-rc-compile
> to do that at db creation time.

OK. An option would save an extra chgrp + chmod step. s6-rc-bundle
still fails, as expected:

$ s6-rc-db -c db list all
a-oneshot
a-longrun
s6rc-fdholder
s6rc-oneshot-runner

$ s6-rc-bundle -c db add a-bundle a-longrun
s6-rc-bundle: fatal: unable to open db/resolve.cdb.new: Permission denied

> >* s6-rc: [...]
> This is a bit more borderline, because a user can still run
> "s6-rc change" and it will attempt to change the machine state, and will
> probably yield a big batch of errors about s6-svc being forbidden to
> change a service state or s6-sudoc being unable to connect to the
> oneshot runner. Things should work exactly as they're supposed to, but
> it might be pretty ugly.

Yeah, it all works as expected; YMMV about the clarity of error messages.

$ s6-rc -ul live change a-longrun
s6-rc: fatal: unable to take locks: Permission denied

[After chgrp + chmod, and starting s6rc-oneshot-runner]
$ s6-rc -dl live list
s6rc-oneshot-runner
s6rc-fdholder
a-longrun
a-oneshot

$ s6-rc -ul live change a-oneshot
s6-sudoc: fatal: unable to connect to the s6-sudod server - check that
you have appropriate permissions
s6-rc: warning: unable to start service a-oneshot: command exited 111

$ s6-rc -ul live change a-longrun
s6-svlisten1: fatal: unable to subscribe to events for
live/servicedirs/a-longrun: Permission denied
s6-rc: warning: unable to start service a-longrun: command exited 111
s6-svc: fatal: unable to control live/servicedirs/a-longrun: Permission denied

[OK, make the 'event' fifodir publicly accessible and try again]
$ s6-rc -ul live change a-longrun
s6-svlisten1: fatal: unable to s6_svstatus_read: Permission denied
s6-rc: warning: unable to start service a-longrun: command exited 111
s6-svc: fatal: unable to control live/servicedirs/a-longrun: Permission denied

[OK, make 'supervise' group searchable and try again, see later]
$ s6-rc -ul live -t 1000 change a-longrun
s6-svc: fatal: unable to control live/servicedirs/a-longrun: Permission denied
s6-svlisten1: fatal: timed out
s6-rc: fatal: timed out

> If you don't mind ugliness, fine; but if you
> want me to make it official, I'll want to add a permissions check to
> "s6-rc change" in order to fail early and cleanly.

Personally, I don't mind the messages. I am able to interpret them,
and there is a "permission denied" there anyway, but then again, I am
familiar with some of s6-rc's internals. I don't know about other
people, though.

> >* s6-svdt: [...]
> I haven't studied this in detail, but making the permissions of a
> supervise/ directory distinct from those of the whole supervision tree
> sounds sketchy to me. If you make supervise/ readable by another group,
> it means permissions of the files inside need to be carefully crafted,
> which I'm not sure is the case for everything. At the very least the
> status file should be made unreadable by others - not that it's
> critical,
> but restricting certain stuff to a group is pretty meaningless if other
> stuff such as the state of the service is available to everyone.

After further testing, it turns out that being group *searchable*
(permissions 0710) is enough for unprivileged use. It seems that the
openat() call that s6-svdt makes to read the death tally (and also
s6-svlisten1 for the status file) fails otherwise.

$ s6-svdt scan/a-longrun
s6-svdt: fatal: unable to read death tally for service scan/a-longrun:
Permission denied

strace reveals that, for s6-svlisten1:

openat(AT_FDCWD, "scan/a-longrun/supervise/status",
O_RDONLY|O_NONBLOCK) = -1 EACCES (Permission denied)

And for s6-svdt:

openat(AT_FDCWD, "scan/a-longrun/supervise/death_tally",
O_RDONLY|O_NONBLOCK) = -1 EACCES (Permission denied)

> >* s6-svstat: [...]
>
> That's an excellent finding!
> I always thought daemontools (from which every other suite takes
> inspiration!) had a separate 'ok' fifo because using the 'control' fifo
> as a usage marker involves some tricky manipulation at supervisor start,
> and djb just couldn't be bothered. I got rid of 'ok' because that saves
> one fd. But you're right: separating 'ok' from 'control' allows checking
> whether the supervisor is running with a different permission set from
> actually sending commands.

To be honest, after seeing that daemontools supervise's code opened
the 'ok' FIFO for reading and did nothing else with it other that
marking the corresponding file descriptor close-on-exec, for the
longest time I wondered why it even existed. But looking at
daemontools svstat's code, yes, that is the FIFO that is checked to
print the "supervise not running" message. Nothing is done with the
┬┤control' FIFO.

> Do you think it's worth it to change the locking implementation back
> to one that uses an additional fd in order to make s6-svstat more
> accessible? Keep in mind s6-supervise is long-running and there can be
> *a lot* of them on a system, so it might add up to hundreds of extra
> open descriptors.

I'm unsure. I think that from a UX (haha) perspective, not being able
to use s6-svstat, and s6-svdt to a lesser degree, as an unprivileged
user might feel too restrictive, and, with a single FIFO setup, that
doesn't look possible without also allowing unprivileged use of
s6-svc. Also, people who currently use runit have those hundreds of
open descriptors already.

Thanks,
G.
Received on Mon Feb 03 2020 - 02:12:10 UTC

This archive was generated by hypermail 2.3.0 : Sun May 09 2021 - 19:38:49 UTC