     [file version.texi not included]

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 VERSION, last updated on UPDATED.

TLSd Copying Conditions
***********************

          [file COPYING not included]

TLSd invocation
***************

     tlsd [option ...] [--] <command> [argument ...]

Command line arguments
======================

-k KEYFILE
     Private key file to use (default is /etc/tls/key.pem).

-c CERTFILE
     Certificate file to use (default is /etc/tls/cert.pem).

-p PORT
     Port to listen on (default is to use a randomly selected one).

-b HOST
     Bind address (default is 0.0.0.0).

-s SIGNO
     Send a signal to a child on termination. No signal is sent by default: child processes are expected to exit once their stdin is closed.

-n
     Do not require a peer certificate. This makes the SHA256 environment variable for child processes optional.

-d DIRECTORY
     Write peer certificates in DER format into a directory.

-i IDENT
     Syslog identifier to use.

-e
     Print messages into stderr, in addition to syslog.

-h
     Print a help message and exit.

Examples
========

Echo server
-----------

     tlsd -e cat

Authentication
--------------

     tlsd -p 5556 -- sh -c 'echo "Hello, ${SHA256}! I am a ${SIDE}."'

Connection initiation
---------------------

     echo 'localhost 5600' | tlsd -e echo 'hello'

Signals
=======

The following signals are handled:

SIGINT, SIGTERM
     Terminate gracefully.

SIGHUP
     Reload key and certificate.

TLSd usage
**********

Initiating connections
======================

TLSd reads space-separated hosts and services (ports) from its stdin, and initiates connections with those.

Child processes
===============

When TLSd runs child processes, it sets the following environment variables:

SHA256
     Peer's fingerprint: SHA256 hash of their certificate.

SIDE
     Either CLIENT or SERVER, indicates what side of the connection we are on.

A child process can read peer's messages from stdin, and send messages to a peer by writing them into stdout.

fp2alias
********

fp2alias is a helper program for TLSd. It reads the SHA256 environment variable, adds the 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.

Invocation
==========

     fp2alias [option ...] [--] [<command> [argument ...]]

Command line arguments
----------------------

-f CERTFILE
     A file with "fingerprint alias" entries (default is /etc/tls/aliases).

-a
     Add new aliases. This basically turns a private service into a public one.

-i IDENT
     Syslog identifier to use.

-e
     Print messages into stderr, in addition to syslog.

-h
     Print a help message and exit.

Examples
--------

Authentication
..............

     tlsd -- fp2alias -- sh -c 'echo "Hello, ${ALIAS}!"'

std2fifo
********

std2fifo is a helper program for TLSd. Given a root directory and an environment variable, it creates a "<root dir>/<env var>/" 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.

Invocation
==========

     std2fifo [option ...] [--] <dir>

Command line arguments
----------------------

-v VAR
     An environment variable name (default is SHA256).

-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.

-i IDENT
     Syslog identifier to use.

-e
     Print messages into stderr, in addition to syslog.

-h
     Print a help message and exit.

Examples
--------

Testing
.......

     FOO=bar std2fifo -v FOO -ce /tmp/

Per-connection FIFO pairs
.........................

     tlsd -p 5601 -e -- std2fifo -e /var/lib/tlsd-im/

Common tools
************

Some of the tools that are handy to use with TLSd are mentioned here. See their documentation for more information.

Certificate generation
======================

To generate X.509 certificates that are needed for mutual authentication, one can use GnuTLS:

     certtool --generate-privkey --outfile key.pem
     certtool --generate-self-signed --load-privkey key.pem --outfile cert.pem

Or OpenSSL:

     openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365

Add -nodes to the above command in order to generate an unencrypted key, for use with tlsd.

Client connection
=================

To connect to a TLS server, one can also use GnuTLS:

     gnutls-cli --insecure --x509keyfile=key.pem --x509certfile=cert.pem \
     --port=5556 localhost

Or OpenSSL:

     openssl s_client -key key.pem -cert cert.pem -connect localhost:5556

Or ncat:

     nc --ssl --ssl-key key.pem --ssl-cert cert.pem localhost 5556

It may be handy to set aliases to those commands in your shell.

