aboutsummaryrefslogtreecommitdiff
path: root/rcynic/README
blob: bd51a85b6426ec349f971f2779bd645c7307a91f (plain) (blame)
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
-*- Text -*- $Id$

"Cynical rsync" -- fetch and validate RPKI certificates.

This is the C implementation.  It's still rough in places, but it
appears to work, and at least for the current test data available from
APNIC and RIPE it produces the same results as my Perl prototype did.

To build this you will need to link it against an OpenSSL libcrypto
that has support for the RFC 3779 extensions.  See ../openssl/README.
 
I developed this code on FreeBSD 6-STABLE and have not (yet) tested it
on any other platform; as far as I know I have not used any seriously
non-portable features, but neither have I done a POSIX reference
manual lookup for every function call.  Please report any portability
problems.

All certificates and CRLs are in DER format, with filenames derived
from the RPKI rsync URIs at which the data are published.  At some
point I'll probably write a companion program to convert a tree of DER
into the hashed directory of PEM format that most OpenSSL applications
expect.

All configuration is via an OpenSSL-style configuration file, except
for selection of the name of the configuration file itself.  A few of
the parameters can also be set from the command line, to simplify
testing.  The default name for the configuration is rcynic.conf; you
can override this with the -c option on the command line.  The config
file uses OpenSSL's config file syntax, and you can set OpenSSL
library configuration paramaters (eg, "engine" settings) in the config
file as well.  rcynic's own configuration parameters are in a section
called "[rcynic]".

Most configuration parameters are optional and have defaults that
should do something reasonable if you are running rcynic in a test
directory.  If you're running it as a system progran, perhaps under
cron, you'll want to set additional parameters to tell rcynic where to
find its data and where to write its output.

The one thing you MUST specify in the config file in order for the
program to do anything useful is file name of one or more trust
anchors.  Trust anchors for this program are represented as
DER-formated X509 objects that look just like certificates, except
that they're trust anchors.  To date I have only tested this code with
self-signed trust anchors; in theory, this is not required, in
practice the code may require tweaks to support other trust anchors.

Example of a minimal config file:

    [rcynic]

    trust-anchor.0 = trust-anchors/apnic-trust-anchor.cer
    trust-anchor.1 = trust-anchors/ripe-ripe-trust-anchor.cer
    trust-anchor.2 = trust-anchors/ripe-arin-trust-anchor.cer

By default, rcynic uses three writable directory trees:

- unauthenticated	Raw data fetched via rsync.  In order to take
			full advantage of rsync's optimized transfers,
			you should preserve and reuse this directory
			across rcynic runs, so that rcynic need not
			re-fetch data that have not changed.

- authenticated		Data that rcynic has checked.  This is the
			real output of the process.

- old_authenticated	Saved results from immediately previous rcynic
			run, used when attempting to recover from
			certain kinds of errors.

rcynic renames the authenticated tree to become the old_authenticated
tree when it starts up, then builds a new authenticated tree.

rcynic copies the trust anchors themselves into the top level
directory of the authenticated tree xxxxxxxx.n.cer, where xxxxxxxx and
n are the OpenSSL object name hash and index within the resulting
virtual hash bucket (the same as the c_hash Perl script that comes
with OpenSSL would produce), and ".cer" is the literal string ".cer".
The reason for this is that trust anchors, by definition, are not
fetched automatically, and thus do not really have publication URIs in
the sense that every other object in these trees do.  So rcynic uses a
naming scheme which insures (a) that each trust anchor has a unique
name within the output tree and (b) that trust anchors cannot be
confusd with certificates: trust anchors always go in the top level of
the tree, data fetched via rsync always go in subdirectories.

As currently implemented, rcynic does not attempt to maintain an
in-memory cache of objects it might need again later.  It does keep an
internal cache of the URIs from which it has already fetched data in
this pass, and it keeps a stack containing the current certificate
chain as it does its validation walk.  All other data (eg, CRLs) are
freed immediately after use and read from disk again as needed.  From
a database design standpoint, this is not very efficient, but as the
rcynic's main bottlenecks are expected to be crypto and network
operations, it seemed best to keep the design as simple as possible,
at least until execution profiling demonstrates a real issue.

Usage and configuration:

Logging levels:

rcynic has its own system of logging levels, similar to what syslog()
uses but customized to the specific task rcynic performs.  Levels:

 log_sys_err		Error from operating system or library
 log_usage_err		Bad usage (local configuration error)
 log_data_err		Bad data (broken certificates or CRLs)
 log_telemetry		Normal chatter about rcynic's progress
 log_verbose		Extra verbose chatter
 log_debug		Only useful when debugging

