LibreNMS

Siehe auch

  • Icinga

LibreNMS (Network Monitoring System) ist ein PHP (Laravel)/MySQL-basiertes Netzwerk-Monitoring-System mit Autodiscovery-Fähigkeiten. Es entstand durch einen Fork der Software Observium. Geräte müssen zu Beginn über SNMP- oder IPMI-Fähigkeiten verfügen, um Out-of-the-Box überwacht werden zu können. Im Bereich SNMP verzichtet LibreNMS auf den Einsatz von MIBs, und setzt auf Erkennungs-Definitionen in YAML oder PHP - MIBs nutzt es nur, um OIDs leichter lesbarer zu gestalten. Es aktualisiert sich im Standard täglich selbst aus dem Github-Repo.

Es unterstützt eine breite Masse an Netzwerk-Hardware von verschiedenen Herstellern, z. B. Cisco, FS (Fiberstore), Dell oder HP. Vorkonfigurierte Alert-Rules gibt es unter anderem für folgende spezifischen Geräte:

  • APC UPS

  • Aruba Wireless AP

  • AXIS camera

  • Cisco ASA, Cisco NX-OS

  • Comware

  • Dell iDRAC, Dell Server

  • F5 appliance

  • HP Procurve, HPE BladeSystem, HPE iLo

  • Netscaler

  • Palo Alto Networks

  • QNAP NAS

  • RITTAL

  • Synology NAS

  • UBNT EdgeSwitch

LibreNMS kann direkt an Graylog und diverse Zeitreihendatenbanken wie Graphite, InfluxDB, OpenTSDB, Prometheus und RRDToolDB angeschlossen werden. Zudem bietet es die Möglichkeit, Nagios-Plugins zu nutzen, um zusätzliche Überwachung ausserhalb von SNMP oder IPMI durchzuführen.

LibreNMS führt im Standard folgende Tasks aus:

  • Suche nach Alerts: minütlich

  • Discovery für neu hinzugefügte Devices: alle 5 Minuten (es dauert also bis zu 5 Minuten, bis alle Eigenschaften eines neuen Gerätes erkannt wurden)

  • Nagios-Checks ausführen: alle 5 Minuten

  • Abfrage von Daten für Rechnungen: alle 5 Minuten

  • Rechnungsdaten zusammenstellen: stündlich

  • Automatische Updates: täglich (funktioniert auch für Stable Releases)

Minimale Voraussetzungen:

  • 2x CPU

  • 8 GB RAM (weniger macht im Real-Life-Betrieb keinen Sinn)

  • 10 GB Disk

Installation

Einsatz auf RHEL 8, unter Apache mit PHP >= 7.3 (PHP 8+ funktioniert einwandfrei).

EPEL-Repo installieren.

Dann:

dnf -y install bash-completion cronie fping git httpd ImageMagick mtr net-snmp net-snmp-utils nmap python3 python3-PyMySQL python3-redis python3-memcached python3-pip python3-systemd rrdtool unzip

MariaDB-Server aus dem Hersteller-Repo installieren und absichern.

MariaDB konfigurieren:

cat > /etc/my.cnf.d/z00-server.cnf << EOF
[mysqld]
innodb_file_per_table = 1
innodb_flush_log_at_trx_commit = 0
lower_case_table_names = 0
EOF

systemctl restart mariadb

# create db and db-user
mysql --user root --execute="CREATE DATABASE librenms CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; GRANT ALL PRIVILEGES ON librenms.* TO 'librenms'@'localhost' IDENTIFIED BY 'password'; GRANT ALL PRIVILEGES ON librenms.* TO 'librenms'@'::1' IDENTIFIED BY 'password'; FLUSH PRIVILEGES;"

LibreNMS von Github ziehen und installieren, und dabei auf eine offizielle Version setzen:

cd /opt
git clone https://github.com/librenms/librenms.git
cd /opt/librenms
git fetch --tags
git checkout $(git describe --tags $(git rev-list --tags --max-count=1))

useradd librenms -d /opt/librenms -M -r -s "$(which bash)"

chown -R librenms:librenms /opt/librenms
chmod 771 /opt/librenms
chmod -R g=rwx /opt/librenms/rrd /opt/librenms/logs /opt/librenms/bootstrap/cache /opt/librenms/storage

# enable lnms command completion
ln -s /opt/librenms/lnms /usr/bin/lnms
cp /opt/librenms/misc/lnms-completion.bash /etc/bash_completion.d/

