S6-FDHOLDERD(8)

πŸ“¦ version 2.15.1.0, in nixpkgs unstable

🌸 S6-FDHOLDERD(8) 🌸

okay so bestie, you found the man page for s6-fdholderd β€” the literal serving part of the s6-fdholder-daemon(8) fd-holding server πŸ’… she's iconic, she holds your file descriptors, she's got main character energy. This page is from the System Manager's Manual, section 8. Let's get into it 🫢


✨ NAME ✨

s6-fdholderd β€” the serving part of the s6-fdholder-daemon(8) fd-holding server. periodt πŸ’–


🌈 SYNOPSIS 🌈

s6-fdholderd [-1] [-v verbosity] [-c maxconn] [-n maxfds]
             [-t clienttimeout] [-T lameducktimeout]
             [-i rulesdir | -x rulesfile]

πŸ¦‹ DESCRIPTION πŸ¦‹

so the whole vibe here: s6-fdholderd assumes that its stdin is a bound and listening Unix domain socket. That's it, that's the premise. It accepts connections from clients connecting to that socket, and then it stores and retrieves file descriptors on their behalf. literally its whole job πŸŽ€

More precisely β€” s6-fdholderd accepts connections from clients to an already bound and listening SOCK_STREAM Unix domain socket which is its standard input. No socket-making here, she just walks in and starts working with what you gave her ✨

Depending on the verbosity level, it logs what it does to stderr.

It runs until killed by a signal (normally SIGTERM). And here's the part you need to hear: when s6-fdholderd is killed, all the fds it is currently holding are lost 😭 like gone-gone. So if they need to be preserved, the admin should make sure to transfer them beforehand with s6-fdholder-transferdump(8). no cap, do the transfer first, don't come crying to me πŸ™ˆ

Client connections are short-lived. Clients generally perform one operation, then disconnect. in-and-out, no lingering, love that for them πŸ’…

Possible operations include:

s6-fdholderd is meant to be execve'd into by a program that gets the listening socket. That program is normally s6-ipcserver-socketbinder(8), 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. slay, flexible queen πŸ‘‘

s6-fdholderd will store any open file descriptor, without discriminating on its type. Like, zero judgement, she's not a gatekeeper πŸ’– 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. ⚠️ so yeah, regular files = chaos vibes, you've been warned.

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. Security by obscurity is not the move here, bestie. 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. multiple instances, all of them thriving 🌸

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. memory management icon behavior, honestly πŸ”₯


πŸŽ€ Signals πŸŽ€

  • SIGTERM β€” Enter lameduck mode, then exit when no more operation is pending. (polite exit, she finishes her sentence first ✨)
  • 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. iykyk πŸ¦‹

πŸ‘‘ Identifiers πŸ‘‘

Every file descriptor is stored in the s6-fdholderd daemon via the s6-fdholder-store(8) program, with an identifier. That identifier is a zero-terminated character string, containing 1 to 255 characters. 1 to 255, bestie, that's the range πŸ’…

Any non-null character can be used in an identifier. Non-printable or special characters will be quoted when printed by s6-fdholder-list(8). 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. so like, the server genuinely does not care, but your future self will 😭

A good convention is to use unix:path/to/socket for Unix domain sockets and protocol:ip:port for INET domain sockets. chefs kiss naming scheme, honestly πŸ“

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. recycle era ♻️


🧚 Configuration 🧚

Before running s6-fdholderd (or its wrapper s6-fdholder-daemon(8)), it is necessary to configure it. This is done by a series of rules, or ruleset, stored in either a rulesfile in the CDB[1] 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 has been provided. no config = no service, she's not doing it 🚫

Rulesets can be converted between the rulesdir and rulesfile formats with the s6-accessrules-cdb-from-fs(8) and s6-accessrules-fs-from-cdb(8) conversion tools. conversion girlies, we love you 🌈


πŸ’œ Rules format πŸ’œ

The rules file, or rules directory, follows the s6-accessrules(7) 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 right, root gets nothing by default. That is why it is essential to create a sensible ruleset prior to running the server in order to do anything useful. don't skip this step, bestie, I'm begging πŸ™

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 in 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[2] 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 in 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[2] 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 in 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 in 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. πŸ”₯

✨ OPTIONS ✨

  • -1 β€” writes a single newline to stdout, then closes stdout, right before it hops into the client-accepting loop. Bestie, this is the readiness-notification moment πŸ’… if stdout is redirected in a suitable way, monitoring programs can use it to check when the server is actually accepting connections. Peep s6-notifywhenup(7) for the whole tea on readiness notification.
  • -v verbosity β€” be more or less chatty. verbosity can be 0 (quiet 🀫), 1 (normal), or 2 or more (verbose, like, full group-chat mode πŸ—£οΈ).
  • -c maxconn β€” accept at most maxconn concurrent connections. Default is 16. Ngl, 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's best for maxconn to be as small as possible β€” keep that little guy tiny 🐜.
  • -n maxfds β€” store at most maxfds file descriptors. Default is 1000. Fr fr, 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 use s6-softlimit(8) to properly adjust the number of openable files[3] of the current process. Don't skip this step, ok? 😭
  • -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. (Yes, bestie, it's -t again β€” that's literally what the page says, so I'm not touching it 🫢.)
  • -x rulesfile β€” read access rights configuration from CDB[1] file rulesfile.
  • -i rulesdir β€” read access rights configuration from the filesystem in directory rulesdir.

🌸 EXAMPLES 🌸

Okay so imagine you wanna run an s6-fdholderd daemon in the /service/fdholder directory with the -i rules option β€” here's the whole glow-up, step by step:

  • Prepare the rules directory ✨:
mkdir /service/fdholder/rules
cd /service/fdholder/rules
mkdir uid gid
  • Allow a few users, or literally 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 the same users to list all identifiers:
echo > gid/23/env/S6_FDHOLDER_LIST
🚨 To allow everyone to dump entire states into the server β€” never do this! it's only an example:
mkdir -p uid/default/env
touch uid/default/allow
echo > uid/default/env/S6_FDHOLDER_SETDUMP

πŸ¦‹ SEE ALSO πŸ¦‹

The receipts πŸ“š:

This man page is ported from the authoritative documentation at: https://skarnet.org/software/s6/s6-fdholderd.html


πŸ‘‘ AUTHORS πŸ‘‘

  • Laurent Bercot
  • Alexis <flexibeast@gmail.com> (man page port)

Aaaand that's the whole page, bestie πŸŽ€πŸ’– This icon of a man page is from the Debian package, dated January 15, 2023, and it's titled S6-FDHOLDERD(8) β€” slay, go configure your fd holder, you absolute main character 🌈⭐

πŸ“¦ where this page comes from