Freifunkmapserver aufsetzen (ffmap-d3)

Da wir dank eines gesponsorten Servers bei Freifunk Gütersloh mehr Spielraum haben, wollte ich als Fingerübung die Karte erneuern, parallel auf einer neuen VM. Allein, die Doku ist mau, daher hier quasi eine Mitschrift auch für Ralf und Cord, meine Admin-Kollegen …

Basisinstallation

Die neue VM wurde mittels des uvt-kvm-Wrapper-Skriptes erzeugt, IP vorab in IPAM rausgesucht und eingetragen, DNS-Einträge (map.4830.org) auf librarian.uu.org vorgenommen:

root@conquest:~# ./New-FFGT-VM.sh map 192.251.226.106 255.255.255.128 192.251.226.113

Die VM hat dann ja schon unsere SSH-Keys und unseren User, aber nun kommen die externen Repositories …

root@map:~# more /etc/apt/sources.list.d/*
::::::::::::::
/etc/apt/sources.list.d/alfred.list
::::::::::::::
deb http://download.opensuse.org/repositories/home:/fusselkater:/ffms/Debian_7.0/ /
::::::::::::::
/etc/apt/sources.list.d/fastd.list
::::::::::::::
deb http://repo.universe-factory.net/debian/ sid main

Dank an dieser Stelle an die Kollegen aus Münster für ihr Wiki-Dokument sowie an die Kollegen aus Hamburg, aus deren Anleitung ich die fastd-bat0-Installation übernommen habe. Eigentlich hätte ich auch ein Gateway per puppet installieren können und nur Apache und Alfred hinzufügen, aber, naja, zu Fuß geht’s auch.

Vor der Nutzung der Repositories: Schlüssel importieren:

gpg --keyserver pgpkeys.mit.edu --recv-key 16EF3F64CB201D9C && gpg -a --export 16EF3F64CB201D9C | apt-key add -
wget http://download.opensuse.org/repositories/home:fusselkater:ffms/Debian_7.0/Release.key && apt-key add - < Release.key
 apt-get update

Dann ersteinmal bißchen Software nachinstallieren:

aptitude install batctl batman-adv-dkms fastd bridge-utils alfred alfred-json

Das sollte dann auch libjansson4:amd64 anziehen, was für alfred-json benötigt wird.

batman_adv brauchen wir in Version 14, im Kernel ist Version 15 aktuell — also per dkms downgraden:

dkms remove batman-adv/2013.4.0 --all
dkms --force install batman-adv/2013.4.0
echo >>/etc/modules "batman-adv"
modprove -v batman-adv

Wichtig ist, daß wir 2013.4.0 laufen haben — bzw. »compatibility version 14«:

root@map:~# cat /sys/module/batman_adv/version
2013.4.0
root@map:~# dmesg | grep batman
[21218.137874] batman_adv: module verification failed: signature and/or  required key missing - tainting kernel
[21218.152097] batman_adv: B.A.T.M.A.N. advanced 2013.4.0 (compatibility version 14) loaded

Fastd-Konfiguration holen wir uns von einem anderen GW oder stats; bei den Hamburgern ist der händische Ablauf beschrieben:

root@map:~# mkdir /etc/fastd/
root@stats:~# rsync -av --progress /etc/fastd/ffgt-mesh-vpn map.4830.org:/etc/fastd/
root@map:~# vi /etc/fastd/ffgt-mesh-vpn/secret.conf # ffgt-ip.txt hat die Infos
root@map:~# vi /etc/fastd/ffgt-mesh-vpn/fastd.conf # MAC ändern, letzte 2 Bytes von eth1 => de:ca:fb:ad:XX:YY

Und dann war da noch die Netz­werk­in­ter­face­kon­fi­gu­ra­ti­on:

