s6-networking
Software
skarnet.org
The s6-tlsc-io program
s6-tlsc-io is a program that establishes a TLS or SSL
client connection over an existing TCP connection, then
communicates with an existing local program over already
established pipes. It is the only client-side program in
s6-networking that performs cryptography.
s6-networking does not include
cryptographic software. All the crypto used in s6-tlsc-io
is provided by the chosen SSL backend:
BearSSL or
LibreSSL, depending on
the options given when configuring s6-networking.
Interface
s6-tlsc-io [ -S | -s ] [ -Y | -y ] [ -v verbosity ] [ -K kimeout ] [ -k servername ] [ -d notif ] [ -- ] fdr fdw
- s6-tlsc-io expects to read cleartext data to stdin, and write
cleartext data to stdout. It also expects descriptors fdr and
fdw to be open (typically connected to the network), to
respectively read ciphertext from and write ciphertext to.
- It initiates a TLS handshake over the network connection,
expecting a TLS server on the other side.
- Then it acts as a full duplex tunnel, encrypting and transmitting
data from stdin to fdw, and decrypting and transmitting data
from fdr to stdout.
- When it cannot transmit any more data from/to the local application
because connections have closed, s6-tlsc-io exits.
Exit codes
- 0: the connection terminated normally.
- 96: error while configuring the TLS context - for instance, invalid trust anchor set.
- 97: error while setting up the TLS client engine.
- 98: TLS error while running the engine.
- 100: wrong usage.
- 111: system call failed.
Protocol version and parameters
During the TLS handshake, s6-tlsc-io 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-io to adapt to as many servers
as possible, that's why it adopts a liberal approach to protocol
versions.
Environment variables
s6-tlsc-io 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-io 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
PEM-encoded.
If s6-tlsc-io 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-io
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
s6-applyuidgid
program is a chainloading way of doing it.
Server name determination for SNI
The -k servername option is important to
s6-tlsc-io: it tells it to send servername
as the name to require a certificate for.
Not setting this option allows s6-tlsc-io to
proceed without SNI, which may be a security risk.
SSL close handling
If the local application initiates the end of the session by sending
EOF to fdr, there are two ways for the TLS 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 (2020), most protocols are auto-terminated, so
it is not dangerous anymore to use EOF tranmission, and that
is the default for s6-tlsc-io. Nevertheless, by
using the -S option, you can
force it to use the close_notify method if your
application requires it to be secure.
s6-tlsc-io options
- -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
no effect.
- -S : send a close_notify alert
and break the connection when receiving a local 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 : if the handshake takes
more than kimeout milliseconds to complete, close the connection.
The default is 0, which means infinite timeout (never kill the connection).
- -d notif : handshake notification.
notif must be a file descriptor open for writing. When the
TLS handshake has completed, some data (terminated by two null
characters) will be sent to file descriptor notif. The
data contains information about the TLS parameters of the connection;
its exact contents are left unspecified, but there's at least
an SSL_PROTOCOL=protocol string and
an SSL_CIPHER=cipher string, both
null-terminated.
Sending this data serves a dual purpose: telling the notif
reader that the handshake has completed, and providing it with some
basic information about the connection. If this option is not given,
no such notification is performed.