Remi-Repo installieren.

PHP 8 aus dem Remi-Repo installieren.

Anschliessend:

dnf -y install php-fpm php-cli php-common php-curl php-gd php-json php-mbstring php-process php-snmp php-xml php-zip php-mysqlnd

# Install PHP dependencies
sudo -u librenms -i ./scripts/composer_wrapper.php install --no-dev

# set the timezones
sed -i "s|^;date.timezone.*|date.timezone = Europe/Zurich|g" /etc/php.ini
timedatectl set-timezone Europe/Zurich

# configure php-fpm
cp /etc/php-fpm.d/www.conf /etc/php-fpm.d/librenms.conf
sed -i "s|^\[www\]|[librenms]|g" /etc/php-fpm.d/librenms.conf
sed -i "s|^user = .*|user = librenms|g" /etc/php-fpm.d/librenms.conf
sed -i "s|^group = .*|group = librenms|g" /etc/php-fpm.d/librenms.conf
sed -i "s|^listen = .*|listen = /run/php-fpm-librenms.sock|g" /etc/php-fpm.d/librenms.conf

# if there are no other PHP web applications on this server:
mv /etc/php-fpm.d/www.conf /etc/php-fpm.d/www.conf.orig

Apache konfigurieren:

cat > /etc/httpd/conf.d/librenms.conf << EOF
<VirtualHost *:80>
    DocumentRoot /opt/librenms/html/
    ServerName  librenms.example.com
    Protocols h2 h2c http/1.1

    AllowEncodedSlashes NoDecode
    <Directory "/opt/librenms/html/">
        Require all granted
        AllowOverride All
        Options FollowSymLinks MultiViews
    </Directory>

    # Enable http authorization headers
    <IfModule setenvif_module>
        SetEnvIfNoCase ^Authorization$ "(.+)" HTTP_AUTHORIZATION=$1
    </IfModule>

    <FilesMatch ".+\.php$">
        SetHandler "proxy:unix:/run/php-fpm-librenms.sock|fcgi://localhost"
    </FilesMatch>
</VirtualHost>
EOF
> /etc/httpd/conf.d/welcome.conf

systemctl enable --now httpd
systemctl enable --now php-fpm

rrdcached installieren:

setsebool -P httpd_setrlimit on
systemctl enable --now rrdcached
/opt/librenms/config.php
$config['rrdcached']    = "unix:/tmp/rrdcached.sock";

SELinux:

dnf -y install policycoreutils-python-utils

semanage fcontext -a -t httpd_sys_content_t '/opt/librenms/html(/.*)?'
# httpd_log_t is required for the logrotate to work
semanage fcontext -a -t httpd_log_t '/opt/librenms/logs(/.*)?'
semanage fcontext -a -t httpd_sys_rw_content_t '/opt/librenms/(rrd|storage)(/.*)?'
restorecon -RFvv /opt/librenms
setsebool -P httpd_can_sendmail 1
setsebool -P httpd_execmem 1
chcon -t httpd_sys_rw_content_t /opt/librenms/.env

cat > /tmp/http_fping.tt << EOF
module http_fping 1.0;

require {
type httpd_t;
class capability net_raw;
class rawip_socket { getopt create setopt write read };
}

#============= httpd_t ==============
allow httpd_t self:capability net_raw;
allow httpd_t self:rawip_socket { getopt create setopt write read };
EOF

checkmodule -M -m -o /tmp/http_fping.mod /tmp/http_fping.tt
semodule_package -o /tmp/http_fping.pp -m /tmp/http_fping.mod
semodule -i /tmp/http_fping.pp

Python:

pip3 install -r /opt/librenms/requirements.txt

SNMP konfigurieren:

\cp /opt/librenms/snmpd.conf.example /etc/snmp/snmpd.conf

# set community string to "public" for example
sed -i "s/RANDOMSTRINGGOESHERE/public/g" /etc/snmp/snmpd.conf

curl -o /usr/bin/distro https://raw.githubusercontent.com/librenms/librenms-agent/master/snmp/distro
chmod +x /usr/bin/distro
systemctl enable snmpd
systemctl restart snmpd

Cronjobs, Logrotate:

cp /opt/librenms/librenms.nonroot.cron /etc/cron.d/librenms
cp /opt/librenms/misc/librenms.logrotate /etc/logrotate.d/librenms

Firewall: Port 80/443 freischalten.

