summaryrefslogtreecommitdiff
path: root/tlsd.texi
blob: 8ecb22f63de66286851529c745e3cf54c4bda1d7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
\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@} <-> <dir>/<env var>/@{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 ...] [--] <command> [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 ...] [--] [<command> [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 "<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.

@section Invocation
@example
std2fifo [option ...] [--] <dir>
@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/
|-- <sha256 hash>/
|   |-- in
|   |-- out
|   |-- address
|   `-- lock
|-- <sha256 hash>/
|   |-- 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