Command line options:

 -c configfile	Path to configuration file (default: rcynic.conf)
 -l loglevel	Logging level (default: log_telemetry)
 -s		Log via syslog
 -t		Log via stdout/stderr when also using syslog
 -p		Ask syslog() to send to stderr too
 -j		Start-up jitter interval (see below; default: 600)

Configuration file:

rcynic uses the OpenSSL libcrypto configuration file mechanism.  All
libcrypto configuration options (eg, for engine support) are
available.  All rcynic-specific options are in the "[rcynic]"
section.  You -must- have a configuration file in order for rcynic to
do anything useful, as the configuration file is the only way to list
your trust anchors.

Configuration variables:

authenticated		Path to output directory (where rcynic should
			place objects it has been able to validate).
			Default: rcynic-data/authenticated

old-authenticated	Path to which rcynic should rename the output
			directory (if any) from the previous rcynic
			run.  rcynic preserves the previous run's
			output directory both as a backup data source
			for the current run and also so that you don't
			lose all your state if rcynic chokes and
			dies.  Default: rcynic-data/authenticated.old


unauthenticated		Path to directory where rcynic should store
			unauthenticatd data retrieved via rsync.
			Unless something goes horribly wrong, you want
			rcynic to preserve and reuse this directory
			across runs to minimize the network traffic
			necessary to bring your repository mirror up
			to date.  Default: rcynic-data/unauthenticated

rsync-timeout		How long (in seconds) to let rsync run before
			terminating the rsync process, or zero for no
			timeout.  You want this timeout to be fairly
			long, to avoid terminating rsync connections
			prematurely.  It's present to let you defend
			against evil rsync server operators who try to
			tarpit your connection as a form of denial of
			service attack on rcynic.  Default: no timeout
			(but this may change, best set it explictly).


rsync-program		Path to the rsync program.  Default: rsync,
			but you should probably set this variable
			rather than just trusting the PATH environment
			variable to be set correctly.

log-level		Same as -l option on command line.  Command
			line setting overrides config file setting.
			Default: log_telemetry

use-syslog		Same as -s option on command line.  Command
			line setting overrides config file setting.
			Values: true or false.  Default: false

use-stdouterr		Same as -t option on command line.  Command
			line setting overrides config file setting.
			Values: true or false.  Default: false

syslog-perror		Same as -p option on command line.  Command
			line setting overrides config file setting.
			Values: true or false.  Default: false

syslog-facility		Syslog facility to use.  Default: local0


syslog-priority-xyz	(where xyz is an rcynic logging level, above)
			Override the syslog priority value to use when
			logging messages at this rcynic level.
			Defaults:

			syslog-priority-log_sys_err:	err
			syslog-priority-log_usage_err:	err
			syslog-priority-log_data_err:	notice
			syslog-priority-log_telemetry:	info
			syslog-priority-log_verbose:	info
			syslog-priority-log_debug:	debug

jitter			Startup jitter interval, same as -j option on
			command line.  Jitter interval, specified in
			number of seconds.  rcynic will pick a random
			number within the interval from zero to this
			value, and will delay for that many seconds on
			startup.  The purpose of this is to spread the
			load from large numbers of rcynic clients all
			running under cron with synchronized clocks,
			in particular to avoid hammering the RPKI
			rsync servers into the ground at midnight UTC.
			Default: 600

lockfile		Name of lockfile, or empty for no lock.  If
			you run rcynic under cron, you should use this
			parameter to set a lockfile so that successive
			instances of rcynic don't stomp on each other.
			Default: no lock



Running rcynic chrooted

[This is only a sketch, needs details and finicky proofreading]

rcynic does not include any direct support for running chrooted, but
is designed to be (relatively) easy to run in a chroot jail.  Here's
how.

You'll either need staticly linked copies of rcynic and rsync, or
you'll need to figure out which shared libraries these programs need
(try using the "ldd" command).  Here we assume staticly linked
binaries, because that's simpler.

You'll need a chroot wrapper program.  Your platform may already have
one (FreeBSD does -- /usr/sbin/chroot), but if you don't, you can
download Wietse Venema's "chrootuid" program from:

  ftp://ftp.porcupine.org/pub/security/chrootuid1.3.tar.gz