Der Webinstaller kann über http://librenms.example.com aufgerufen werden, wird am Ende aber die Datei /opt/librenms/config.php nicht schreiben können. Er zeigt dafür den generierten Inhalt an - diesen kopieren und manuell in die Datei einfügen.

Beispiel für diese Datei:

/opt/librenms/config.php
<?php

## Have a look in misc/config_definitions.json for examples of settings you can set here. DO NOT EDIT misc/config_definitions.json!

// This is the user LibreNMS will run as
//Please ensure this user is created and has the correct permissions to your install
$config['user'] = 'librenms';

### This should *only* be set if you want to *force* a particular hostname/port
### It will prevent the web interface being usable form any other hostname
#$config['base_url']        = "/";

### Enable this to use rrdcached. Be sure rrd_dir is within the rrdcached dir
### and that your web server has permission to talk to rrdcached.
$config['rrdcached']    = "unix:/tmp/rrdcached.sock";

### Default community
$config['snmp']['community'] = array('public');

### Authentication Model
$config['auth_mechanism'] = "mysql"; # default, other options: ldap, http-auth
#$config['http_auth_guest'] = "guest"; # remember to configure this user if you use http-auth

### List of RFC1918 networks to allow scanning-based discovery
#$config['nets'][] = "10.0.0.0/8";
#$config['nets'][] = "172.16.0.0/12";
#$config['nets'][] = "192.168.0.0/16";

# Uncomment the next line to disable daily updates
#$config['update'] = 0;

# Number in days of how long to keep old rrd files. 0 disables this feature
$config['rrd_purge'] = 0;

# Uncomment to submit callback stats via proxy
#$config['callback_proxy'] = "hostname:port";

# Set default port association mode for new devices (default: ifIndex)
#$config['default_port_association_mode'] = 'ifIndex';

# Enable the in-built billing extension
$config['enable_billing'] = 1;

# Enable the in-built services support (Nagios plugins)
$config['show_services'] = 1;

Danach:

echo '$config["update_channel"] = "release";' >> /opt/librenms/config.php
chown librenms:librenms /opt/librenms/config.php

Nach der Installation empfiehlt sich

  • Eine Validation: Settings (Zahnrad-Symbol oben rechts) > Validate Settings.

  • Das Anlegen der ersten Alert Rules: Alerts > Alert Rules > Click here to create the default alert rules

  • Alert Rules anpassen, z.B. Ping Latency: Severity auf Warning und Latency > 100 stellen.

  • Anlegen erster Devices.

  • Erste Alerts bestätigen und beheben: Alerts > Notifications

  • LibreNMS-Panel statt Grafana-Panel bei den Alerts

  • https://community.librenms.org/ > slamlomsk8er

Details siehe https://docs.librenms.org/Installation/Install-LibreNMS/.

AutoDiscovery

AutoDiscovery für Netze aktivieren - Netze bekanntgeben (im Beispiel nicht über die Web-Oberfläche, sondern per Config-Datei):

/opt/librenms/config.php
# https://docs.librenms.org/Extensions/Auto-Discovery/
# v1 or v2c
$config['snmp']['community'][] = "public";
#$config['snmp']['community'][] = "another_community";

$config['nets'][] = '10.80.32.0/24';

Die Netzmasken müssen zum Netz passen - ein 192.168.1.0/22 funktioniert beispielsweise nicht.

SNMP-Scan manuell anstossen:

su - librenms
./snmp-scan.py --verbose

Services

LibreNMS kann Nagios-Plugins ausführen, beispielsweise die Monitoring-Plugins der Linuxfabrik (hier müssen die Python3-Varianten eingesetzt werden). Services können lokal oder remote ausgeführt werden - letzteres erfolgt per NRPE. Mehr dazu unter https://docs.librenms.org/Extensions/Services/.

Der Check-Dateiname muss mit check_ beginnen, sie müssen per chmod +x ausführbar sein, und sie müssen den -H-Parameter unterstützen (LibreNMS setzt diesen immer, auch wenn bei der Service-Definition im Web-GUI unter „Remote Host“ nichts angegeben wird).

