s6
Software
skarnet.org
The s6-sudod program
s6-sudod receives command-line arguments, environment variables
and standard descriptors from a peer s6-sudoc
program over a Unix socket, then forks another program.
Interface
s6-sudod [ -0 ] [ -1 ] [ -2 ] [ -d ] [ -t timeout ] [ sargv... ]
- s6-sudod gets 3 file descriptors via fd-passing over a Unix socket that
must be open on its descriptors 0 and 1. (The received descriptors will be the
stdin, stdout and stderr of the server program.) It expects a
s6-sudoc process to be sending them on the
client side.
- It also receives a list of command-line arguments cargv..., and
an environment clientenv.
- s6-sudod forks and executes sargv... cargv...
The client command line is appended to the server command line.
- s6-sudod waits for its child to exit and transmits its exit code
to the peer s6-sudoc process. It then exits 0.
Environment
s6-sudod transmits its own environment to its child, plus the environment sent
by s6-sudoc, filtered in the following manner:
for every variable sent by s6-sudoc, if the
variable is present but empty in s6-sudod's environment, then
its value is overriden by the value given by s6-sudoc. A variable that is
already nonempty, or that doesn't exist, in s6-sudod's environment, will not
be transmitted to the child. In other words:
- If there's no variable X in s6-sudod's environment, the child
will have no variable X defined
- If there's a non-empty variable X in s6-sudod's environment,
the child will inherit that variable, with its value, from s6-sudod
- If there's an empty variable X in s6-sudod's environment,
and s6-sudoc transmits variable X, then the child will inherit that
variable with the value from s6-sudoc. (If s6-sudoc does not transmit X,
the variable will be present, but empty, in the child's environment.)
Options
- -0 : do not inherit stdin from s6-sudoc. The child will be
run with its stdin pointing to /dev/null instead.
- -1 : do not inherit stdout from s6-sudoc. The child will be
run with its stdout pointing to /dev/null instead.
- -2 : do not inherit stderr from s6-sudoc. The child will be
run with its stderr being a copy of s6-sudod's stderr instead. (This is useful
to still log the child's error messages without sending them to the client.)
- -d : detach. The child will keep running until it naturally
exits, even if the client disconnects. Setting this option also enforces
-0, -1 and -2. Bear in mind that this option
relinquishes a lot of control over the child, and administrators should make sure
it is appropriately short-lived.
- -t timeout : if s6-sudod has not
received all the needed data from the client after timeout
milliseconds, it will exit without spawning a child. By default, timeout
is 0, meaning infinite. This mechanism exists to protect the server from
malicious or buggy clients that would uselessly consume resources.
Usage example
The typical use of s6-sudod is in a
local service with a
s6-ipcserver process listening on a Unix
socket, a s6-ipcserver-access process
performing client authentication and access control, and possibly a
s6-envdir
process setting up the environment variables that will be accepted by
s6-sudod. The following script, meant to be a run script in a
service directory,
will set up a privileged program:
#!/command/execlineb -P
fdmove -c 2 1
fdmove 1 3
s6-envuidgid serveruser
s6-ipcserver -U -1 -- serversocket
s6-ipcserver-access -v2 -l0 -i rules --
exec -c
s6-envdir env
s6-sudod
sargv
- execlineb
executes the script.
- fdmove makes
sure the script's error messages are sent to the service's logger.
- fdmove
redirects the script's stdout to file descriptor 3. This is useful if
the service directory contains a notification-fd file containing
3, so the daemon can perform
readiness notification by writing a
newline to its stdout. (The
-1 option to s6-ipcserver tells it to do this.)
- s6-envuidgid
sets the UID, GID and GIDLIST environment variables for s6-ipcserver to interpret.
- s6-ipcserver binds to serversocket,
drops its privileges to those of serveruser, and announces its
readiness. Then, for every client connecting to serversocket:
- s6-ipcserver-access checks the
client's credentials according to the rules in directory rules.
- exec -c
clears the environment.
- s6-envdir
sets environment variables according to the directory env. You can
make sure that a variable VAR will be present but empty by performing
echo > env/VAR. (A single newline is interpreted by s6-envdir as
an empty variable; whereas if env/VAR is totally empty, then the
VAR variable will be removed from the environment.)
- s6-sudod reads a command line cargv, a client environment
and file descriptors over the socket.
- s6-sudod spawns sargv cargv.
(Actually, s6-ipcserver does not do this
itself: it executes into other programs that each do one of the tasks. But for
our example, it does not matter.)
This means that user clientuser running
s6-sudo serversocket cargv will be
able, if authorized by the configuration in rules, to run
sargv cargv as user serveruser, with stdin,
stdout, stderr and the environment variables properly listed in env
transmitted to sargv.
Notes
- If the -d option to s6-sudod has not been given, and
s6-sudoc is killed (or exits after timeoutrun milliseconds)
while the server program is still running, s6-sudod will send a SIGTERM and a
SIGCONT to its child, then exit 1. However, sending a SIGTERM to the child
does not guarantee that it will die; and
if it keeps running, it might still read from the file that
was s6-sudoc's stdin, or write to the files that were s6-sudoc's stdout or
stderr. This is a potential security risk.
Administrators should audit their server programs to make sure this does not
happen.
- More generally, anything using signals or terminals will not be
handled transparently by the s6-sudoc + s6-sudod mechanism. The mechanism
was designed to allow programs to gain privileges in specific situations:
short-lived, simple, noninteractive processes. It was not designed to emulate
the full suid functionality and will not go out of its way to do so.
- Administrators should also make sure that it's not a problem if
s6-sudod's child keeps running after the s6-sudoc client exits, if they
have given the -d option to s6-sudod. In particular, they should
study what happens if another connection to the same service occurs while
an instance is still running.
- sargv may be empty. In that case, the client is in complete
control of the command line executed as serveruser. This setup is
permitted by s6-sudod, but it is very dangerous, and extreme attention should
be paid to the construction of the s6-ipcserver-access rules.