s6
Software
skarnet.org
The s6-fdholderd program
s6-fdholderd is the serving part of the
s6-fdholder-daemon
fd-holding server.
It assumes that its stdin is a bound and listening Unix
domain socket, and
it accepts connections from clients connecting to it, and stores and
retrieves file descriptors on their behalf.
Interface
s6-fdholderd [ -1 ] [ -v verbosity ] [ -c maxconn ] [ -n maxfds ] [ -i rulesdir | -x rulesfile ]
- s6-fdholderd accepts connections from clients to an already
bound and listening SOCK_STREAM Unix domain socket which is its
standard input.
- Depending on the verbosity level, it logs what it does to stderr.
- It runs until killed by a signal. Depending on the received
signal, it may kill its children before exiting.
- Client connections are short-lived. Clients generally perform
one operation, then disconnect.
- Possible operations include:
Options
- -1 : write a newline to stdout, and close stdout,
right before entering the client-accepting loop.
If stdout is suitably redirected, this can be used by monitoring
programs to check when the server is accepting connections.
The s6-notifywhenup
program can be used before the s6-ipcserver
invocation to notify listeners when the server is ready.
- -v verbosity : be more or less
verbose. verbosity can be 0 (quiet), 1 (normal), or 2 or more
(verbose).
- -c maxconn : accept at most
maxconn concurrent connections. Default is 16. It is
impossible to set it higher than the value of the S6_FDHOLDER_MAX macro,
i.e. 256. Client connections to this server are short-lived, so this
number needs not be too high. Every client connection eats up
one available file descriptor, so it is best for maxconn to be
as small as possible.
- -n maxfds : store at most
maxfds file descriptors. Default is 1000.
It is impossible to set it higher than the number of files that can
be opened by the s6-fdholderd process, minus a few descriptors
needed for correct operation. Before running s6-fdholderd, make sure to
properly adjust the
number
of openable files of the current process.
- -t clienttimeout : disconnect a client
if it's in the middle of an operation and it has not written or read any
data in clienttimeout milliseconds. By default, clienttimeout
is 0, which means infinite.
- -T lameducktimeout : give clients
lameducktimeout milliseconds to finish their current operation
before exiting after receiving a SIGTERM. By default, lameducktimeout
is 0, which means infinite.
- -x rulesfile : read access rights
configuration from
CDB
file rulesfile.
- -i rulesdir : read access rights
configuration from the filesystem in directory rulesdir.
Signals
- SIGTERM: enter lameduck mode, then exit when no more operation
is pending.
- SIGHUP: reopen rulesfile, if s6-fdholderd has been run
with the -x option. It is not necessary to send s6-fdholderd
a SIGHUP when the -i option is used instead: configuration
changes in the filesystem are automatically picked up.
Identifiers
- Every file descriptor is stored in the s6-fdholderd daemon via the
s6-fdholder-storec program, with
an identifier. That identifier is a zero-terminated character
string, containing 1 to 255 characters.
- Any non-null character can be used in an identifier. Non-printable or
special characters will be quoted when printed by
s6-fdholder-listc. However, it is
recommended to only use reasonable characters in identifiers: clients
should be able to know at a glance what file descriptor is represented by
an identifier. Identifiers have no special meaning to the server.
- A good convention is to use unix:/path/to/socket for
Unix domain sockets and protocol:ip:port
for INET domain sockets.
- An identifier is chosen by the storing client, within the limits of
what the server authorizes it to use.
- The retrieving client must know the exact identifier corresponding to
a descriptor to be able to retrieve that descriptor. It must also be
authorized by the server.
- When an identifier is in use, it cannot be used again to store another
descriptor. However, once the descriptor has been deleted or has expired,
it is possible to reuse the same identifier.
Configuration
Before running s6-fdholderd (or its wrapper
s6-fdholder-daemon), it is necessary
to configure it. This is done by a series of rules, or ruleset,
stored in either a rulesfile in the
CDB format,
or in a rulesdir, i.e. a directory in the filesystem following a
certain format. s6-fdholderd will refuse to run if neither the -i
nor the -x option have been provided.
Rulesets can be converted between the rulesdir and
rulesfile formats with the
s6-accessrules-cdb-from-fs and
s6-accessrules-fs-from-cdb
conversion tools.
Rules format
The rules file, or rules directory, follows the
s6 accessrules format for uid and
gid checking. For every connecting client, s6-fdholderd matches the uid
and gid of the client against the provided ruleset, and determines what
the client is authorized to do.
By default, no client is allowed to do anything - not even
connect to the server. Even root, the super-user, will be denied
access. That's why
it is essential to create a sensible ruleset prior to running the server
in order to do anything useful.
The various rights that a client can have are the following (using a
rulesdir as an example, but a rulesfile works the same way):
- Connect to the server. This is a prerequisite for
doing anything. It will allow a client to perform "public" operations,
ones that do not require specific access rights other than connecting.
(There are no such operations for now, but it could change in the
future; for now, when you allow a client to connect to the server,
make sure to give him other rights too.)
This right is given if an
allow file is found in one of the subdirectories checked by
s6_accessrules_keycheck_uidgid.
For instance, to allow everyone to connect, touch
rulesdir/uid/default/allow.
The other rights are defined in the "environment" part of the ruleset:
- File descriptor storage rights. This will be checked for storage and
deletion of individual file descriptors. This right is given if a non-empty
file named S6_FDHOLDER_STORE_REGEX is found is the env/
subdirectory of one of the subdirectories checked by
s6_accessrules_keycheck_uidgid.
This file should contain a single line, which will be interpreted as an
extended
regular expression by s6-fdholderd; the regular expression describes the
set of identifiers that the client is allowed to use to store file
descriptors. For instance, ^unix:/tmp/ indicates that a client
that matches this rule will be allowed to store or delete file descriptors
using any identifier starting with unix:/tmp/.
- File descriptor retrieval rights. This will be checked for retrieval
of individual file descriptors. This right is given if a non-empty
file named S6_FDHOLDER_RETRIEVE_REGEX is found is the env/
subdirectory of one of the subdirectories checked by
s6_accessrules_keycheck_uidgid.
This file should contain a single line, which will be interpreted as an
extended
regular expression by s6-fdholderd; the regular expression describes the
set of identifiers that the client is allowed to use to retrieve file
descriptors. For instance, ^unix:/tmp/ indicates that a client
that matches this rule will be allowed to retrieve file descriptors that are
identified by strings starting with unix:/tmp/.
- Listing rights. This will be checked for clients wanting to list
the identifiers of the descriptors currently stored in the server. This
right is given if a non-empty file named S6_FDHOLDER_LIST is
found in the env/ subdirectory of one of the subdirectories checked by
s6_accessrules_keycheck_uidgid.
- Dump reading rights. This will be checked for clients wanting to
copy the whole state of the server. This right is given if a non-empty
file named S6_FDHOLDER_GETDUMP is found is the env/
subdirectory of one of the subdirectories checked by
s6_accessrules_keycheck_uidgid.
This is very powerful: you should only give this right to root,
or to a dedicated uid that is only used to perform dump transfers.
- Dump writing rights. This will be checked for clients wanting to
copy an entire set of file descriptors into the server.
This right is given if a non-empty
file named S6_FDHOLDER_SETDUMP is found is the env/
subdirectory of one of the subdirectories checked by
s6_accessrules_keycheck_uidgid.
This is very powerful: you should only give this right to root, or
to a dedicated uid that is only used to perform dump transfers.
Configuration examples
Assuming you want to run a s6-fdholderd daemon in the
/service/fdholder directory with the -i rules option,
you should:
- Prepare the rules directory: mkdir /service/fdholder/rules ;
cd /service/fdholder/rules ; mkdir uid gid
- Allow a few users, or everyone, to connect. To allow root to
connect: mkdir uid/0 ; touch uid/0/allow. To allow everyone
to connect: mkdir uid/default ; touch uid/default/allow.
Depending on your policy, you should now give certain rights to
certain users or groups. For instance:
- To allow user number 50 to perform dump transfers from and to
this server: mkdir -p uid/50/env ; touch uid/50/allow ;
echo > uid/50/env/S6_FDHOLDER_GETDUMP ; echo >
uid/50/env/S6_FDHOLDER_SETDUMP
- To allow user number 72 to store a descriptor under the name
foobar and only this name: mkdir -p uid/72/env ;
touch uid/72/allow ; echo '^foobar$' >
uid/72/env/S6_FDHOLDER_STORE_REGEX
- To allow users having 23 as their primary group number to retrieve file
descriptors with an identifier containing foo, then one
character, then bar:
mkdir -p gid/23/env ; touch gid/23/allow ; echo foo.bar >
gid/23/env/S6_FDHOLDER_RETRIEVE_REGEX
- To allow everyone to dump entire states into the server:
mkdir -p uid/default/env ; touch uid/default/allow ;
echo > uid/default/env/S6_FDHOLDER_SETDUMP.
Never do this!
Notes
- s6-fdholderd is meant to be execve'd into by a program that gets
the listening socket. That program is normally
s6-ipcserver-socketbinder,
which creates the socket itself; but it can be a different one if the
socket is to be obtained by another means, for instance if it has
been retrieved from another fd-holding daemon.
- s6-fdholderd will store any open file descriptor, without
discriminating on its type. However, it makes more sense to store certain
file descriptor types than others: for instance, Unix domain or INET domain
sockets, or named pipes, are good candidates for fd-holding. On the other
hand, it makes little sense to fd-hold regular files, and if done anyway,
the results can be surprising, because the read/write file offset is
stored with the descriptor, and no automatic rewind is performed by the
daemon.
- Despite there being access control for listing, the security of the
system should not depend on a client
not knowing what identifier a certain descriptor is stored under. If you
need to hold descriptors that only a few programs are supposed to access,
you can always run a separate s6-fdholderd instance in a private directory
with a configuration tailored to your needs
- and you can even make the name of the listening socket private.
s6-fdholderd is lightweight, you can start as many instances as you need,
and you can run them as long as you need then kill them with SIGTERM.
- s6-fdholderd pre-allocates its storage at start, in the stack. It
uses a small amount of heap memory for communication with a client, but frees
it as soon as the client disconnects. It should never run out of memory in
normal usage, even if used intensively.