Tutorial: GNUnet on Debian 9

Introduction

Welcome to the hopefully painless GNUnet tutorial for Debian 9! It provides very concrete instructions on how to compile, install and configure a current version of GNUnet. The goal is to support newcomers, either end users or developers, who want to get in touch with GNUnet for the first time. After installing GNUnet we will make sure that out new GNUnet installation is working correctly.

Attention: If you came across the official gnunet package for Debian 9, ignore it! It is ancient and not compatible with current GNUnet installations.

Now let's start!

Requirements

First let's install the following Debian 9 packages to use GNUnet painlessly. Optional dependencies are listed in Appendix A. They are required for some experimental GNUnet features.

$ sudo apt install git libtool autoconf autopoint build-essential libgcrypt-dev libidn11-dev zlib1g-dev libunistring-dev libglpk-dev miniupnpc libextractor-dev libjansson-dev libcurl4-gnutls-dev libsqlite3-dev openssl libnss3-tools

Make an installation directory

Next we create a directory in our home directory where we store the source code later. We should keep this directory after installation because it contains Makefiles that can be used for uninstalling GNUnet again (see chapter *Uninstall GNUnet and its dependencies*).

$ mkdir ~/gnunet_installation

Get the source code

We download the GNUnet source code using git. On Debian 9 we need the sources of another library (libmicrohttpd).

Attention: The official libmicrohttpd package for Debian 9 is too old, we need at least version 0.9.52.

$ cd ~/gnunet_installation
$ git clone --depth 1 https://gnunet.org/git/gnunet.git
$ git clone --depth 1 https://gnunet.org/git/gnunet-gtk.git
$ git clone --depth 1 https://gnunet.org/git/libmicrohttpd.git

Compile and Install

Before we can compile GNUnet on Debian 9, we compile and install libmicrohttpd.

$ cd ~/gnunet_installation/libmicrohttpd
$ autoreconf -fi
$ sudo apt install libgnutls28-dev
$ ./configure --disable-doc --prefix=/opt/libmicrohttpd
$ make -j$(nproc || echo -n 1)
$ sudo make install

Installing GNUnet is not hard. We have two options: installing a *production version* and installing a *development version*. If you want to start writing GNUnet applications or join the GNUnet development choose the development version (it will print more debug output and contains debug symbols that can be displayed with a debugger). Otherwise choose the production version.

Option 1: GNUnet for production / usage

$ cd ~/gnunet_installation/gnunet
$ ./bootstrap
$ export GNUNET_PREFIX=/usr
$ ./configure --prefix=$GNUNET_PREFIX --disable-documentation --with-microhttpd=/opt/libmicrohttpd
$ sudo addgroup gnunetdns
$ sudo adduser --system --group --disabled-login --home /var/lib/gnunet gnunet
$ make -j$(nproc || echo -n 1)
$ sudo make install

Option 2: GNUnet for development

$ cd ~/gnunet_installation/gnunet
$ ./bootstrap
$ export GNUNET_PREFIX=/usr
$ export CFLAGS="-g -Wall -O0"
$ ./configure --prefix=$GNUNET_PREFIX --disable-documentation --enable-logging=verbose --with-microhttpd=/opt/libmicrohttpd
$ make -j$(nproc || echo -n 1)
$ sudo make install

Option 3: gnunet-gtk

Installing the Gtk+-based graphical user interface can be done on top of either the production or development installation.

$ sudo apt install libgtk-3-dev libgladeui-dev libunique-dev libqrencode-dev $ cd ~/gnunet_installation/gnunet-gtk
$ ./bootstrap
$ export GNUNET_PREFIX=/usr
$ export CFLAGS="-g -Wall -O0"
$ ./configure --prefix=$GNUNET_PREFIX --with-gnunet=$GNUNET_PREFIX
$ make -j$(nproc || echo -n 1)
$ sudo make install

Configuration

Congratulations! GNUnet is now installed! Before we start it we need to create a configuration file. By default GNUnet looks in our home directory for the file `~/.gnunet/gnunet.conf`. We can start with an empty file for now:

$ touch ~/.config/gnunet.conf

It's reccomended that you increase your bandwidth restrictions from the acutely low defaults. The example below sets the WAN and LAN limits to the value "unlimited".

$ gnunet-config -s ats -o WAN_QUOTA_IN -V unlimited
$ gnunet-config -s ats -o WAN_QUOTA_OUT -V unlimited
$ gnunet-config -s ats -o LAN_QUOTA_IN -V unlimited
$ gnunet-config -s ats -o LAN_QUOTA_OUT -V unlimited