root@map:~# more /etc/network/interfaces.d/b*
::::::::::::::
/etc/network/interfaces.d/bat0.cfg
::::::::::::::
allow-hotplug bat0
iface bat0 inet6 manual
    pre-up modprobe batman-adv
    pre-up batctl if add ffgt-mesh-vpn
    up ip link set $IFACE up
    post-up brctl addif br-ffgt $IFACE
    post-up batctl it 10000
    post-up /sbin/ip rule add from all fwmark 0x1 table 42
    pre-down brctl delif br-ffgt $IFACE || true
    down ip link set $IFACE down
    #post-up start-stop-daemon -b --start --exec /usr/local/sbin/alfred -- -m -i br-ffgt -b bat0;
    #post-up start-stop-daemon -b --start --exec /usr/local/sbin/batadv-vis -- -si bat0;
::::::::::::::
/etc/network/interfaces.d/br-ffgt.cfg
::::::::::::::
auto br-ffgt
iface br-ffgt inet6 static
    pre-up brctl addbr br-ffgt
    bridge-ports none
    address fd42:ffee:ff12:0aff:0301:0000:0000:af55
    netmask 64
    post-up /sbin/ip -6 addr add 2001:bf7:1310::af55/64 dev br-ffgt
    post-uo /sbin/ip -6 route add default via 2001:bf7:1310::1
    pre-down sbin/ip -6 addr del 2001:bf7:1310::af55/64 dev br-ffgt

iface br-ffgt inet static
    address 10.255.144.8
    netmask 255.255.255.255

»af« und »55« sind das vorletzte und letzte Byte der eth1-MAC; 10.255.144.8 ist die nächste freie Anycast-IP gem. IPAM.

fastd hat nun alle Backbone-Einträge, die auch stats hatt(e), …

root@map:~# ls -lR /etc/fastd/ffgt-mesh-vpn/
/etc/fastd/ffgt-mesh-vpn/:
total 16
drwxr-xr-x 2 root root 4096 Mar 20 19:02 backbone
-rw-r--r-- 1 root root  409 Mar 20 18:45 fastd.conf
drwxr-xr-x 2 root root 4096 Nov 19 03:28 peers
-rw-r----- 1 root root   75 Mar 20 18:44 secret.conf

/etc/fastd/ffgt-mesh-vpn/backbone:
total 16
-rw-r--r-- 1 root root  99 Jul  6  2014 gw01
-rw-r--r-- 1 root root 102 Mar 20 18:59 gw03
-rw-r--r-- 1 root root 101 Mar  6 15:18 gw04
-rw-r--r-- 1 root root 102 Mar 20 19:02 gw07

/etc/fastd/ffgt-mesh-vpn/peers:
total 0

… lokal sollte man noch stats' Daten hinzufügen, aber es wird ersteinmal so funktionieren — Zeit, den Kram zu starten:

root@map:~# ifup br-ffgt
root@map:~# service fastd start
root@map:~# batctl gw
off
root@map:~# batctl gwl
      Gateway      (#/255)           Nexthop [outgoingIF]: gw_class ... [B.A.T.M.A.N. adv 2013.4.0, MainIF/MAC: ffgt-mesh-vpn/de:ca:fb:ad:af:55 (bat0)]
   de:ad:be:ef:07:01 (255) de:ad:be:ef:07:01 [ffgt-mesh-vpn]: 215 - 96MBit/96MBit
   de:ad:be:ef:04:01 (251) de:ad:be:ef:04:01 [ffgt-mesh-vpn]: 207 - 48MBit/48MBit
   de:ad:be:ef:00:00 (255) de:ad:be:ef:00:00 [ffgt-mesh-vpn]: 215 - 96MBit/96MBit

Soweit, so gut, vermesht ist die neue Kiste also.

Kommen wir zur Installation der Map-Software — und da haben die Kollegen aus Göttingen dan­kens­wer­ter­weise einiges dokumentiert. Und hier laufen wir das erste Mal in ein Problem; die Göttinger schreiben, zu recht:

Voraussetzungen

Die Installation von ffmap-d3 und dem backend hat von allen Aufgaben am meisten Zeit und Nerven gekostet, u.a. weil so viele Versionen existieren, keine Anleitung auffindbar war und der Autor eigentlich keine Ahnung von modernen Webanwendungen hat.

Die Karte zieht ihre Informationen aus der A.L.F.R.E.D Datenbank. Mindestens das Backend muss also auf einem Rechner laufen, der Verbindung zu Alfred Datenbank hat, bzw. der Verbindung zum Mesh-Netzwerk hat. Wenn er das hat muss das hier funktionieren:

root@linux:~# alfred-json -z -r 158

Screenshot

Die modernisierte ffmap-d3.

Nun hat ›alfred‹ leider das Problem, daß das mit ›Multi-Master‹ eher nicht wirklich funktioniert. Da das Datensammelskript sowieso sudo-Wrapper um alfred-json und andere Kommandos erwartet, verzichten wir erst einmal auf lokal laufenden Alfred und fragen den auf stats laufenden Master per ssh ab ...
root@map:~# ssh stats.guetersloh.freifunk.net alfred-json -z -r 158 |wc -l
8629
root@map:~# ssh stats.guetersloh.freifunk.net alfred-json -z -r 159 |wc -l
8086

Ja, jetzt wird's insecure …

ffgt@map:~$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/ffgt/.ssh/id_rsa): 
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in /home/ffgt/.ssh/id_rsa.
Your public key has been saved in /home/ffgt/.ssh/id_rsa.pub.
The key fingerprint is:
[…] ffgt@map
The key's randomart image is:
+--[ RSA 2048]----+
[…]
+-----------------+
ffgt@map:~$ ls -l .ssh/
total 12
-rw------- 1 ffgt ffgt 1226 Mar 20 12:38 authorized_keys
-rw------- 1 ffgt ffgt 1679 Mar 21 00:19 id_rsa
-rw-r--r-- 1 ffgt ffgt  390 Mar 21 00:19 id_rsa.pub
ffgt@map:~$ logout
root@map:~# ssh stats.guetersloh.freifunk.net cat \>\>.ssh/authorized_keys < ~ffgt/.ssh/id_rsa.pub
 root@map:~# su - ffgt
 ffgt@map:~$ ssh root@stats.guetersloh.freifunk.net alfred-json -z -r 159 |wc -l
 The authenticity of host 'stats.guetersloh.freifunk.net (2001:bf7:1310::5747)' can't be established.
 ECDSA key fingerprint is […].
 Are you sure you want to continue connecting (yes/no)? yes
 8086
 ffgt@map:~$ ssh root@stats.guetersloh.freifunk.net alfred-json -z -r 158 |wc -l
 8629

Kann man sicherlich auch noch anders lösen, aber für den Moment soll's mal so tun.

ffgt@map:~$ git clone https://github.com/ffnord/ffmap-d3.git
Cloning into 'ffmap-d3'...
remote: Counting objects: 1945, done.
remote: Compressing objects: 100% (9/9), done.
remote: Total 1945 (delta 4), reused 0 (delta 0), pack-reused 1936
Receiving objects: 100% (1945/1945), 884.79 KiB | 1.48 MiB/s, done.
Resolving deltas: 100% (1147/1147), done.
Checking connectivity... done.
ffgt@map:~/ffmap-d3$ diff -u config.js config.js.org 
--- config.js   2015-03-21 01:03:12.548671799 +0000
+++ config.js.org       2015-03-21 01:00:59.156671799 +0000
@@ -26,7 +26,7 @@
     url:       "/",
 
     // visible link in the navigation:
-    sitename:  "guetersloh.freifunk.net",
+    sitename:  "gothamcity.freifunk.net",
 
     // initial gravity, friction, of the graph at pageload:
     gravity:   0.05,
ffgt@map:~/ffmap-d3$ diff -u config.json config.json.org 
--- config.json 2015-03-21 01:03:35.432671799 +0000
+++ config.json.org     2015-03-21 01:00:59.156671799 +0000
@@ -1,5 +1,5 @@
 {
-  "cityname": "Gütersloh",
-  "sitename": "guetersloh.freifunk.net",
+  "cityname": "Gotham City",
+  "sitename": "gothamcity.freifunk.net",
   "url": "/"
 }