rlwrap
------

rlwrap (readline wrapper) improves text input, and makes the above clients usable for chat-like applications.

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 /etc/tor/torrc something like the following:

     HiddenServiceDir /var/lib/tor/my-service/
     HiddenServicePort 5556 127.0.0.1:5556

Reload Tor, and /var/lib/tor/my-service/hostname should contain your new hostname.

The clients should be able to connect simply by prefixing their commands with torify, and using that hostname.

When running torify tlsd, torify may not like binding it to 0.0.0.0, but it can be allowed in /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.

SSH
===

SSH port forwarding is handy for NAT traversal as well, if you have a remote server: just ssh -R 5600:0.0.0.0:5600 example.com to forward incoming connections to your machine.

Writing services
****************

A service program is expected to write output into its stdout, read input from stdin, and exit when its stdin is closed (or upon receiving a signal, which should be specified for 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 inetd, nc -le, or systemd socket activation) and/or as interactive programs for local use could also be a good idea.

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:

  * Set users, groups, and file permissions properly.

  * Use sandboxing, e.g. SELinux's sandbox: tlsd -- sandbox -M my-service.

  * Isolate worker processes from master process. For instance, only run a small client via tlsd, which would connect to a daemon that provides actual service, and runs as a separate user (which possibly has privileges to do what tlsd can't, but can't read your private keys).

  * Limit resource usage (with cgroups, ulimit, etc).

  * Use virtualization or dedicated machines.

  * Use safe languages. For the sake of portability and ease of building, TLSd itself is written in C, but it's quite a risk.

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 nc instances as tlsd services, using fp2alias to obtain aliases, and a basic shell script to prepend those aliases to messages.

Well, here goes the chat service:

          [file tlsd-chat.sh not included]

To try it:

     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

It is usually handy to set daemons to be run by your init system; for systemd, there are example service files in the examples/chat/ directory.

Client scripting
----------------

A client can connect with either 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:

          [file tls-chat.sh not included]

Sample file server
==================

Let's make a file server now. One can actually use nginx and curl (and minor HTTP abuse) instead, possibly in combination with scp or rsync, but let's do it anyway -- because we can, and quite easily.

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:

     $ 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

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:

          [file serve-files.sh not included]

Let's try it:

     $ 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

File browsing
-------------

The serving works fine, but we don't have a way to browse files yet. For that, we can use 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:

          [file browse-files.sh not included]

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), 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:

          [file accept-files.sh not included]

Let's see how that works:

     $ 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

Sample peer-to-peer instant messaging
=====================================

Let's make an IM now.

Daemon
------

We can use tlsd in combination with std2fifo to get a nice ii-like filesystem layout, to begin with. The daemon itself could look like this:

     tlsd -p 18765 -- std2fifo /var/lib/tlsd-im/

But we also need to restrict connections, allowing just one per certificate:

     tlsd -p 18765 -- sh -c 'flock -n "/var/lib/tlsd-im/${SHA256}/lock" \
       std2fifo /var/lib/tlsd-im/'

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, tlsd-im-cmd.sh and tlsd-im.sh:

          [file tlsd-im-cmd.sh not included]

          [file tlsd-im.sh not included]

Connecting
----------

Now we can connect to another tlsd instance (or any other TLS server) with a command like this:

     echo 'example.com 18765' > /var/lib/tlsd-im/connect

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:

          [file tlsd-im-reconnect.sh not included]

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.

UI
--

The layout we've got:

     /var/lib/tlsd-im/
     |-- <sha256 hash>/
     |   |-- in
     |   |-- out
     |   |-- address
     |   `-- lock
     |-- <sha256 hash>/
     |   |-- in
     |   |-- out
     |   |-- address
     |   `-- lock
     `-- connect

To make it nicer, we can set aliases with 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 examples/p2p-im/ directory, along with a Makefile and the above shell scripts. Once the /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 inotify. IM clients such as pidgin and bitlbee allow to set local aliases, so we can leave it up to them.

Setup
-----

What's left is to set it up: run tlsd-im.sh in background, run 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, 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:

          [file approximate-setup.sh not included]

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.