Now we can start it with the command line tool `gnunet-arm` (Automatic Restart Manager).

$ gnunet-arm -s

It starts the default GNUnet services. We can list them with the `-I` option:

$ gnunet-arm -I
Running services:
ats (gnunet-service-ats)
revocation (gnunet-service-revocation)
set (gnunet-service-set)
nat (gnunet-service-nat)
transport (gnunet-service-transport)
peerstore (gnunet-service-peerstore)
hostlist (gnunet-daemon-hostlist)
identity (gnunet-service-identity)
namecache (gnunet-service-namecache)
peerinfo (gnunet-service-peerinfo)
datastore (gnunet-service-datastore)
zonemaster (gnunet-service-zonemaster)
zonemaster-monitor (gnunet-service-zonemaster-monitor)
nse (gnunet-service-nse)
cadet (gnunet-service-cadet)
dht (gnunet-service-dht)
core (gnunet-service-core)
gns (gnunet-service-gns)
statistics (gnunet-service-statistics)
topology (gnunet-daemon-topology)
fs (gnunet-service-fs)
namestore (gnunet-service-namestore)
vpn (gnunet-service-vpn)

For stopping GNUnet again we can use the `-e` option.

$ gnunet-arm -e

Make sure it works

Let's try out some of GNUnet's use cases. Some should be done before others:

filesharing

Let's publish a file in the GNUnet filesharing network. We use the keywords ("commons" and "state") so other people will be able to search for the file.

We can choose any file and describe it with meaningful keywords (using the `-k` command line option).

$ gnunet-publish -k commons -k state ostrom.pdf
Publishing `/home/myself/ostrom.pdf' done.
URI is `gnunet://fs/chk/M57SXDJ72EWS25CT6307KKJ8K0GCNSPTAZ649NA1NS10MJB4A1GZ9EN4Y02KST9VA5BHE8B335RPXQVBWVZ587Y83WQ7J3DHMBX30Q8.DHNGBN4CB2DBX1QRZ1R0B1Q18WTEAK4R94S9D57C9JMJJ3H7SSQDCV4D1218C4S2VP085AMQQSMG18FCP6NQMZQZJ91XR5NBX7YF0V0.42197237'.

Finding the file by keyword works with `gnunet-search`.

$ gnunet-search commons
#1:
gnunet-download -o "ostrom.pdf" gnunet://fs/chk/M57SXDJ72EWS25CT6307KKJ8K0GCNSPTAZ649NA1NS10MJB4A1GZ9EN4Y02KST9VA5BHE8B335RPXQVBWVZ587Y83WQ7J3DHMBX30Q8.DHNGBN4CB2DBX1QRZ1R0B1Q18WTEAK4R94S9D57C9JMJJ3H7SSQDCV4D1218C4S2VP085AMQQSMG18FCP6NQMZQZJ91XR5NBX7YF0V0.42197237

It gives us the command line call to download the file (and store it as ostrom.pdf)!

CADET (and Chat)

We can use the `gnunet-cadet` command line tool to open a port and from another machine connect to this port and chat or transfer data. First we need our *peer ID* of the GNUnet peer opening the port.

$ gnunet-peerinfo -s
I am peer `P4T5GHS1PCZ06R82D3KW8Z8J1113BQZWAWGYHTZ8G1ZXMWXQGAVG'.

Now we open the port (it can be any string!):

$ gnunet-cadet -o my-secret-port

On the other machine we can connect using the peer ID and the port and start chatting!

$ gnunet-cadet P4T5GHS1PCZ06R82D3KW8Z8J1113BQZWAWGYHTZ8G1ZXMWXQGAVG my-secret-port

Name resolution using GNS on the command line

GNS is the GNU name service, a fully decentralized alternatice to DNS. We'll publish an IP address in a GNS record try to resolve it on the command line. First we need an identity which is the equivalent to a zone in DNS. We'll call it "myself" and create it using the `gnunet-identity` command line tool. Instead of "myself" you can surely use your nick or any other name.

$ gnunet-identity -C myself

We can check if it worked using the same tool. We expect the name of our identity and the corresponding public key to be displayed.

$ gnunet-identity -d
myself - HWTYD3P5D77JVFNVMZ1M5T10V4SZYNMY3PCGQCSVENKD6ZCRKPMG

Now we add a public `A` record to our zone. It has the name "ccc", a value of "195.54.164.39" and it expires after one day.

$ gnunet-namestore -z myself -a -e "1 d" -p -t A -n ccc -V 195.54.164.39

Now we can query that record using the command line tool `gnunet-gns`.

$ gnunet-gns -t A -u ccc.myself
ccc.myself:
Got `A' record: 195.54.164.39