Anders als die Kollegen in Göttingen installieren wir nur gem. README.md:

root@map:~# apt-get install nodejs npm
root@map:~# ln -s /usr/bin/nodejs /usr/bin/node # No fscking comment
root@map:~# npm install -g grunt-cli bower
root@map:~# su - ffgt
ffgt@map:~$ cd ffmap-d3/
ffgt@map:~/ffmap-d3$ npm install 
ffgt@map:~/ffmap-d3$ grunt
[...]
Done, without errors.
ffgt@map:~/ffmap-d3$ ls -lt
total 104
drwxrwxr-x  6 ffgt ffgt  4096 Mar 21 01:10 build
drwxrwxr-x  9 ffgt ffgt  4096 Mar 21 01:10 bower_components
drwxrwxr-x 11 ffgt ffgt  4096 Mar 21 01:08 node_modules
-rw-rw-r--  1 ffgt ffgt    90 Mar 21 01:03 config.json
-rw-rw-r--  1 ffgt ffgt  3501 Mar 21 01:03 config.js
drwxrwxr-x  2 ffgt ffgt  4096 Mar 21 01:00 templates
-rw-rw-r--  1 ffgt ffgt    87 Mar 21 01:00 config.json.org
-rw-rw-r--  1 ffgt ffgt  3501 Mar 21 01:00 config.js.org
drwxrwxr-x  2 ffgt ffgt  4096 Mar 21 01:00 css
drwxrwxr-x  2 ffgt ffgt  4096 Mar 21 01:00 img
drwxrwxr-x  2 ffgt ffgt  4096 Mar 21 01:00 lib
drwxrwxr-x  2 ffgt ffgt  4096 Mar 21 01:00 nodes
-rw-rw-r--  1 ffgt ffgt   616 Mar 21 01:00 package.json
drwxrwxr-x  2 ffgt ffgt  4096 Mar 21 01:00 tasks
-rw-rw-r--  1 ffgt ffgt   387 Mar 21 01:00 bower.json
-rw-rw-r--  1 ffgt ffgt   396 Mar 21 01:00 Gruntfile.js
-rw-rw-r--  1 ffgt ffgt 35147 Mar 21 01:00 LICENSE
-rw-rw-r--  1 ffgt ffgt  1147 Mar 21 01:00 README.md

Unter build wäre auslieferungsfertiger Code. Jetzt braucht's aber noch Quelldaten, die erstellt ein Backend … oder, in den Worten der Göttinger Kollegen:

Das Backend hat die Aufgabe die Daten für die Karte zu besorgen.

Dazu benutzt es die Ausgabe folgender Kommandos:

alfred-json -z -r 158
alfred-json -z -r 159
batadv-vis -u /var/run/alfred.sock -i bat0 -f json
batctl -m bat0 gwl -n
batctl -m bat0 gw

Und eine "Konfigurationsdatei":

/opt/ffmap-backend/aliases.json

[…]
Das Backend gibt es in zig unterschiedlichen Versionen. Die Ursprungsversion ist zur Zeit hier: https://github.com/ffnord/ffmap-backend

Wir haben uns nach Studium des Entwicklungsverlaufs dazu entschieden, die Version von Freifunk Mainz, Wiesbaden & Umgebung zu nehmen.

Aufgrund, sagen wir, anderer Informationen, tendiere ich eher zum Orginal.

