Dank einer Reihe von Sensoren und Kameras erhalte ich Kurznachrichten aufs Mobiltelefon, falls jemand an meiner Wohnungstür herumfummelt oder gar hindurchgeht. Da ist es nur konsequent, dass ebenfalls das Alarmhorn des Wachpersonals tutet, falls entweder ich oder ein Bösewicht durch die ssh-Tür meines Linuxrechners wollen.
Neben der schon einmal in [2] vorgestellten Lösung "Prowl" zum Verschicken von Textnachrichten findet sich in der kunterbunten Welt der Telefon-Apps ein weiterer Anbieter namens "Pushover", der gegen eine Einmalzahlung von $4.99 lebenslang über eine Web-API bis zu 7,500 Nachrichten im Monat an beliebig viele Smartphone- und sogar Desktop-Clients verteilt.
Auf iOS oder Android ist es die Pushover-App, in die sich der User einloggt und
die dann eingehende Nachrichten als Push Notifications anzeigt, auch wenn das
Telefon mit der Lockscreen abgesperrt ist.
Als weiteres Schmankerl bietet Pushover native Desktop-Clients
für den Mac und über eine etwas hanebüchene Browserlösung sogar für den
Linux-Desktop. Dort ruft der User entweder in Chrome oder Firefox die Webseite
client.pushover.net
auf, loggt sich mit seinem Pushover-Account ein,
erlaubt dem Browser anschließend Desktop-Notifications auf dem hauseigenen
Desktop, und fertig ist der Desktop-Client. Dies funktioniert allerdings nur
so lange wie der Browser offen ist und ein Tab auf die Webseite von Pushover
zeigt, was aber bei mir daheim dank Pinned Tabs bereits für GMail und Evernote
der Fall ist, also spielt ein Tab mehr auch keine Rolle mehr.
Den neuesten Einträgen in einer Logdatei wie auth.log
in /var/logs
zu folgen ist gar keine so einfache
Sache. Selbst eine Unix-Funktion wie tail -f
zu implementieren, die jeder
Admin wohl mehrmals täglich nutzt, erfordert Kenntnis der Systemfunktion
seek()
, mit der ein Filehandle bis ans Ende einer Datei fahren kann.
Liefert dann ein read()
keine mehr Daten zurück, ist dies wirklich das Ende
der Datei, kommen aber zusätzliche Zeilen zum Vorschein, wurden diese
offensichtlich zwischenzeitlich angehängt und tail -f
gibt sie aus.
Selbst falls der Admin die Datei umbenennt, bleibt mit dem Daten
konsumierenden Prozess und seinem immer noch offenen Filehandle alles beim
alten, das lesende Programm bekommt dergleichen gar nicht mit.
Abbildung 1: Ein tail -f auf die Datei /var/log/auth.log zeigt erfolgreiche und fehlgeschlagene Login-Versuche am ssh-Daemon an. |
Aber selbst tail -f
kommt aus dem Takt, falls der distributionseigene
Logdateirotierer einsetzt, die alte Datei wegschiebt, komprimiert und
eine frische leere an ihre Stelle setzt. In diesem Fall wäre es fatal,
mit read()
weiterhin auf dem offenen Filehandle herumzuorgeln, denn
frische Daten kommen zweifellos nun in einer völlig anderen Datei an.
Dies bekommt der Log-Jäger mit, in dem der in regelmäßigen Abständen
prüft, ob die Datei unter dem angegebenen Namen noch immer auf die
gleiche i-node im Dateisystem horcht. Fördert stat()
zutage, dass letztere
sich geändert hat, muss der Loganalysator das offene Filehandle schließen und
eine neues auf die geänderte Datei (unter dem gleichen ursprünglichen
Namen) öffnen.
Abbildung 2: Auf dem Linux-Desktop sorgt ein auf den Pushover-Service eingenordetes Browser-Tab für Popups. |
Zum Glück muss heutzutage niemand mehr diese Logik als Programm aufschreiben,
denn mehrere Open-Source-Implementierungen erledigen den Job bereits perfekt.
Für Python existiert zum Beispiel Pygtail
([3]), angeblich ein Python-Port
der weit verbreiteten Utilty logcheck
. Wer kein eigenes Python-Programm schreiben
möchte und auf dynamische Wartbarkeit verzichten kann, darf für den ersten
Teil der Alarm-Pipeline zum Parsen der System-Log-Datei natürlich auch
logcheck
direkt verwenden.
Abbildung 3: Das Mobiltelefon mit installierter Pushover-App zeigt einen Login-Versuch auf dem Ssh-Server der überwachten Linux-Rechners an. |
01 #!/usr/bin/python3 02 import sys 03 import os 04 import re 05 from pygtail import Pygtail 06 07 log_file = '/var/log/auth.log' 08 09 offset_file = os.path.join(os.getenv("HOME"),"data", 10 os.path.basename(sys.argv[0]) + "." + 11 os.path.basename(log_file) + 12 ".offset" ) 13 14 for line in Pygtail(log_file, offset_file=offset_file): 15 if not re.search('CRON',line) and \ 16 not re.search('Connection closed',line): 17 sys.stdout.write(line)
Listing 1 importiert das mittels pip3 install pygtail
installierte
Modul für Python 3.x und schustert in Zeile 9 einen Pfad für die
von Pygtail benötigte
Merkerdatei im Verzeichnis data
im Home-Directory des Users zusammen,
im vorliegenden Fall data/authwatch.auth.log.offset
. Hier legt
Pygtail den Offset in die Datei ab, bis zu dem es beim letzten Aufruf
gelesen hat, und erst wenn hinter dieser Stelle neue Daten auftauchen,
wird es sie auch ausgeben und ansonsten still schweigen. Da der Cron das
Skript später im 5-Minuten-Takt aufgerufen wird, und es sich nach getaner Arbeit
sofort verabschiedet, braucht es diesen persistenten Merker. Das Verzeichnis
~/data
muss der Admin vor Benutzung des Skripts einmal manuell anlegen, falls
es noch nicht existiert.
Weiter blendet Listing 1 regulär auftretende Events auf, wie Einträge in denen
Schlüsselworte "CRON"
oder "Connection closed"
auftauchen. Die Zeilen
15-16 suchen danach mittels regulärer Ausdrücke und dem dafür importierten
Standardmodul re
.
Wer ein neumodisches Linux mit dem vielgescholtenen systemd
fährt, findet
dort keine Logdatei auth.log
, sondern darf mittels journalctl
auf
der Kommandozeile oder den Python-Bindings von
systemd
und deren Methode journal
die letzten Einträge des System-Logs
herauswringen. Statt eines Offsets in einer Datei merkt sich das Skript dann
den Zeitstempel der letzten Abfrage in einer extra Datei und springt bei der
nächsten mit seek_realtime()
knapp darüber hinaus, damit keine Duplikate
erfasst werden. Um rotierte Logdateien braucht sich das Skript in diesem Fall
keine Gedanken zu machen, da systemd
solche Implementierungsniederungen abstrahiert.
requests
Der zweite Teil der Alarm-Pipeline steht in Listing 2, und der dort abgesetzte REST-Request auf den Pushover-API-Server verlangt nach zwei Tokens, die der User mit seiner Registrierung auf dem Pushover-Service erhält. Die ersten vier Wochen sind kostenlos, wer Gefallen daran findet, kann danach für $4.99 eine Lizenz erwerben.
Den ersten Token unter dem Schlüssel user
sehen registrierte User auf der
Dashboard-Übersicht in Abbildung 4. Der zweite Token unter dem Schlüssel token
identifiziert die App gegenüber Pushover,
im vorliegenden Fall das Python-Skript in Listing 2, das
ich bei Pushover unter dem Namen Snapshot
registriert habe. Für einen
erfolgreichen REST-Request mit der Python-Library requests
fehlt nur noch der
Parameter message
mit dem Nachrichtentext und schon setzt sich die Methode
post()
in Zeile 9 mit Pushover in Verbindung. Nach Python-Manier wirft die
Bibliothek bei auftretenden Fehlern Exceptions, die ohne Bearbeitung das Programm
abbrechen und einen Stacktrace ausgeben, der hoffentlich bei der Behebung hilft.
Die Python-Library requests
verspricht mit ihrem Werbeslogan "HTTP for Humans" nicht
zuviel. Sie kommt durchdachter daher wie die von mir letztes Mal gescholtenen <urllib>
und <urllib2>.
Abbildung 4: Registrierte User sehen im Dashboard ihren User-Token. |
01 #!/usr/bin/python3 02 import requests 03 import sys 04 import re 05 06 string = sys.stdin.read()[:1024] 07 08 if re.search('\S', string): 09 r = requests.post( 10 'https://api.pushover.net/1/messages.json', 11 data = { 12 'token':'XXXXXXXXXXXXXXX', 13 'user':'YYYYYYYYYYYYYYY', 14 'message':string 15 })
Zeile 6 in Listing 2 holt den vom ersten Teil der Pipeline gesendeten Text aus
der Standardeingabe herein
und stutzt ihn mit der in Python üblichen Syntax für Array-Slices [:1024]
auf
die bei Pushover maximal zulässige Nachrichtenlänge von 1024 Zeichen zurecht.
Abbildung 5: Neu registrierte Apps des Users bekommen einen Token. |
Das if
-Konstrukt ab Zeile 8 prüft dann mit dem regulären Ausdruck \S
, ob
die Nachricht überhaupt druckbare Zeichen enthält und beendet Leerfahrten ohne
viel Federlesens.
Bleibt nur noch, einen Cronjob aufzusetzen, der die Pipeline etwa alle 5 Minuten
aufruft und der in der Environment-Variablen $HOME
das Home-Verzeichnis des
Admins findet damit das beschreibbare Verzeichnis data
zur Ablage der Merkerdatei:
*/5 * * * * /path/authwatch | /path/pushover
Abgesetzte Nachrichten verbreitet Pushover auf alle vom User registrierten Devices, und so kann es sein, dass ein fehlgeschlagenes Login ein wahres Feuerwerk an Notifications auslöst, wenn in einem Raum mehrere Mobilgeräte aktiv angeschlossen sind. Aber zumindest zählt dies in den Nutzungsbeschränkungen als nur eine Nachricht, von denen pro Monat insgesamt 7.500 erlaubt sind bevor der Pushover-Server den Zähler zurücksetzt.
Listings zu diesem Artikel: http://www.linux-magazin.de/static/listings/magazin/2017/05/snapshot/
Michael Schilli, "Private Rezeption": Linux-Magazin 04/16, S.94, <U>http://www.linux-magazin.de/Ausgaben/2016/04/Perl-Snapshot
"pygtail", Logcheck's logtail2 for Python, https://github.com/bgreenlee/pygtail