Installation guide

On the munin2smartphone + static html server

Note that this guide:

  • Uses nginx as a static web server and reverse proxy,
  • Stores static HTML pages to /var/lib/munin2smartphone,
  • Serves those pages on URL https://your.fqdn.example.htmlreports,
  • Makes munin2smartphone listen on address 127.0.0.1 and port 8765,
  • Receives data on URL https://your.fqdn.example/pushdatahere.

Build the Public Key Infrastructure

We are going to authenticate our data broker with an HTTPS-client certificate.

Here is a step-by-step using Debian 10 and easy-rsa 3 as a crypto-tools wrapper. Feel free to adapt (hopefully this is accurate).

sudo apt install easy-rsa
mkdir munin2smartphone-pki
cd munin2smartphone-pki
cp /usr/share/easy-rsa/easyrsa .
./easyrsa init-pki
./easyrsa build-ca

Deploy the CA to nginx:

sudo mkdir -p /etc/nginx/client_certs/
sudo cp ./pki/ca.crt /etc/nginx/client_certs/

Build the client cert (you will be prompted for a password, which you will need later):

./easyrsa build-client-full httpmunin

You want to transfer ./pki/private/httpmunin.key, ./pki/issued/httpmunin.crt and ./pki/ca.crt to the munin host. You may safely delete ./pki/private/httpmunin.key from this place.

Configure 2 nginx locations

We will serve the static pages and forward ssl-verified requests to munin2smartphone through nginx.

We need to add 2 location directives and specify the ssl CA, something like:

server {
    index index.html index.htm index.nginx-debian.html;

    server_name your.fqdn.example;

    # [...]

    # ------ Copy-paste and adapt below ------

    # Here, the static html location
    location ~ ^/htmlreports(/.*)$ {
        alias /var/lib/munin2smartphone$1;
    }

    # Now, make client ssl verification optional
    # and define the forwarding location

    # client certificate
    ssl_client_certificate /etc/nginx/client_certs/ca.crt;
    # make verification optional, so we can display a 403 message
    # to those who fail authentication (= forbidden)
    ssl_verify_client optional;

    location /pushdatahere/ {
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header        X-Forwarded-Proto $scheme;
            proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
            if ($ssl_client_verify != SUCCESS) {
                    return 403;
            }
            proxy_pass      http://127.0.0.1:8765;
    }

    # ------ End of the new sections ------

    # [...]

}

Install munin2smartphone

You probably want to use a virtualenv with Python 3.8 (3.6 at least) and

pip install munin2smartphone

Now test-run munin2smartphone in a terminal!

Real system installation: To be documented (help welcome). I plan on:

  • using a systemd unit to start the daemon,
  • creating a debian package of munin2smartphone and deps.

Run munin2smartphone with relevant options and configuration

This is covered in another section of the documentation: options_config.

On the munin host

HTTPS POST with curl

At the end of the PKI step-by-step, we transfered 3 files to this host.

Now ensure that their names and location match the script below, that is:

  • /etc/munin/httpmunin-ca.crt (initially ca.crt)
  • /etc/munin/httpmunin.crt
  • /etc/munin/httpmunin.key
/usr/local/bin/push-munin.sh - This file should be readable and executable by the user munin.
#!/bin/bash

# We'll receive data from stdin (piped into this script).
sed "s,/,\\/,g" | \
/usr/bin/curl \
--cacert /etc/munin/httpmunin-ca.crt \
--cert /etc/munin/httpmunin.crt \
--key /etc/munin/httpmunin.key \
--pass superpass \
https://your.fqdn.example/pushdatahere/ \
-d @-

Call to munin-limits every 5 minutes

Let’s code a very simple daemon.

munin-limits will evaluate the state of every known check and take the configured action.

/usr/local/bin/call-munin-limits.py - This file should be readable and executable by the user munin.
#!/usr/bin/python3

import time
import subprocess

INTERVAL = 300 # seconds = 5 minutes
COMMAND = [
    "/usr/share/munin/munin-limits",
    "--contact",
    "widget",
    "--force",
    "--always-send",
    "warning,critical",
]


def main():
    while True:
        print("Calling {}".format(' '.join(COMMAND)))
        process = subprocess.Popen(COMMAND)
        process.wait()
        print("Exit code: {}".format(process.returncode))
        time.sleep(INTERVAL)

if __name__ == "__main__":
    main()

Configure the service

/etc/systemd/system/push-munin.service
[Unit]
Description=Triggers a push of all munin states in json

[Service]
ExecStart=/usr/local/bin/call-munin-limits.py
User=munin

[Install]
WantedBy=multi-user.target

Then run

systemctl daemon-reload
systemctl enable push-munin
systemctl start push-munin
  1. Add a contact in munin, and specify the concatenated JSON format
contact.widget.command /usr/local/bin/push-munin.sh
contact.widget.text \
        { \
                "group":"${var:group}", \
                "host":"${var:host}", \
                "graph_category":"${var:graph_category}", \
                "graph_title":"${var:graph_title}", \
                "warning":[  ${loop<,>:wfields { \
                        "label":"${var:label}", \
                        "value":"${var:value}", \
                        "w":"${var:wrange}", \
                        "c":"${var:crange}", \
                        "extra":"${var:extinfo}" \
                } } ], \
                "critical":[ ${loop<,>:cfields { \
                        "label":"${var:label}", \
                        "value":"${var:value}", \
                        "w":"${var:wrange}", \
                        "c":"${var:crange}", \
                        "extra":"${var:extinfo}" \
                } } ], \
                "unknown":[  ${loop<,>:ufields { \
                        "label":"${var:label}", \
                        "value":"${var:value}", \
                        "w":"${var:wrange}", \
                        "c":"${var:crange}", \
                        "extra":"${var:extinfo}" \
                } } ] \
        }

On the smartphone

On android, you can use this widget.

Configure a view to your static web pages with an automatic reload.

Notes

on the refresh interval

I chose to use a 5 minutes interval everywhere. 5 minutes is the default polling interval for munin.

Should you wish to change this, you need to change the interval in:

on the munin contact name

We are going to configure a contact named “widget” in munin. This contact will trigger a shell script pushing data over HTTPS with curl. Every 5 minutes, we will shoot an event so that munin processes the data and sends it to our munin2smartphone daemon.

Should you want to use another name than widget for the contact, change it