root@map:~# su - ffgt
ffgt@map:~$ git clone https://github.com/ffnord/ffmap-backend.git
Cloning into 'ffmap-backend'...
remote: Counting objects: 1292, done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 1292 (delta 0), reused 0 (delta 0), pack-reused 1289
Receiving objects: 100% (1292/1292), 449.56 KiB | 0 bytes/s, done.
Resolving deltas: 100% (770/770), done.
Checking connectivity... done.
ffgt@map:~$ cd ffmap-backend/
ffgt@map:~/ffmap-backend$ ls
alfred.py            backend.py  GlobalRRD.py  LICENSE  NodeRRD.py  README.md  RRD.py
aliases.json_sample  batman.py   graph.py      nodedb   nodes.py    rrddb.py

Flugs die Scripte erstellt, die auf dem anderen Rechner die Programme ausführen — lt. README.md macht man da sonst sudo-Aufrufe davor:

ffgt@map:~/ffmap-backend$ echo -e '#!/bin/sh\nssh root@stats.guetersloh.freifunk.net /usr/local/sbin/alfred $*' >$HOME/alfred ; chmod +x $HOME/alfred
ffgt@map:~/ffmap-backend$ echo -e '#!/bin/sh\nssh root@stats.guetersloh.freifunk.net /usr/local/bin/alfred-json $*' >$HOME/alfred-json ; chmod +x $HOME/alfred-json
ffgt@map:~/ffmap-backend$ echo -e '#!/bin/sh\nssh root@stats.guetersloh.freifunk.net /usr/sbin/batctl $*' >$HOME/batctl ; chmod +x $HOME//batctl 
ffgt@map:~/ffmap-backend$ echo -e '#!/bin/sh\nssh root@stats.guetersloh.freifunk.net /usr/sbin/batadv-vis $*' >$HOME/batadv-vis ; chmod +x $HOME/batadv-vis
ffgt@map:~/ffmap-backend$ $HOME/alfred-json -z -r 158 | wc -l
8880

Sieht ja gut aus; noch schnell die aktuelle aliases.json geholt und mal einen Test gewagt:

ffgt@map:~/ffmap-backend$ ssh root@stats.guetersloh.freifunk.net cat /home/ffgt/ffgt/ffmap-backend/aliases.json >aliases.json
ffgt@map:~/ffmap-backend$ ./backend.py 
Traceback (most recent call last):
  File "./backend.py", line 7, in 
    import networkx as nx
ImportError: No module named 'networkx'
ffgt@map:~/ffmap-backend$ pip install networkx
The program 'pip' is currently not installed. You can install it by typing:
sudo apt-get install python-pip
ffgt@map:~/ffmap-backend$ sudo apt-get install python-pip
Reading package lists... Done
Building dependency tree       
Reading state information... Done
The following extra packages will be installed:
  python-colorama python-distlib python-html5lib python-setuptools
Suggested packages:
  python-lxml python-genshi
Recommended packages:
  python-dev-all
The following NEW packages will be installed:
  python-colorama python-distlib python-html5lib python-pip python-setuptools
0 upgraded, 5 newly installed, 0 to remove and 0 not upgraded.
Need to get 542 kB of archives.
After this operation, 2,513 kB of additional disk space will be used.
Do you want to continue? [Y/n] y
[…]
The following NEW packages will be installed:
  python-colorama python-distlib python-html5lib python-pip python-setuptools
0 upgraded, 5 newly installed, 0 to remove and 0 not upgraded.
[…]
ffgt@map:~/ffmap-backend$ sudo pip install networkx
Downloading/unpacking networkx
  Downloading networkx-1.9.1-py2.py3-none-any.whl (1.2MB): 1.2MB downloaded
Downloading/unpacking decorator>=3.4.0 (from networkx)
  Downloading decorator-3.4.0.tar.gz
  Running setup.py (path:/tmp/pip_build_root/decorator/setup.py) egg_info for package decorator
    
    warning: no previously-included files found matching 'Makefile'
Installing collected packages: networkx, decorator
  Running setup.py install for decorator
    
    warning: no previously-included files found matching 'Makefile'
Successfully installed networkx decorator
Cleaning up...
ffgt@map:~/ffmap-backend$ ./backend.py 
Traceback (most recent call last):
  File "./backend.py", line 7, in 
    import networkx as nx
