The s6-tlsc program
s6-tlsc is a program that establishes a TLS or SSL
client 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-tlsc
is provided by the chosen SSL backend:
LibreSSL, depending on
the options given when configuring s6-networking.
s6-tlsc [ -S | -s ] [ -Y | -y ] [ -Z | -z ] [ -v verbosity ] [ -K kimeout ] [ -k servername ] [ -6 rfd ] [ -7 wfd ] [ -- ] prog...
- s6-tlsc expects to have an open TCP connection it
can talk to on its (by default) descriptors 6 (for reading)
and 7 (for writing).
- It spawns prog... as a child process,
interposing itself between it and the network.
- It initiates a TLS/SSL handshake over the
network connection, expecting a TLS/SSL server on the other
- It manages the encryption/decryption of all the
messages between prog and the server.
prog speaks plaintext, but only ciphertext is sent
on the network.
- When prog exits, s6-tlsc exits.
- 96: error while configuring the TLS/SSL context - for instance, invalid trust anchor set.
- 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-tlsc
waits for prog to exit, then exits with an
of prog's exit code.
Protocol version and parameters
During the TLS/SSL handshake, s6-tlsc tries
every version of the protocol that is supported by the
backend, with all supported 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 client, it is better for s6-tlsc to adapt to as many servers
as possible, that's why it adopts a liberal approach to protocol
s6-tlsc expects to have one of the
CADIR or CAFILE environment variables set.
It will refuse to run if both are unset. If both are set,
CADIR has priority. The value of that variable is:
- for CADIR: a directory where trust anchors
(i.e. root or intermediate CA certificates) can be found,
one per file, DER- or PEM-encoded.
- for CAFILE: a file containing the whole set
of trust anchors, PEM-encoded.
If you are using client certificates, s6-tlsc also reads
two more environment variables: KEYFILE contains
the path to a file containing the private key, DER- or
PEM-encoded; and CERTFILE contains the path to
a file containing the client certificate, DER- or
If s6-tlsc is run as root, it can also read two
other environment variables, TLS_UID and TLS_GID,
which contain a numeric uid and a numeric gid; s6-tlsc
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.
Unless the -Z option has been given to
s6-tlsc, prog... is run with all the
TLS/SSL variables unset: CADIR, CAFILE,
KEYFILE, CERTFILE, TLS_UID and TLS_GID. The goal is
for s6-tlsc to be, by default, as invisible
Server name determination for SNI
The -k servername option is important to
s6-tlsc: it tells it to send servername
as the name to require a certificate for.
Not setting this option allows s6-tlsc to
proceed without SNI,
which may be a security risk.
The s6-tlsclient program can
automatically craft a -k option for s6-tlsc
if the host argument that is given to it is a
host name. But if you're invoking s6-tlsc directly,
do not forget to give it this option.
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-tlsc. 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-tlsc-related variables before spawning prog....
- -z : clean the environment of
s6-tlsc-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 : Do not send a client certificate. This is the default.
- -y : Send a client certificate.
- -k servername : use Server Name
Indication, and send servername. The default is not to
use SNI, which may be a security risk.
- -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).
- -6 rfd : expect an open file
descriptor numbered rfd to read network (ciphertext)
data from. Make sure prog also reads its data
from its own fd rfd. Default is 6.
- -7 wfd : expect an open file
descriptor numbered wfd to write network (ciphertext)
data to. Make sure prog also writes its data to
its own fd wfd. Default is 7.
- The goal of the s6-tlsc interface (and its
server-side companion s6-tlsd) 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.