Tracking your bank accounts with a Raspberry Pi: Kresus

Posted on July 24, 2017 in DIY • 5 min read

I always had troubles tracking expenses on my bank accounts. You never have the correct password or login when you want to check your bank account status, you have to log into every bank website when you have multiple bank accounts, bank interfaces are super painful and so on. I recently discovered Kresus, a nice app to selfhost on one of your servers and centralize in a single interface all your bank accounts, while offering advanced features to ease tracking expenses. For instance you can enter check expenses when you are making the check, and this “virtual” transaction can be merged with the real transaction when the check is taken to the bank, thus ending “Check number XXX” unknown transactions on your operations list.

As I consider banking information very sensitive, I decided to host the app on a dedicated Raspberry Pi on my local network, quite isolated and controlled. I am assuming you start with a lite version of Raspbian. You can have a look at my Raspberry pi post-install checklist.

Note: This post is about a quite cutting-edge setup, using nightly versions of Kresus (instead of stable tagged versions) and Python 3 instead of Python 2. Python 3 support is still very experimental as of writing this blogpost, but the instructions should work by cloning a git tag of Kresus (instead of using the master branch) and replacing python3 by python2.

Install NodeJS

First, you should install NodeJS. NodeJS is sadly only available in a very old version in Raspbian repositories, and NodeJS official repositories and install script does not yet support the ARM platform, so you will have to install it by hand.

The easiest way to get it is to wget the LTS version available on the official website on your Raspberry Pi (take care in downloading the correct ARM build) and inflating it to /usr. As of writing this blog post, this can be done with