So it worked! But only resolving our own records is boring. So we can give our identity (the public key of it to be precise) to someone else so they can try to resolve our records, too. The other person (Bob) has to add it to his namestore like this:

$ gnunet-namestore -z myself -a -e never -p -t PKEY -n alice -V HWTYD3P5D77JVFNVMZ1M5T10V4SZYNMY3PCGQCSVENKD6ZCRKPMG

Our identity in Bobs namestore is a public record (-p) and never expires (-e never). Now Bob (let's assume he has called his identity myself, too) should be able to resolve our "ccc" record, too!

$ gnunet-gns -t A -u ccc.alice.myself
ccc.alice.myself:
Got `A' record: 195.54.164.39

It can continue like this. A friend of Bob would be able to resolve our records too because Bob published our identity in a public record. Bobs friend would simply use "ccc.alice.bob.myself" to resolve our "ccc" record.

Name resolution using GNS with a browser

In the previous use case "Name resolution using GNS on the command line" we got an idea about what GNS is about, but now let's use it with a browser, to make it actually useful. Currently Firefox and Chromium are known to work.

Many websites enforce HTTPS and thus provide certificates for their hostnames (and not our GNS names). Browsers don't like wrong hostnames in certificates and will present error messages. So GNUnet has to trick them by generating own certificates for our GNS names. This means we need to create our own certificate authority and tell our browser about it. Luckily there's a script for it:

$ gnunet-gns-proxy-setup-ca

After executing this script the Browser has to be restarted.

GNUnet provides a proxy service (gnunet-gns-proxy) that the browser can send DNS and HTTP traffic to. It will try to resolve names with GNS first and forward the rest of the DNS traffic to the system's DNS resolver. It will also take care of the HTTP traffic, so the browser gets valid certificates and the web server will not be confused by our GNS hostnames. Our GNS namestore doesn't know about any DNS hostnames yet, so we have to store them, too. For our "ccc" A record, we have to store a LEHO (legacy hostname) record, too. It must contain the website's original DNS hostname:

$ gnunet-namestore -z myself -a -e "1 d" -p -t LEHO -n ccc -V www.ccc.de

Now let's start gnunet-gns-proxy.

$ /usr/lib/gnunet/libexec/gnunet-gns-proxy

Our browser has to be configured so it uses our proxy. In Firefox we have to set these options under "about:config":

network.proxy.socks: localhost
network.proxy.socks_port: 7777
network.proxy.socks_remote_dns true
network.proxy.type: 1

To tell Chromium to use the proxy, it has to be started with the "--proxy-server" command line option:

$ chromium --proxy-server="socks5://127.0.0.1:7777"

Now we should be able to resolve our GNS names in the browser! We just have to type "https://ccc.myself" into the address bar. If our friend Bob prepared his system, too, he can resolve our record by typing "ccc.alice.myself".

VPN

TBD

Uninstall GNUnet and its dependencies

$ cd ~/gnunet_installation/gnunet
$ sudo make uninstall
$ cd ~/gnunet_installation/libmicrohttpd
$ sudo make uninstall
$ sudo apt remove git libtool autoconf autopoint build-essential libgcrypt-dev libidn11-dev zlib1g-dev libunistring-dev libglpk-dev miniupnpc libextractor-dev libjansson-dev libcurl4-gnutls-dev libsqlite3-dev
$ sudo apt autoremove
$ sudo userdel -r gnunet
$ sudo groupdel gnunet
$ sudo groupdel gnunetdns
$ sudo mv /etc/nsswitch.conf.original /etc/nsswitch.conf
$ sudo rm /lib/$(uname -m)-linux-gnu/libnss_gns.so.2

Appendix A: Optional GNUnet features

TBD

Troubleshooting

You can't reach other people's nodes

Should our computer not have reached the open GNUnet network automatically, we can manually instruct our node how to reach the nodes of our friends. This works by exchanging HELLO strings. This is how we get a hello string for our computer.

$ gnunet-peerinfo -gn

We can now pass this string to our friends "out of band" (using whatever existing chat or messaging technology). If the string contains some private IP networks we don't want to share, we can carefully edit them out.

Once we receive such strings from our friends, we can add them like this:

gnunet-peerinfo -p

Now our GNUnet nodes can attempt reaching each other directly. This may still fail due to NAT traversal issues.