mkdir -p /usr/lib64/nagios/plugins/{lib,logs}
touch /usr/lib64/nagios/plugins/logs/services_wrapper.log
chown -R librenms:librenms /usr/lib64/nagios/plugins
/opt/librenms/config.php
$config['nagios_plugins']              = "/usr/lib64/nagios/plugins";
$config['service_discovery_enabled']   = true;
$config['service_discovery_frequency'] = 3600;
$config['service_discovery_workers']   = 16;
$config['service_poller_down_retry']   = 5;
$config['service_poller_enabled']      = true;
$config['service_poller_frequency']    = 300;
$config['service_poller_workers']      = 16;
$config['service_services_enabled']    = true;
$config['service_services_frequency']  = 60;
$config['service_services_workers']    = 16;
/etc/cron.d/librenms
*/5  *    * * *   librenms    /opt/librenms/services-wrapper.py 1

Probleme bei der Check-Ausführung untersucht man so:

su - librenms

# d = Debug
./check-services.php -d

Die Optionen sind:

  • d: Debug

  • h: Hostname oder Device-ID

Alert Rules

Ein Beispiel:

devices.os = "Netscaler" AND sensors.sensor_current != `sensors.sensor_prev` AND sensors.lastupdate < "DATE_SUB(NOW(),INTERVAL 5 MINUTE)"

Beispiele für Eigenschaften:

%applications.app_type = 'portactivity'
%applications_metrics.http_total_to > '100'
application_metrics.metric = ".status"
application_metrics.value >= "2"
applications.app_state != "OK"
applications.app_type = "mysql"
bgpPeers.bgpPeerAdminStatus != "stop"
bgpPeers.bgpPeerFsmEstablishedTime < "300"
bgpPeers.bgpPeerState != "established"
ciscoASA.data > "5000"
component.disabled = 0
component.ignore = 0
component.type = "ISIS"
customoids.customoid_alert = "1"
customoids.customoid_current >= `customoids.customoid_limit_warn`
devices.inserted >= `macros.past_60m`
devices.last_ping_timetaken > "10"
devices.last_polled < DATE_SUBNOW, INTERVAL 6 MINUTE
devices.os = "qnap"
devices.status_reason = "snmp"
devices.uptime < "300"
eventlog.datetime >= `macros.past_60m`
eventlog.message LIKE 'ifSpeed:%'
eventlog.message ~ "@autodiscovered@"
eventlog.type = "discovery"
ipsec_tunnels.tunnel_status != "active"
isis_adjacencies.isisISAdjState = "down"
macros.bill_cdr_over_quota >= "75"
macros.bill_quota_over_quota >= "75"
macros.device = "1"
macros.device_down = "1"
macros.device_up = "1"
macros.port_down = "1"
macros.port_up = "1"
macros.port_usage_perc >= "80"
macros.sensor_port_link = "1"
macros.state_sensor_critical = 1
macros.state_sensor_warning = 1
ports.disabled = 0
ports.ifOperStatus = "down"
ports.ifOperStatus_prev = "up"
ports.ifOutErrors_rate >= "100" || ports.ifInErrors_rate >= "100"
ports.ifSpeed < ports.ifSpeed_prev
ports.port_id = eventlog.reference
processors.processor_usage > "85"
sensors.lastupdate < "DATE_SUBNOW,INTERVAL 5 MINUTE"
sensors.sensor_alert = "1"
sensors.sensor_current != `sensors.sensor_prev`
sensors.sensor_current < `sensors.sensor_limit_low`
sensors.sensor_current ~ "[2|4|5|7|10|11]"
sensors.sensor_current ~ "[3-4]"
sensors.sensor_descr = "Primary Unit.*"
sensors.sensor_descr REGEXP ".*PEM.*"
sensors.sensor_descr ~ "Percentage load"
sensors.sensor_oid ~ ".1.3.6.1.4.1.11.2.14.11.1.2.6.1.4.[2-5]"
sensors.sensor_prev = "2"
sensors.sensor_type = "cefcFanTrayOperStatus"
services.service_status != "0"
state_indexes.state_name = "UPSUPSOverloaded"
storage.storage_deleted = 0
storage.storage_descr = "/"
storage.storage_perc >= 95
syslog.msg ~ "@arp table is full@"
syslog.priority ~ "alert"
syslog.timestamp >= `macros.past_5m`
wireless_sensors.sensor_alert = "1"
wireless_sensors.sensor_class = "clients"
wireless_sensors.sensor_current >= `wireless_sensors.sensor_limit`
wireless_sensors.sensor_type == "arubaos"

API

Die Schnittstelle wurde als Restful API realisiert, befindet sich aber noch in der Entwicklung. Das API liefert JSON oder PNG zurück. Um das API nutzen zu können, sollte unter Settings > API > API-Settings ein Token erzeugt werden.