ImportError: No module named 'networkx'
ffgt@map:~/ffmap-backend$ pip install networkx
Requirement already satisfied (use --upgrade to upgrade): networkx in /usr/local/lib/python2.7/dist-packages
Cleaning up...
ffgt@map:~/ffmap-backend$ head backend.py 
#!/usr/bin/env python3
[…]

Und wieder einmal: die Schlange und ich werden keine Freunde mehr ...

ffgt@map:~/ffmap-backend$ sudo apt-get install python3-pip
ffgt@map:~/ffmap-backend$ sudo pip3 install networkx
ffgt@map:~/ffmap-backend$ ./backend.py 
usage: backend.py [-h] [-a FILE] [-m MESH] -d DESTINATION_DIRECTORY
                  [--vpn MAC] [--prune DAYS]
backend.py: error: the following arguments are required: -d/--destination-directory
ffgt@map:~/ffmap-backend$ PATH=$HOME:$PATH ./backend.py -a aliases.json -d /home/ffgt/ffmap-d3/build/
Warning: Permanently added the ECDSA host key for IP address '193.26.120.16' to the list of known hosts.
Traceback (most recent call last):
  File "/home/ffgt/ffmap-backend/RRD.py", line 68, in ensureSanity
    self.checkSanity(ds_list)
  File "/home/ffgt/ffmap-backend/RRD.py", line 80, in checkSanity
    raise FileNotFoundError(self.filename)
RRD.FileNotFoundError: /home/ffgt/ffmap-backend/nodedb/nodes.rrd

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "./backend.py", line 99, in 
    rrd = rrd(scriptdir +  '/nodedb/', options['destination_directory'] + '/nodes')
  File "/home/ffgt/ffmap-backend/rrddb.py", line 16, in __init__
    self.globalDb = GlobalRRD(self.dbPath)
  File "/home/ffgt/ffmap-backend/GlobalRRD.py", line 20, in __init__
    self.ensureSanity(self.ds_list, self.rra_list, step=60)
  File "/home/ffgt/ffmap-backend/RRD.py", line 70, in ensureSanity
    self.create(ds_list, rra_list, **kwargs)
  File "/home/ffgt/ffmap-backend/RRD.py", line 195, in create
    **kwargs
  File "/home/ffgt/ffmap-backend/RRD.py", line 55, in _exec_rrdtool
    subprocess.check_output(pargs)
  File "/usr/lib/python3.4/subprocess.py", line 603, in check_output
    with Popen(*popenargs, stdout=PIPE, **kwargs) as process:
  File "/usr/lib/python3.4/subprocess.py", line 848, in __init__
    restore_signals, start_new_session)
  File "/usr/lib/python3.4/subprocess.py", line 1446, in _execute_child
    raise child_exception_type(errno_num, err_msg)
FileNotFoundError: [Errno 2] No such file or directory: 'rrdtool'
ffgt@map:~/ffmap-backend$ sudo apt-get install rrdtool
ffgt@map:~/ffmap-backend$ PATH=$HOME:$PATH ./backend.py -a aliases.json -d /home/ffgt/ffmap-d3/build/
ffgt@map:~/ffmap-backend$

Nur leider ... bleibt die Karte leer. Zufällig las ich dann den IRC-Backlog auf #gluon:

01:30 < tcatm> Ich hab mal das neue ffmap-backend in den master gemerged und den alten master in legacy umbenannt.
01:31 < tcatm> Beachtet, dass der Code in ffmap-d3 mit dem master jetzt nicht mehr zusammen funktioniert.

Tja, und wann habe ich ausgecheckt?

ffgt@map:~/ffmap-backend$ ls -lad .git
drwxrwxr-x 8 ffgt ffgt 4096 Mar 21 01:34 .git

Bingo! ;)

Des Rätsels Lösung: »legacy« auschecken, das tut halt mit der aktuellen ffmap-d3. Voíla. Hat auch wirklich kaum mehr als 5 Minuten gebraucht ;)