Warning: The chroot program included in at least some Linux
distributions is not adaquate to this task, you need a wrapper that
knows how to drop privileges after performing the chroot() operation
itself.  If in doubt, use chrootuid.

Unfortunately, the precise details of setting up a proper chroot jail
vary wildly from one system to another, so the following instructions
will likely not be a precise match for the preferred way of doing this
on any particular platform.  We have sample scripts that do the right
thing for FreeBSD, feel free to contribute such scripts for other
platforms.

Step 1: Build the static binaries.  You might want to test them at
this stage too, although you can defer that until after you've got the
jail built.

Step 2: Create a userid under which to run rcynic.  Here we'll assume
that you've created a user "rcynic", whose default group is also named
"rcynic".  Do not add any other userids to the rcynic group unless you
really know what you are doing.

Step 3: Build the jail.  You'll need, at minimum, a directory in which
to put the binaries, a subdirectory tree that's writable by the userid
which will be running rcynic and rsync, your trust anchors, and
whatever device inodes the various libraries need on your system.
Most likely the devices that matter will be /dev/null, /dev/random,a
nd /dev/urandom; if you're running a FreeBSD system with devfs, you
do this by mounting and configuring a devfs instance in the jail, on
other platforms you probably use the mknod program or something.

Important: other than the directories that you want rcynic and rsync
to be able to modify, -nothing- in the initial jail setup should be
writable by the rcynic userid.  In particular, rcynic and rsync should
-not- be allowed to modify: their own binary images, any of the
configuration files, or your trust anchors.  It's simplest just to
have root own all the files and directories that rcynic and rsync are
not allowed to modify.

Sample jail tree, assuming that we're putting all of this under
/var/rcynic:

 # mkdir /var/rcynic
 # mkdir /var/rcynic/bin
 # mkdir /var/rcynic/data
 # mkdir /var/rcynic/dev
 # mkdir /var/rcynic/etc
 # mkdir /var/rcynic/etc/trust-anchors

Copy your trust anchors into /var/rcynic/etc/trust-anchors.

Copy the staticly linked rcynic and rsync into /var/rcynic/bin.

Copy /etc/resolv.conf and /etc/localtime (if it exists) into
/var/rcynic/etc.

Write an rcynic configuration file as /var/rcynic/etc/rcynic.conf
(path names in this file must match the jail setup, more below).

 # chmod -R go-w /var/rcynic
 # chown -R root:wheel /var/rcynic
 # chown -R rcynic:rcynic /var/rcynic/data

If you're using devfs, arrange for it to be mounted at
/var/rcynic/dev; otherwise, create whatever device inodes you need in
/var/rcynic/dev and make sure that they have sane permissions (copying
whatever permissions are used in your system /dev directory should
suffice).

rcynic.conf to match this configuration:

  [rcynic]

  trust-anchor.1	= /etc/trust-anchors/ta-1.cer
  trust-anchor.2	= /etc/trust-anchors/ta-2.cer
  trust-anchor.3	= /etc/trust-anchors/ta-3.cer

  rsync-program		= /bin/rsync
  authenticated		= /data/authenticated
  old-authenticated	= /data/authenticated.old
  unauthenticated	= /data/unauthenticated

Once you've got all this set up, you're ready to try running rcynic in
the jail.  Try it from the command line first, then if that works, you
should be able to run it under cron.

Note: chroot, chrootuid, and other programs of this type are usually
intended to be run by root, and should -not- be setuid programs unless
you -really- know what you are doing.

Sample command line:

  # /usr/local/bin/chrootuid /var/rcynic rcynic /bin/rcynic -s -c /etc/rcynic.conf

Note that we use absolute pathnames everywhere.  This is not an
accident.  Programs running in jails under cron should not make
assumptions about the current working directory or environment
variable settings, and programs running in chroot jails would need
different PATH settings anyway.  Best just to specify everything.

Building static binaries:

On FreeBSD, building a staticly linked rsync is easy: just set the
environment variable LDFLAGS='-static' before building the rsync port
and the right thing will happen.  Since this is really just GNU
configure picking up the environment variable, the same trick should
work on other platforms.

For simplicity, I've taken the same approach with rcynic, so 

  $ make LDFLAGS='-static'

should work.

syslog:

Depending on your syslogd configuration, syslog may not work properly
with rcynic in a chroot jail.  On FreeBSD, the easiest way to fix this
is to add the following lines to /etc/rc.conf:

    altlog_proglist="named rcynic"
    rcynic_chrootdir="/var/rcynic"
    rcynic_enable="YES"



