Key signing parties are a pain and hopefully, one day, we will have better ways to authentication keys than reading hexadecimal strings out loud.

The Zimmermann–Sassaman key-signing protocol makes them much more bearable already by having only one single hexadecimal string read out loud. That string is the cryptographic hash of a document given to every participant listing all participants and their fingerprints. If everyone has the same hash, then we assume that everyone has the same document. Then, participants in turn will confirm that they fully recognize the fingerprint listed in the document.

Alexander Wirt wrote a small key server dedicated to receive keys from the participants. There is also a script that will generate the document from the submitted keys and a ready-to-use keyring. The latter can be run automatically using inoticoming when a new key arrives. Finally, it would be nice if participants could confirm that their key has been properly added to the document, e.g. by making the list available on a web server.

Setting all this up seemed like a good opportunity to play with Tor hidden services and systemd-nspawn.

Here's the setup log with some comments. This was done on a small armhf device with Debian Jessie.

Create a new hidden service

Edit /etc/tor/torrc on the host to setup the hidden service:

HiddenServiceDir /var/lib/tor/ksp/
HiddenServicePort 80 10.0.0.2:80
HiddenServicePort 11371 10.0.0.2:11371

Run:

host# systemctl reload tor.service

Then, to learn the name of the newly created hidden service name:

host# cat /var/lib/tor/ksp/hostname
ksp123456789abcd.onion

Install the container

debootstrap as always:

host# debootstrap --variant=minbase jessie /var/lib/container/ksp

Preliminary container configuration

We do the following step simply using chroot as we are going to use the host network configuration for this stage. The container itself will not have access to the Internet.

host# chroot ksp

Let's set the hostname:

ksp-chroot# echo 'ksp' > /etc/hostname

Set up APT:

ksp-chroot# echo 'deb http://httpredir.debian.org/debian jessie main' > /etc/apt/sources.list
ksp-chroot# apt update

We need dbus to get systemd to work well:

ksp-chroot# apt-get install dbus

Make sure that we can resolve our own hostname:

ksp-chroot# apt-get install libnss-myhostname
ksp-chroot# sed -e '/^hosts:/s/files/myhostname \0/' -i /etc/nsswitch.conf

These are dependencies of the keyserver:

ksp-chroot# apt-get install --no-install-recommends libhttp-daemon-perl \
                liblog-loglite-perl libproc-reliable-perl

These ones are needed for the script generating the list:

ksp-chroot# apt-get install bzip2 inoticoming

And we will use the smallest HTTP server available:

ksp-chroot# apt-get install netcat-traditional micro-httpd

Finally, let's unconfigure all DNS resolvers:

ksp-chroot# echo > /etc/resolv.conf

And we are done with the chroot:

ksp-chroot# exit

Let's retrieve the ksp-tools repository now:

host# cd /var/lib/container/srv
host# git clone https://github.com/formorer/ksp-tools

Container setup

We will now start the container with a shell to configure it:

host# systemd-nspawn -D ksp --network-veth

Let's ask systemd to configure the network for us:

ksp# systemctl enable systemd-networkd

Let's not forget to set a root password:

ksp# passwd

We add a dedicated user to run the keyserver and the list generation script:

ksp# adduser --system --group --disabled-password --disabled-login --home /var/lib/ksp ksp

Let's configure the keyserver:

ksp# cp /srv/ksp-tools/keyserver.conf /var/lib/ksp/keyserver.conf

Let's edit /var/lib/ksp/keyserver.conf:

homedir = /var/lib/ksp

Now create the GnuPG homedir for the keyserve:

ksp# mkdir /var/lib/ksp/keys
ksp# install -d -o ksp -g ksp -m 0700 /var/lib/ksp/keys/gpg

Copy the template list generator:

ksp# cp -r /srv/ksp-tools/example /var/lib/ksp/keys/ksp123456789abcd_onion

Create the key repository:

ksp# install -d -o ksp -g ksp -m 0700 /var/lib/ksp/keys/ksp123456789abcd_onion/keys

