The s6-tlsd program
s6-tlsd is a program that performs the server side of
a TLS or SSL connection over an existing TCP connection, then spawns
an application. It is meant to make network communications
secure even for applications that do not natively support
s6-networking does not include
cryptographic software. All the crypto used in s6-tlsd
is provided by the chosen SSL backend:
LibreSSL, depending on
the options given when configuring s6-networking.
s6-tlsd [ -S | -s ] [ -Y | -y ] [ -Z | -z ] [ -v verbosity ] [ -K kimeout ] [ -- ] prog...
- s6-tlsd expects to have an open TCP connection it
can talk to on its stdin (for reading) and stdout
- It spawns prog... as a child process,
interposing itself between it and the network.
In other words: prog still reads cleartext
on its stdin and writes cleartext on its stdout, but
those will actually be pipes to s6-tlsd, which
will read ciphertext from its own stdin (the network)
and write ciphertext to its own stdout (the network).
- It initiates the server side of a TLS/SSL handshake
over the network connection, expecting a TLS/SSL client on
the other side.
- It manages the encryption/decryption of all the
messages between prog and the client.
prog speaks plaintext, but only ciphertext is sent
on the network.
- When prog exits, s6-tlsd exits.
- 96: error while configuring the TLS/SSL context - for instance, invalid
private key or server certificate files.
- 97: error while setting up the TLS/SSL client engine.
- 98: TLS/SSL error while running the engine.
- 100: wrong usage.
- 111: system call failed.
If the TLS/SSL connection closes cleanly, s6-tlsd
waits for prog to exit, then exits with an
of prog's exit code.
Protocol version and parameters
During the TLS/SSL handshake, s6-tlsd tries the
versions of the protocol that is supported by default by the
backend, with the default algorithms and cipher suites;
the backend normally ensures that the most secure combination
is tried first, with slow degradation until the client and
the server agree.
As a server, s6-tlsd can be conservative in its
choice of protocols. It is currently not very conservative
when using the BearSSL backend; it could become more so in
the future, by defining a custom server profile that supports
only TLS-1.2 but with several algorithms and cipher suites.
s6-tlsd expects to have the following
environment variables set:
- KEYFILE: a path to the file
containing the server's private key, DER- or PEM-encoded.
- CERTFILE: a path to the file
containing the server's certificate, DER- or PEM-encoded.
If PEM-encoded, the file can actually contain a chain
If one of those variables is unset, s6-tlsd
will refuse to run.
If you are using client certificats, s6-tlsd
also requires either one of the following variables to be set:
- CADIR: a directory where trust anchors
(i.e. root or intermediate CA certificates) can be found,
one per file, DER- or PEM-encoded.
- CAFILE: a file containing the whole set
of trust anchors, PEM-encoded.
If s6-tlsd is run as root, it can also read two
more environment variables, TLS_UID and TLS_GID,
which contain a numeric uid and a numeric gid; s6-tlsd
then drops its root privileges to this uid/gid after spawning
prog.... This ensures that the TLS/engine and the
application run with different privileges.
Note that prog...
should drop its own root privileges by its own means: the
program is a way of doing it. If the s6-tlsd
invocation actually comes from a
s6-tlsserver command line,
and privilege-dropping options (-G, -g,
-u or -U) have been given to
directly follows s6-tlsd on the command line, in order
to also drop the child's privileges before executing the application.
The point of that setup is:
- To read the private key file as root
- To run the application as a non-root user
- To run s6-tlsd as a different non-root user
- That way, even if s6-tlsd, the application, or both,
get compromised, the private key is still secure.
Unless the -Z option has been given to
s6-tlsd, prog... is run with all the
TLS/SSL variables unset: CADIR, CAFILE,
KEYFILE, CERTFILE, TLS_UID and TLS_GID. The goal is
for s6-tlsd to be, by default, as invisible
SSL close handling
If prog initiates the end of the session by sending
EOF, there are two ways for the TLS/SSL layer to handle it.
- It can send a close_notify alert, and wait for
an acknowledgement from the peer, at which point the connection
is closed. The advantage of this setup is that it is secure
even when the application protocol is not auto-terminated, i.e.
when it does not know when its data stops. Old protocols such
as HTTP-0.9 are in this case. The drawback of this setup is
that it breaks full-duplex: once a peer has sent the
close_notify, it must discard all the incoming
records that are not a close_notify from the
other peer. So if a client sends EOF while it is still
receiving data from the server, the connection closes
immediately and the data can be truncated.
- It can simply transmit the EOF, shutting down
half the TCP connection, and wait for the EOF back.
The advantage of this setup is that it maintains
full-duplex: a client can send EOF after its initial
request, and still receive a complete answer from the
server. The drawback is that it is insecure when the application
protocol is not auto-terminated.
Nowadays (2017), most protocols are auto-terminated, so
it is not dangerous anymore to use EOF tranmission, and that
is the default for s6-tlsd. Nevertheless, by
using the -S option, you can
force it to use the close_notify method if your
application requires it to be secure.
- -v verbosity : Be more or less
verbose. Default for verbosity is 1. 0 is quiet, 2 is
verbose, more than 2 is debug output. This option currently has
- -Z : do not clean the environment of
s6-tlsd-related variables before spawning prog....
- -z : clean the environment of
s6-tlsd-related variables before spawning prog....
This is the default.
- -S : send a close_notify alert
and break the connection when prog sends EOF.
- -s : transmit EOF by half-closing the TCP
connection without using close_notify. This is the default.
- -Y : Require an optional client certificate.
- -y : Require a mandatory client certificate.
The default, with neither the -Y nor the -y option,
is not to require a client certificate at all.
- -K kimeout : close the connection
if kimeout milliseconds elapse without any data being
received from either side. The default is 0, which means
infinite timeout (never kill the connection).
- The goal of the s6-tlsd interface (and its
client-side companion s6-tlsc) is to
make it so that if you have a client, run by the command line
client... that speaks a cleartext protocol to a server
run by the command line server..., then if the server
has the proper private key and certificate, and the client has
the proper list of trust anchors, you can just change the
client command line to s6-tlsc client... and the
server command line to s6-tlsd server...
without changing the client or the server themselves, and the
communication between them will be secure.