\input texinfo
@setfilename tlsd.info
@settitle TLSd
@direntry
* TLSd: (tlsd). TLS super-server.
@end direntry
@copying
@quotation
@verbatiminclude COPYING
@end quotation
@end copying
@include version.texi
@node Top
@top TLSd
TLSd is a daemon that both accepts and initiates TLS connections, runs
processes, and provides peer certificate's fingerprint as an
environment variable for them. The intent is to facilitate creation
and usage of simple services for peer-to-peer networking.
This manual is for TLSd version @value{VERSION}, last updated on
@value{UPDATED}.
@menu
* Copying Conditions:: Your rights.
* Invocation:: Command line arguments.
* Usage:: Basic usage instructions.
* fp2alias:: A basic fingerprint-to-alias converter.
* std2fifo:: A std@{in,out@} <->
//@{in,out@} proxy.
* Common tools:: Using common tools in combination with TLSd.
* Writing services:: Tips and guidelines on how to write services.
@end menu
@node Copying Conditions
@chapter TLSd Copying Conditions
@insertcopying
@node Invocation
@chapter TLSd invocation
@example
tlsd [option ...] [--] [argument ...]
@end example
@section Command line arguments
@table @option
@item -k @var{keyfile}
Private key file to use (default is @file{/etc/tls/key.pem}).
@item -c @var{certfile}
Certificate file to use (default is @file{/etc/tls/cert.pem}).
@item -p @var{port}
Port to listen on (default is to use a randomly selected one).
@item -b @var{host}
Bind address (default is 0.0.0.0).
@item -s @var{signo}
Send a signal to a child on termination. No signal is sent by default:
child processes are expected to exit once their @code{stdin} is closed.
@item -n
Do not require a peer certificate. This makes the @env{SHA256}
environment variable for child processes optional.
@item -d @var{directory}
Write peer certificates in DER format into a directory.
@item -i @var{ident}
Syslog identifier to use.
@item -e
Print messages into stderr, in addition to syslog.
@item -h
Print a help message and exit.
@end table
@section Examples
@subsection Echo server
@example
tlsd -e cat
@end example
@subsection Authentication
@example
tlsd -p 5556 -- sh -c 'echo "Hello, $@{SHA256@}! I am a $@{SIDE@}."'
@end example
@subsection Connection initiation
@example
echo 'localhost 5600' | tlsd -e echo 'hello'
@end example
@section Signals
The following signals are handled:
@table @asis
@item @code{SIGINT}, @code{SIGTERM}
Terminate gracefully.
@item @code{SIGHUP}
Reload key and certificate.
@end table
@node Usage
@chapter TLSd usage
@section Initiating connections
TLSd reads space-separated hosts and services (ports) from its
@code{stdin}, and initiates connections with those.
@section Child processes
When TLSd runs child processes, it sets the following environment
variables:
@table @env
@item SHA256
Peer's fingerprint: SHA256 hash of their certificate.
@item SIDE
Either @samp{CLIENT} or @samp{SERVER}, indicates what side of the
connection we are on.
@end table
A child process can read peer's messages from @code{stdin}, and send
messages to a peer by writing them into @code{stdout}.
@node fp2alias
@chapter fp2alias
fp2alias is a helper program for TLSd. It reads the @env{SHA256}
environment variable, adds the @env{ALIAS} variable by looking it up in
a file, and runs a given command with that variable in the environment.
If it's not allowed to add new aliases, it would reject unknown users.
@section Invocation
@example
fp2alias [option ...] [--] [ [argument ...]]
@end example
@subsection Command line arguments
@table @option
@item -f @var{certfile}
A file with "@emph{fingerprint} @emph{alias}" entries (default is
@file{/etc/tls/aliases}).
@item -a
Add new aliases. This basically turns a private service into a public
one.
@item -i @var{ident}
Syslog identifier to use.
@item -e
Print messages into stderr, in addition to syslog.
@item -h
Print a help message and exit.
@end table
@subsection Examples
@subsubsection Authentication
@example
tlsd -- fp2alias -- sh -c 'echo "Hello, $@{ALIAS@}!"'
@end example
@node std2fifo
@chapter std2fifo
std2fifo is a helper program for TLSd. Given a root directory and an
environment variable, it creates a "//" directory,
writes input into the "out" FIFO in that directory, and prints the "in"
FIFO output.
Overall, it tries to be suitable for use with TLSd (or other
super-servers), and with common tools on the other end.
@section Invocation
@example
std2fifo [option ...] [--]
@end example
@subsection Command line arguments
@table @option
@item -v @var{var}
An environment variable name (default is @env{SHA256}).
@item -c
Continuous streams mode: do not reopen streams once they are closed, and
do not close the "out" stream after each message. It is intended for
applications such as file transfer, as opposed to textual messaging.
@item -i @var{ident}
Syslog identifier to use.
@item -e
Print messages into stderr, in addition to syslog.
@item -h
Print a help message and exit.
@end table
@subsection Examples
@subsubsection Testing
@example
FOO=bar std2fifo -v FOO -ce /tmp/
@end example
@subsubsection Per-connection FIFO pairs
@example
tlsd -p 5601 -e -- std2fifo -e /var/lib/tlsd-im/
@end example
@node Common tools
@chapter Common tools
Some of the tools that are handy to use with TLSd are mentioned
here. See their documentation for more information.
@section Certificate generation
To generate X.509 certificates that are needed for mutual
authentication, one can use GnuTLS:
@example
certtool --generate-privkey --outfile key.pem
certtool --generate-self-signed --load-privkey key.pem --outfile cert.pem
@end example
Or OpenSSL:
@example
openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365
@end example
Add @option{-nodes} to the above command in order to generate an
unencrypted key, for use with @command{tlsd}.
@section Client connection
To connect to a TLS server, one can also use GnuTLS:
@example
gnutls-cli --insecure --x509keyfile=key.pem --x509certfile=cert.pem \
--port=5556 localhost
@end example
Or OpenSSL:
@example
openssl s_client -key key.pem -cert cert.pem -connect localhost:5556
@end example
Or ncat:
@example
nc --ssl --ssl-key key.pem --ssl-cert cert.pem localhost 5556
@end example
It may be handy to set aliases to those commands in your shell.
@subsection rlwrap
@command{rlwrap} (readline wrapper) improves text input, and makes the
above clients usable for chat-like applications.
@section Tor
Tor hidden services are useful not just for privacy, but also to bypass
NATs, and to have the same address anywhere you go. To setup a hidden
service, simply add into @file{/etc/tor/torrc} something like the
following:
@example
HiddenServiceDir /var/lib/tor/my-service/
HiddenServicePort 5556 127.0.0.1:5556
@end example
Reload Tor, and @file{/var/lib/tor/my-service/hostname} should contain
your new hostname.
The clients should be able to connect simply by prefixing their commands
with @command{torify}, and using that hostname.
When running @command{torify tlsd}, @command{torify} may not like
binding it to 0.0.0.0, but it can be allowed in
@file{/etc/tor/torsocks.conf}. Or just bind to 127.0.0.1, if you don't
want direct incoming connections anyway.
If you wish to remain anonymous, extra care should be taken. This manual
doesn't cover the topic of anonymity.
@section SSH
SSH port forwarding is handy for NAT traversal as well, if you have a
remote server: just @command{ssh -R 5600:0.0.0.0:5600 example.com} to
forward incoming connections to your machine.
@node Writing services
@chapter Writing services
A service program is expected to write output into its @code{stdout},
read input from @code{stdin}, and exit when its @code{stdin} is closed
(or upon receiving a signal, which should be specified for
@command{tlsd} in that case).
While TLSd itself doesn't demand much, a service easily usable with
standard tools requires some care to design. It is suggested to make the
services usable without special client software, with basic shell
commands only. Essentially, to follow the Unix philosophy, and e.g. not
to make up a context-free grammar (one that can't be parsed with regular
expressions properly) where a regular grammar or no parsing at all would
suffice.
Making the services reusable with other similar super-servers (such as
@command{inetd}, @command{nc -le}, or systemd socket activation) and/or
as interactive programs for local use could also be a good idea.
@menu
* Security:: Security tips.
* Sample chat:: Designing and setting up a chat.
* Sample file server:: Designing a file server.
* Sample P2P IM:: Setting up instant messaging.
@end menu
@node Security
@section Security
TLSd tries to be simple and minimalistic; it doesn't do much to improve
security, but leaves that to a user. A few tips to consider:
@itemize @bullet
@item
Set users, groups, and file permissions properly.
@item
Use sandboxing, e.g. SELinux's @command{sandbox}: @command{tlsd --
sandbox -M my-service}.
@item
Isolate worker processes from master process. For instance, only run a
small client via @command{tlsd}, which would connect to a daemon that
provides actual service, and runs as a separate user (which possibly has
privileges to do what @command{tlsd} can't, but can't read your private
keys).
@item
Limit resource usage (with cgroups, @command{ulimit}, etc).
@item
Use virtualization or dedicated machines.
@item
Use safe languages. For the sake of portability and ease of building,
TLSd itself is written in C, but it's quite a risk.
@end itemize
@node Sample chat
@section Sample chat
Let's make a multi-user chat. Ncat can broker connections, so we can set
it as a local daemon, and run more @command{nc} instances as
@command{tlsd} services, using @command{fp2alias} to obtain aliases, and
a basic shell script to prepend those aliases to messages.
Well, here goes the chat service:
@example
@verbatiminclude examples/chat/tlsd-chat.sh
@end example
To try it:
@example
nc -vl --broker 127.0.0.1 7000
tlsd -p 5600 -- fp2alias -a -- examples/chat/tlsd-chat.sh
rlwrap nc --ssl --ssl-key key.pem --ssl-cert cert.pem localhost 5600
@end example
It is usually handy to set daemons to be run by your init system; for
systemd, there are example service files in the @file{examples/chat/}
directory.
@subsection Client scripting
A client can connect with either @command{rlwrap} and some TLS client,
or a custom program. Or an option between those -- a custom shell
script. For instance, to add a bell when one's name is mentioned, they
can use a script that looks like this:
@example
@verbatiminclude examples/chat/tls-chat.sh
@end example
@node Sample file server
@section Sample file server
Let's make a file server now. One can actually use @command{nginx} and
@command{curl} (and minor HTTP abuse) instead, possibly in combination
with @command{scp} or @command{rsync}, but let's do it anyway -- because
we can, and quite easily.
@subsection Preparation
Assuming that the certificates are already set, let's create a directory
for files, and make it accessible to both tlsd and our regular user:
@example
$ sudo mkdir -p /srv/tlsd/files/
$ sudo chown -R tlsd:tlsd /srv/tlsd/
$ sudo chmod -R g+w,o-r /srv/tlsd/
$ sudo gpasswd -a $USER tlsd
@end example
@subsection File serving
To download files with common tools, a bare minimum is a Gopher-like
protocol where users send file selectors, server sends files, and drops
the connection. Something like this should do:
@example
@verbatiminclude examples/file-server/serve-files.sh
@end example
Let's try it:
@example
$ echo foo > /srv/tlsd/files/bar
$ tlsd -ep 5601 -- serve-files.sh &> tlsd-output &
$ echo bar | openssl s_client -key ~/.tls/key.pem -cert ~/.tls/cert.pem \
-quiet -connect localhost:5601
@end example
@subsection File browsing
The serving works fine, but we don't have a way to browse files yet. For
that, we can use @command{ls}, and perhaps not drop connections: make a
browser for textual files and directory listings. Otherwise protocol can
be the same, no need to complicate things:
@example
@verbatiminclude examples/file-server/browse-files.sh
@end example
@subsection File upload
Finally, there should be file upload -- but with some authorization. We
can make users to upload files into per-user directories, and the
presence of a directory itself would mean authorization; to identify
users easier (i.e., not by a SHA256 hash), @command{fp2alias} should be
handy. As of the protocol, it may be similar to the other two: a client
sends a selector followed by a file, and then drops a connection. Here
it goes:
@example
@verbatiminclude examples/file-server/accept-files.sh
@end example
Let's see how that works:
@example
$ tlsd -ep 5603 -- fp2alias -- accept-files.sh &> tlsd-output &
[1] 14063
$ cat <(echo 'my-file') - | openssl s_client -key ~/.tls/key.pem \
-cert ~/.tls/cert.pem -connect localhost:5603
# openssl output skipped
hello
# pressing C-d
DONE
$ fg 1
tlsd -ep 5603 -- fp2alias -- accept-files.sh &>tlsd-output
^C
$ tail /srv/tlsd/files/$USER/my-file
hello
@end example
@node Sample P2P IM
@section Sample peer-to-peer instant messaging
Let's make an IM now.
@subsection Daemon
We can use @command{tlsd} in combination with @command{std2fifo} to get
a nice @command{ii}-like filesystem layout, to begin with. The daemon
itself could look like this:
@example
tlsd -p 18765 -- std2fifo /var/lib/tlsd-im/
@end example
But we also need to restrict connections, allowing just one per
certificate:
@example
tlsd -p 18765 -- sh -c 'flock -n "/var/lib/tlsd-im/$@{SHA256@}/lock" \
std2fifo /var/lib/tlsd-im/'
@end example
But since we'll be running it in background, some kind of a control
channel should be used. And Tor would be useful to bypass NATs, and some
minor checks would be needed to set file permissions, so the final
couple of scripts, @file{tlsd-im-cmd.sh} and @file{tlsd-im.sh}:
@example
@verbatiminclude examples/p2p-im/tlsd-im-cmd.sh
@end example
@example
@verbatiminclude examples/p2p-im/tlsd-im.sh
@end example
@subsection Connecting
Now we can connect to another @command{tlsd} instance (or any other TLS
server) with a command like this:
@example
echo 'example.com 18765' > /var/lib/tlsd-im/connect
@end example
But we want automatic connection restoration on disconnect. So let's put
peer addresses into ``address'' files inside of their directories. Then
we can write a basic script, and set a cron job or a systemd timer for
it:
@example
@verbatiminclude examples/p2p-im/tlsd-im-reconnect.sh
@end example
It is not reliable (consider simulatneous connection initiation from
both ends: both could fail, but only one should) and can be improved,
but that's just a few lines of a shell script.
@subsection UI
The layout we've got:
@example
/var/lib/tlsd-im/
|-- /
| |-- in
| |-- out
| |-- address
| `-- lock
|-- /
| |-- in
| |-- out
| |-- address
| `-- lock
`-- connect
@end example
To make it nicer, we can set aliases with @command{ln}, but it's not
that great without specialized UI, and different users like different
UIs. Fortunately, there is libpurple that powers different IM clients
(pidgin and bitlbee among them), so we can write a plugin for it.
An example plugin can be found in the @file{examples/p2p-im/} directory,
along with a @file{Makefile} and the above shell scripts. Once the
@file{/var/lib/tlsd-im/} directory is specified as the username, the
plugin simply interacts with those FIFOs, and keeps track of newly
created directories using @code{inotify}. IM clients such as pidgin and
bitlbee allow to set local aliases, so we can leave it up to them.
@subsection Setup
What's left is to set it up: run @command{tlsd-im.sh} in background, run
@command{tlsd-im-reconnect.sh} automatically from time to
time. Unfortunately, the right ways to run user daemons seem to vary
among GNU/Linux distributions even more than init systems do. Besides,
@command{bitlbee} normally runs under a separate user, while clients
such as Pidgin run under our regular user.
One way to solve this is to just set it system-wide, adding all the
users that should be able to access it (such as bitlbee and/or our
regular user) into a dedicated group:
@example
@verbatiminclude examples/p2p-im/approximate-setup.sh
@end example
Then one should be able to use it with bitlbee, Pidgin, or other
libpurple-based IM clients. Online status and message delivery tracking
are not great, and generally it can be much better even with plain TLS,
but a usable P2P IM is ready.
@bye