# Sample script to create a jail for rcynic on FreeBSD.

#!/bin/sh -
# $Id$
#
# Create a chroot jail for rcynic.  You need to build staticly linked
# rcynic and rsync binaries and install them in the jail yourself.
#
# Cobbled together from bits and pieces of existing system scripts,
# mostly /usr/ports/mail/postfix/pkg-install and /etc/rc.d/named.

jaildir="/var/rcynic"
jailuser="rcynic"
jailgroup="rcynic"

if /usr/sbin/pw groupshow "${jailgroup}" 2>/dev/null; then
    echo "You already have a group \"${jailgroup}\", so I will use it."
elif /usr/sbin/pw groupadd ${jailgroup}; then
    echo "Added group \"${jailgroup}\"."
else
    echo "Adding group \"${jailgroup}\" failed..."
    echo "Please create it, and try again."
    exit 1
fi

if /usr/sbin/pw usershow "${jailuser}" 2>/dev/null; then
    echo "You already have a user \"${jailuser}\", so I will use it."
elif /usr/sbin/pw useradd ${jailuser} -g ${jailgroup} -h - -d /nonexistant -s /usr/sbin/nologin -c "RPKI validation system"; then
    echo "Added user \"${jailuser}\"."
else
    echo "Adding user \"${jailuser}\" failed..."
    echo "Please create it, and try again."
    exit 1
fi

if ! /bin/test -d "${jaildir}"; then
    /bin/mkdir "${jaildir}"
fi

/usr/sbin/mtree -deU -p "${jaildir}" <<EOF

    /set type=dir uname=root gname=wheel mode=0555
    .
	bin
	..
	dev
	..
	etc
	    trust-anchors
	    ..
	..
	var
	    run
	    ..
	..
	data	uname=$jailuser gname=$jailgroup mode=0755
	..
    ..

EOF

/sbin/umount "${jaildir}/dev" 2>/dev/null
if ! /sbin/mount -t devfs dev "${jaildir}/dev"; then
    echo "Mounting devfs on ${jaildir}/dev failed..."
    exit 1
fi
/sbin/devfs -m "${jaildir}/dev" rule apply hide
/sbin/devfs -m "${jaildir}/dev" rule apply path null unhide
/sbin/devfs -m "${jaildir}/dev" rule apply path random unhide

for i in /etc/localtime /etc/resolv.conf; do
    j="${jaildir}${i}"
    if /bin/test -r "$i" && ! /usr/bin/cmp -s "$i" "$j"; then
	/bin/cp -p "$i" "$j"
	/usr/sbin/chown root:wheel "$j"
	/bin/chmod 444 "$j"
    fi
done

if /bin/test -d trust-anchors; then
    for i in trust-anchors/*.cer; do
	j="$jaildir/etc/trust-anchors/${i##*/}"
	/bin/test -r "$j" && continue
	echo "Copying $i to $j"
	/bin/cp -p "$i" "$j"
	/usr/sbin/chown root:wheel "$j"
	/bin/chmod 444 "$j"
    done
fi

if /bin/test -r "$jaildir/etc/rcynic.conf"; then
    echo "You already have config file \"${jaildir}/etc/rcynic.conf\", so I will use it."
else
    echo "Creating minmal ${jaildir}/etc/rcynic.conf"
    /bin/cat >"${jaildir}/etc/rcynic.conf" <<-EOF
	[rcynic]
	rsync-program		= /bin/rsync
	authenticated		= /data/authenticated
	old-authenticated	= /data/authenticated.old
	unauthenticated		= /data/unauthenticated
	lockfile		= /data/lock
	EOF
    j=1
    for i in $jaildir/etc/trust-anchors/*.cer; do
	echo >>"${jaildir}/etc/rcynic.conf" "trust-anchor.$j		= /etc/trust-anchors/${i##*/}"
	j=$((j+1))
    done
fi

/usr/sbin/chown root:wheel "${jaildir}/etc/rcynic.conf"
/bin/chmod 444 "${jaildir}/etc/rcynic.conf"



# Sample script to run rcynic in a chroot jail on FreeBSD.

#!/bin/sh -
# $Id$
#
# Run rcynic in a chroot jail (which must already be set up)

jaildir="/var/rcynic"
jailuser="rcynic"
jailgroup="rcynic"

/usr/sbin/chroot -u "$jailuser" -g "$jailgroup" "$jaildir" \
    /bin/rcynic -c /etc/rcynic.conf