pi@raspberry ~ $ wget https://nodejs.org/dist/v6.11.1/node-v6.11.1-linux-armv6l.tar.xz
pi@raspberry ~ $ tar xvf node-v6.11.1-linux-armv6l.tar.xz
pi@raspberry ~ $ sudo cp -R node-v6.11.1-linux-armv6l/* /usr/
pi@raspberry ~ $ rm -r node-v6.11.1-linux-armv6l*

If everything went smoothly, node and npm commands should be available.

Install Weboob

Weboob is a dependency of Kresus, it is the free software powering the backend and actually fetching data from your bank website. You should install it before installing Kresus.

First, install Weboob dependencies:

pi@raspberry ~ $ sudo apt-get update && sudo apt-get install git python3 python3-dev python3-pip libffi-dev libxml2-dev libxslt-dev libyaml-dev libtiff-dev libjpeg-dev zlib1g-dev libfreetype6-dev libwebp-dev build-essential gcc g++ wget python3-imaging python3-lxml

Then, clone the Weboob git repository and install it:

pi@raspberry ~ $ git clone https://git.weboob.org/weboob/devel/ weboob
pi@raspberry ~ $ cd weboob && sudo pip3 install .

You should cd ~pi/weboob && git pull origin master periodically to ensure that Weboob modules (the code actually scraping data from your bank website) are up to date. This is best done in a daily crontask.

Add a dedicated Kresus user

Let us add a new user, without password and with /bin/false as shell:

pi@raspberry ~/weboob $ sudo adduser kresus --disabled-password
pi@raspberry ~/weboob $ sudo chsh kresus
[] Type "/bin/false"

This will be the user with restricted privileges that will be used to clone and run Kresus. To su as this user, use

pi@raspberry ~ $ sudo su kresus -s /bin/bash

Note: When doing a su like this, you cannot use screen afterwards, you should use script /dev/null before, see this SO thread.

Install Kresus

We can now clone Kresus Git repository

kresus@raspberry ~ $ cd && git clone https://framagit.org/kresusapp/kresus/

and install required NPM packages

kresus@raspberry ~ $ cd kresus && npm install

This will take quite a long time, especially the pouchdb build as there are not any prebuilt binary for Raspberry Pi. I had a problem with leveldown dependency on my Raspberry Pi, it was not installing its dependency prebuild and build was failing with a prebuild command not found error. If you have any such issue, just npm install prebuild and re-run npm install.

Note: Make sure that as less stuff as possible is running on your Raspberry Pi when running npm install, or the process might get killed due to an out of memory error. In case you have a problem with a given module and npm install does not want to reinstall it, just delete the associated folder under kresus/node_modules and rerun npm install.

Finally, build the Kresus production files:

kresus@raspberry ~/kresus $ NODE_ENV=production make build

Check that Kresus is working

kresus@raspberry /home/kresus/kresus $ NODE_ENV=production KRESUS_PYTHON_EXEC=python3 KRESUS_WEBOOB_DIR=/home/pi/weboob node bin/kresus.js

It should be starting and giving you access to the web interface on port 9876, giving Kresus server is listening on port 9876... and info - init | Server is ready, let's start the show! in the logs. Attention, by default Kresus will not listen on anything else than localhost so you will have to proxy (typically with ssh SOCKS) on your Raspberry Pi to try it in your browser.

Launch on start

Let us use Systemd to start automatically Kresus on boot

$ cat /etc/systemd/system/kresus.service
[Unit]
Description=Personal finance manager
After=network.target

[Service]
WorkingDirectory=/home/kresus/kresus
Environment=NODE_ENV=production
Environment=KRESUS_PYTHON_EXEC=python3
Environment=KRESUS_WEBOOB_DIR=/home/pi/weboob
ExecStart=/usr/bin/node bin/kresus.js

Type=simple
Restart=always

User=kresus

StandardOutput=journal
StandardError=inherit
SyslogIdentifier=kresus

[Install]
WantedBy=multi-user.target
$ sudo systemctl daemon-reload
$ sudo systemctl start kresus.service

And if everything works fine,

sudo systemctl enable kresus.service

to automatically start your Kresus at every boot.

Nginx

Now, we would like our Kresus to be accessible from the rest of our local network, with an authentication layer on top. We will use Nginx for this, here is a sample configuration file:

server {
        listen 80 default_server;
        listen [::]:80 default_server;

        root /home/kresus/kresus/build/client;

        # Add index.php to the list if you are using PHP
        index index.html index.htm index.nginx-debian.html;

        server_name _;

        location / {
            # Only proxy the API calls
            location /api {
                    proxy_pass             http://127.0.0.1:9876;
                    proxy_set_header       Host $host;
                    proxy_set_header Proxy "";

                    # Longer timeouts on my (slow) Raspberry Pi
                    proxy_connect_timeout       300;
                    proxy_send_timeout          300;
                    proxy_read_timeout          300;
                    send_timeout                300;
            }

            deny all;
            allow 192.168.0.0/24;
        }

        # HTTP Authentication
        auth_basic "Restricted";
        auth_basic_user_file /home/kresus/kresus/.htpasswd;

        # Security headers
        add_header x-xss-protection "1; mode=block";
        add_header x-frame-options "DENY";
        add_header X-Content-Type-Options "nosniff";
        add_header Content-Security-Policy "default-src 'self'; child-src 'none'; object-src 'none'; img-src 'self' data:;";
        add_header Referrer-Policy "no-referrer";
        # If using HTTPS, enable the following line
        # add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
}

It is really important you add HTTP Authentication on top of it and configure your firewall to limit access to certain IPs (mine is only accessible from local network). Indeed, out of the box, Kresus does not provide any form of authentication (to avoid risking to make it wrong). Then, the HTTP Authentication layer from your webserver is the only authentication layer across your install.

As my Raspberry Pi is not web-accessible this alternative DNS method for Let’s Encrypt was useful to get a certificate anyways.

Hope this helps tracking your expenses ! :)

Bonus part: putting Kresus’ db in RAM

Kresus is quite slow on a Raspberry Pi v1, even using a good class 10 SD card and a bit of overclocking. An easy trick to improve this is to put the database in RAM, mounted through tmpfs.

$ # First, create a mount point
$ mkdir /home/kresus/kresus_vram && chown kresus:kresus /home/kresus/kresus_vram
$ # Then, mount the tmpfs endpoint
$ cat /etc/fstab
[]
tmpfs /home/kresus/kresus_vram tmpfs defaults,size=32M,uid=kresus,gid=kresus 0 0
$ # Set up a crontask to backup to the SD card often (tmpfs is erased after a reboot)
$ sudo crontab -u kresus -l
*/5 * * * *     /usr/bin/rsync -ar /home/kresus/kresus_vram/ /home/kresus/.kresus/

Then, just edit the systemd unit to sync in the other way at startup:

Environment=KRESUS_DIR=/home/kresus/kresus_vram
ExecStartPre=/usr/bin/rsync -ar /home/kresus/.kresus/ /home/kresus/kresus_vram/

And you are all set :)