Alle verfügbaren Methoden auslesen:

curl -H 'X-Auth-Token: YOURAPITOKENHERE' https://librenms.example.com/api/v0

# example: getting sensor values
curl -H 'X-Auth-Token: YOURAPITOKENHERE' 'http://librenms.example.com/api/v0/resources/sensor' | jq

Die möglichen Routen finden sich in der routes/api.php, die passenden API-Funktionen in includes/html/api_functions.inc.php.

Troubleshooting

tail -f /opt/librenms/logs/librenms.log /var/log/httpd/*log /var/log/php-fpm/error.log

Installation überprüfen:

sudo su - librenms
./validate.php
exit

Device-Grafiken werden nicht richtig erzeugt? Prüfen, ob die durch folgendes Kommando erzeugten Pfad-Outputs vorliegen:

sudo su - librenms
./poller.php -h DEVICE -d -f
exit

Device lässt sich nicht über das GUI oder per ./delhost DEVICE löschen?

mysql --user librenms --password
DELETE FROM devices WHERE device_id=9999999;

FAQ

Woher kommen Schwellwerte („limits“) während der Discovery?

Die Schwellwerte werden automatisch bei der Discovery eines neuen Devices gesetzt. Falls es Hersteller-spezifische Definitionen gibt (unter includes/discovery), werden diese verwendet (für Details siehe https://docs.librenms.org/Developing/os/Health-Information). Falls dies nicht der Fall ist, und sensors.guess_limits in der Config gesetzt ist, versucht LibreNMS zu erraten, welche Schwellwerte verwendet werden sollen. Dies passiert in der sensor_limit() und sensor_low_limit()-Funktion in includes/discovery/functions.inc.php. Z.B. für Temperaturen ist Stand 2022-11-24:

  • Low Limit: $limit = $current - 10;

  • High Limit: $limit = $current + 20;

Warum sind Schwellwerte („limits“) schon bei einzelnen Geräten pro Core manchmal unterschiedlich? Also zum Beispiel unter Device > Edit > Health.

Dazu muss man verstehen, woher die Schwellwerte überhaupt kommen (siehe vorherige Frage). Sowohl bei den Hersteller-Defaults als auch bei den von LibreNMS erratenen Schwellwerte kann es sein, dass diese Schwellwerte anhand von dem aktuellen Wert festgelegt werden. Da die Cores zum Zeitpunkt der Discovery verschiedene Temperaturen haben, sind auch die Schwellwerte entsprechend verschieden.

Woher weiss man, welche Werte man z.B. in einer Alert-Rule zu einem Device abfragen kann?

Laut der offiziellen Dokumentation zu Alert-Rules sind das sogenannte „Entities“. Eine „Entity“ basiert direkt auf dem MySQL-Schema, also tabelle.spalte, z.B. devices.hostname. Eine Liste der häufig verwendeten Entities findet man hier: https://docs.librenms.org/Alerting/Entities/.

Disk-Schwellwerte: Wenn man keine Schwellwerte im Graphen per rrdtool sieht, heisst das, dass man nicht alarmiert wird?

Schwellwerte sind in dem Fall meist trotzdem vorhanden, diese sieht man, wenn man auf Device > Edit geht. Alarmiert wird allerdings nur, wenn es auch eine passende Alert-Rule gibt (z.B. storage.storage_perc >= "storage_perc_warn").

Wie werden SNMP-Traps ausgewertet, bzw in Alert-Rules verwendet?
  • Es gibt es schon mitgelieferte Trap-Handlers (siehe Liste der Handlers, Mapping „Trap zu Handler“ bzw Details im Developer Guide). Normalerweise generiert so ein Handler einen Eintrag im Event Log (der Handler kann aber theoretisch alles mögliche machen).

  • Alternativ kann man unter General Settings > External > SNMP Traps Integration: „Create eventlog for snmptraps“ auf „All“ setzen, und somit für alle Traps einen Eintrag im Event Log generieren.

Sobald man einen Eintrag im Event Log hat, kann man basierend darauf Alert-Rules erstellen. Ob mit oder ohne Handler, snmptrapd muss richtig konfiguriert werden, inklusive explizierter Angabe der MIBs, an denen man interessiert ist. Siehe https://docs.librenms.org/Extensions/SNMP-Trap-Handler.

Built on 2022-12-06