Create a directory accessible to the web server where the participant list will be generated:

ksp# mkdir -p /var/www
ksp# install -d -o ksp -g ksp -m 0755 /var/www/keys

Let's configure the list generation script by editing /var/lib/ksp/keys/ksp123456789abcd_onion/conf/vars:

KS=ksp123456789abcd.onion
export GNUPGHOME=/tmp/ksp-gpg
KSPFILE="/var/www/keys/ksp-event.txt"

Don't forget to adjust the header in /var/lib/ksp/keys/ksp123456789abcd_onion/conf/list-header.

Now we create a unit file for the keyserver in /etc/systemd/system/keyserver.service:

[Unit]
Description=Key signing party keyserver

[Service]
Type=simple
Environment="KSP_HOMEDIR=/var/lib/ksp"
ExecStart=/srv/ksp-tools/bin/kspkeyserver.pl --nodaemonize
User=ksp
Group=ksp
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=full
ReadOnlyDirectories=/
ReadWriteDirectories=-/var/lib/ksp
CapabilityBoundingSet=CAP_NET_BIND_SERVICE

[Install]
WantedBy=multi-user.target

Another unit for the list generator as /etc/systemd/system/ksp-list-generator.service:

[Unit]
Description=Key signing party list generator

[Service]
Type=simple
EnvironmentFile=/var/lib/ksp/keys/ksp123456789abcd_onion/conf/vars
ExecStart=/usr/bin/inoticoming --foreground /var/lib/ksp/keys/ksp123456789abcd_onion/keys --chdir /var/lib/ksp/keys/ksp123456789abcd_onion bin/generate-list \;
User=ksp
Group=ksp
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=full
ReadOnlyDirectories=/
ReadWriteDirectories=/var/www/keys
CapabilityBoundingSet=

[Install]
WantedBy=multi-user.target

For the web server, we first configure a socket listening on port 80 in /etc/systemd/system/micro-httpd.socket:

[Unit]
Description=micro-httpd socket

[Socket]
ListenStream=80
Accept=yes

[Install]
WantedBy=sockets.target

And then the web server in /etc/systemd/system/micro-httpd@.service:

[Unit]
Description=micro-httpd server

[Service]
ExecStart=-/usr/sbin/micro-httpd /var/www/ksp
StandardInput=socket
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=full
ReadOnlyDirectories=/
CapabilityBoundingSet=

Let's now ask systemd to start all of these at boot time:

ksp# systemctl daemon-reload
ksp# systemctl enable keyserver.service
ksp# systemctl enable ksp-list-generator.service
ksp# systemctl enable micro-httpd.socket

One way to kill the container is to type Control+] three times.

Boot the container

Let's get this party started!

host# systemd-nspawn -b -D /var/lib/container/ksp --network-veth

Hopefully, things should work now. Participants to the KSP should then be able to send their key with:

$ torsocks gpg --keyserver ksp123456789abcd.onion --send-key $KEYID

(Sadly, this is broken with GnuPG 2.1 at the moment.)

The participant list should be available at http://ksp123456789abcd.onion/ksp-event.txt.

Final steps

We need to tell systemd to start the container started at boot time:

host# systemctl enable systemd-nspawn@ksp.service

But the default command-line will not use a dedicated network, so we need to override that part of the configuration. First create a directory:

host# mkdir /etc/systemd/system/systemd-nspawn@ksp.service.d

And edit /etc/systemd/system/systemd-nspawn@ksp.service.d/use-network-veth.conf:

[Service]
# The empty line because we want to override all previous ExecStart
# and not add an extra command
ExecStart=
ExecStart=/usr/bin/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --directory=/var/lib/container/%i --network-veth

Let's reload systemd and verify that our snippet is there:

host# systemctl daemon-reload
host# systemctl cat systemd-nspawn@ksp.service

All good? Let's start it:

host# systemctl start systemd-nspawn@ksp.service

One should also add a firewall to disallow any outgoing connections from the ve-ksp interface as an extra protection.