Aus Linux-Magazin 04/2016

Anmeldeversuche im heimischen WLAN überwachen

© Dmitry Kalinovsky, 123RF

Push-Notifications auf sein Handy halten den abwesenden Hausherrn darüber auf dem Laufenden, welche Clients sich im WLAN zu Hause an- und abmelden.

Online PLUS

Im Screencast demonstriert Michael Schilli das Beispiel: https://www.linux-magazin.de/Ausgaben/2016/04/plus

Immer wenn die Lichter an meinem Router wild zu blinken anfangen, ohne dass ich selbst im Netz aktiv bin, kommt mir der Verdacht, ob nicht einer meiner Nachbarn mein WPA2-Passwort geknackt hat und mit meinem Internetaccount Schabernack treibt. Ich wohne in einer Großstadt im zweiten Stock eines Hauses auf halber Höhe eines gewaltigen Hügels. Vermutlich empfangen ein paar Hundert Leute das Signal meines WLAN-Routers – und die Taugenichtse unter ihnen würden gerne damit herumspielen.

Die heute vorgestellten Skripte schnappen sich daher in regelmäßigen Abständen die beim WLAN-Router angemeldeten schnurlosen Geräte und schicken mir jedes Mal eine kurze Nachricht auf den Handyschirm, wenn sich ein neuer Client in meinem drahtlosen Netzwerk daheim an- oder abmeldet.

Dazu musste ich keine neue Handy-App schreiben, vielmehr bietet “Prowl” fürs iPhone (und “Notify my Android” für Android-Geräte) ein Webinterface, über das Anwendungen ihre Nachrichten abschicken können. Die Prowl-Server sorgen anschließend dafür, dass die Events an Endgeräte mit laufender Prowl-App zu den angemeldeten Usern weitergeleitet werden. Das funktioniert selbst bei verriegeltem Handy, dann erscheinen die Nachrichten kurz auf dem Lockscreen (Abbildungen 1 und 2).

Abbildung 1: So erfährt der Handy-Nutzer, wer sich zu Hause ins WLAN eingeklinkt hat.

Abbildung 1: So erfährt der Handy-Nutzer, wer sich zu Hause ins WLAN eingeklinkt hat.

Unbrauchbares Werkzeug

Wie kommt der Asus-RT-66U-Router, auf dem ich sofort nach dem Kauf wie immer reflexartig die freie DD-WRT-Software installiert habe, dazu, die angemeldeten User zu überwachen und bei Änderungen den Webservice auf dem Prowl-Server anzurufen? Der Prozess, der auf dem Router die dynamischen DHCP-IP-Adressen ausgibt, heißt »dnsmasq« und legt die zurzeit ausgegebenen DHCP-Leases in der Datei »/tmp/dnsmasq.leases« ab. Das verwendete Spaltenformat (Listing 1) listet für jede Lease die Gültigkeitsdauer in Sekunden auf, die MAC-Adresse des Geräts, die ihm zugewiesene IP-Adresse und einen Namen, mit dem das Gerät sich selbst beschreibt.

Abbildung 2: Der Router schiebt die DHCP-Daten auf einen Server, auf dem ein Cronjob den Prowl-Server auf dessen Web-API benachrichtigt. Der sendet eine Push-Notification an ein Handy mit installierter Prowl-App.

Abbildung 2: Der Router schiebt die DHCP-Daten auf einen Server, auf dem ein Cronjob den Prowl-Server auf dessen Web-API benachrichtigt. Der sendet eine Push-Notification an ein Handy mit installierter Prowl-App.

Listing 1

dnsmasq.leases

1 86400 74:da:42:1b:44:a7 192.168.20.148 raspberrypi *
2 86400 e8:80:2e:e9:11:a9 192.168.20.130 MikesiPhone e8:80:2e:e9:11:a9
3 86400 10:68:3a:17:4a:ba 192.168.20.131 android-oba143110ae5ee34 10:68:3a:17:4a:ba
4 86400 00:51:b6:76:a1:b6 192.168.20.134 Mikes-Macbook 00:51:b6:76:a1:b6

Auf einer Linux-Distribution wäre es relativ einfach, in einer Endlosschleife ankommende oder abgehende Clients zu erfassen und bei Änderungen einen HTTP-Request auf das Prowl-API abzusetzen. Allerdings bietet die DD-WRT-Distro auf dem Router nur eine sehr eingeschränkte Auswahl an Unix-Tools, und die sonst triviale Aufgabe, die Lease-Datei mit Perl zu durchforsten und im Bedarfsfall HTTP-Requests abzufeuern, wird zum Denksporträtsel.

Zwar könnte ich Perl auf dem Router installieren sowie einige CPAN-Module, aber je mehr die Konfiguration von einer fabrikneuen DD-WRT-Distribution abweicht, desto fehleranfälliger wäre das Ergebnis bei Upgrades. Ein einfaches Shellskript, das nur die aus der Busybox stammenden Tools auf dem Router verwendet, läuft hoffentlich auch in ein paar Jahren noch ohne manuelle Anpassungen und lässt sich relativ problemlos über das DD-WRT-GUI installieren. Das speichert das Skript im NVRAM des Routers, pflanzt es bei einem Neustart ins neue Dateisystem und startet seine Endlosschleife.

Mit Engelszungen

Das Shellskript in Listing 2 beschränkt sich daher darauf, die Datei mit den ausgegebenen IP-Adressen mittels »ssh« auf einen Account auf einen Server bei meinem Hoster zu spielen, wo ein Cronjob sie regelmäßig auf Abweichungen überprüft und mit Perl und einem von Prowl eingeholten API-Key den Prowl-Server kontaktiert.

Listing 2

lease-push.sh

01 #!/bin/sh
02 file=/tmp/dnsmasq.leases
03 var=lease-file
04
05 HOME=/tmp/root
06
07 cd /tmp
08 uudecode <<'EOT'
09 begin 664 keyfile
10 M5V%S(&EC:"!N:6-H="!W96G#GRP@;6%C:'0@;6EC:"!N:6-H="!H96G#GRX@
11 [...]
12 51\.V=&4N($]D97(@4V-H:6QL97(N
13 `
14 end
15 EOT
16
17 cd /tmp/root/.ssh
18 uudecode <<'EOT'
19 begin 664 known_hosts
20 M3&ER=6T@3&%R=6T@3,.V9F9E;'-T:65L+B!797(@;FEC:'1S(&AA="P@9&5R
21 [...]
22 >(&AA="!N:6-H="!V:65L+B!$;VYA;&0@5')U;7`N
23 `
24 end
25 EOT
26
27 cd /tmp
28 while [ "forever" ]
29 do
30     sum=`/usr/bin/sha1sum $file | cut -d " " -f1`
31     stored="$(/usr/sbin/nvram get $var)"
32
33     if [ "$stored" != "$sum" ]
34     then
35      /usr/bin/scp -i keyfile $file perlsnapshot@somehoster.com:
36      /usr/sbin/nvram set $var=$sum
37     fi
38
39     sleep 30
40 done

Selbst diese Aufgabe ist allerdings nicht ganz einfach, denn der »scp« -Client der auf Busybox basierenden Distribution ist nicht voll funktionsfähig und lässt sich nur mit Engelszungen dazu überreden, sich gegenüber dem Server beim Hoster mit Hilfe eines privaten Schlüssels zu identifizieren und dann die Datei dorthin zu übertragen.

Übergebe ich dem »ssh« -Prozess auf DD-WRT einen mit »ssh-genkey« erzeugten Private Key, dann verabschiedet er sich kurz und knapp mit der Meldung, dass irgendwo in den Tiefen seiner Implementierung angeblich ein String zu lang ist. Wer die Meldung googelt, findet heraus, dass die abgespeckte »ssh« -Version ein spezielles Format für Private Keys benötigt. Dies erzeugt ein Tool namens »dropbearkey« auf der DD-WRT-Distribution auf dem Router, in die ich mich mit »ssh« unter »root« einloggen kann, nachdem ich auf dem GUI unter »Services« den SSH-Daemon aktiviert habe. Den private Key erzeugt der Aufruf

dropbearkey -t rsa -f   keyfile

dann in der Datei »keyfile« .

Den zugehörigen public Key zeigt »dropbearkey« auf der Standardausgabe. Wer diesen Textstring in die Datei ».ssh/authorized_keys« auf dem Server kopiert und im Router per Kommandozeile mit »ssh -i keyfile Servername« die Verbindung aufbaut, muss also kein Passwort mehr eingeben und kann den Prozess automatisieren.

Backe, backe Kuchen

Allerdings überlebt das Dateisystem auf dem Router keinen Reboot, da der es im flüchtigen Speicher ablegt und bei einem Neustart aus Informationen aus dem NVRAM zusammenbaut. Deswegen ist es üblich, dass Skripte auf dem Router alle benötigten Informationen in ihren Code einbacken. Die Binärdaten aus der speziellen Private-Key-Datei bändigt Listing 2 daher mit dem leicht vergreisten Utility »uudecode« , dessen zwar unleserlicher, aber genau 61 Zeichen breiter Datensalat perfekt in ein Source-Listing passt. Erzeugt hat ihn vorher der Aufruf:

cat keyfile | uuencode -f keyfile

Dessen Ausgabe habe ich von Hand in Listing 2 verfrachtet. Ähnliches gilt für die Datei »known_hosts« , die die Hostkeys vertrauter Hosts auflistet. Der erste Aufruf von »ssh« zur Verbindung mit einem neuen Server fragt auf dem Terminal interaktiv nach, ob der Server vertrauenswürdig erscheint. Antwortet der User mit »y« , legt SSH den Hostkey in »~/.ssh/known_hosts« ab. Die »uuencode« -Daten ab Zeile 19 kamen zustande, indem ich »uuencode« auf die so entstandene Datei »known_hosts« ansetzte und mit der Maus ins Listing 2 kopierte.

Richtung St. Nimmerlein

Die Endlosschleife ab Zeile 28 in Listing 2 prüft alle 30 Sekunden auf dem Router, ob die Liste der mit »cut« ausgeschnittenen MAC-Adressen einen anderen »sha1sum« -Stempel ergibt als beim vorigen Durchlauf. Damit das auch nach einem Reboot weiterläuft, speichert Zeile 36 den aktuellen Wert mit »nvram set« im nicht flüchtigen Speicher des Routers, von wo ihn »nvram get« in Zeile 31 wieder hervorzaubert, um ihn mit dem aktuellen Wert aus der Lease-Datei zu vergleichen und bei Abweichungen die aktuelle Datei auf den Server zu übertragen.

Damit der Router das Skript nach jedem Reboot ausführt, pflanze ich es auf dem Web-UI unter »Administration | Scripts« in das mit “Commands” betitelte Textfeld und klickt auf den Knopf »Save At Startup« am unteren Rand des Fensters (Abbildung 3). Geht etwas schief, ist es nicht ganz einfach, die Ursache zu finden, da die abgespeckten Kommandos nur sehr liederlich Auskunft geben. Aber wer sich mit SSH auf dem Router einloggt und das Skript von Hand in der Kommandozeile ausführt, kommt der Fehlerursache normalerweise näher.

Abbildung 3: Dieses Skript gibt die Lease-Einträge an den Server.

Abbildung 3: Dieses Skript gibt die Lease-Einträge an den Server.

Überbringer

Der Router sorgt also dafür, dass die aktuelle Version der Lease-Datei stets auf einem Linux-Server bereitsteht, von wo sich ein minütlich einsetzender Cronjob ihrer annimmt, Änderungen feststellt und im Bedarfsfall den Prowl-Webservice benachrichtigt:

* * * * * /home/perlsnapshot/lease-notify

Der Prowl-Service verlangt, dass sich Nutzer auf http://www.prowlapp.com mit einem Usernamen und Passwort registrieren, die Angabe einer E-Mail-Adresse ist nicht notwendig, hilft aber später aus der Not, falls der User das Passwort vergisst. Ein Account berechtigt zum Empfang von API-Keys, mit denen sich Applikationen, die Events senden wollen, gegenüber dem Webservice ausweisen. Der Prowl-Server leitet die Kurznachrichten dann an alle Instanzen der App weiter, die unter dem Account angemeldet sind. Die App kostet 3 Dollar für das iPhone, dagegen sind die Registrierung auf der Webseite zum Einholen eines API-Key und das Weitersenden der Push-Notifications bei bis zu 1000 gesendeten Events pro Tag kostenlos.

Link auf Wunsch

Der einfache Test in Listing 3 veranschaulicht das Verfahren mit dem CPAN-Modul WebService::Prowl, das den Zugriff auf den Webservice schön abstrahiert. Es verlangt einen API-Key, den es auf http://prowlapp.com unter dem Reiter »API Keys« als Textstring für eingeloggte User gibt (Abbildung 4). Die Methode »verify()« in Zeile 8 prüft, ob die Kommunikation mit dem Prowl-Server funktioniert, und die Methode »add()« in Zeile 10 nimmt die vier Textstrings der übermittelten Nachricht entgegen.

Listing 3

prowl-test

01 #!/usr/local/bin/perl -w
02 use strict;
03
04 use WebService::Prowl;
05 my $ws = WebService::Prowl->new(
06     apikey => "xxxxxxxxxxxxxxxxxxxxxxx");
07
08 $ws->verify || die $ws->error();
09
10 $ws->add(
11     application => "Perl Snapshot",
12     event       => "Just a test.",
13     description => "Huzzah, it works!",
14     url         => "http://linux-magazin.de",
15 );
Abbildung 4: Auf <custom name="key" srcset=

prowlapp.com« gibt es kostenlose API-Keys.” width=”300″ height=”228″ /> Abbildung 4: Auf prowlapp.com« gibt es kostenlose API-Keys.

Die ersten drei geben den Namen der sendenden Applikation, die Art des Events und zusätzlich noch einen kurzen Beschreibungstext an. Der vierte Parameter »url« gibt eine URL an, auf die das Handy springen soll, wenn der User auf den Event tippt und dann sein Einverständnis zum Öffnen des Browsers gibt. In Abbildung 5 kommt die Push-Notification des von Listing 3 abgesetzten Test-Events gerade auf meinem verriegelten iPhone 5 an. Die Abbildung 6 zeigt den Dialog, der erscheint, nachdem der Nutzer mit dem Finger auf den Event getippt hat.

Abbildung 5: Das iPhone zeigt die Testnachricht auf dem Lockscreen.

Abbildung 5: Das iPhone zeigt die Testnachricht auf dem Lockscreen.

Abbildung 6: Die dem Event beiliegende URL wird geöffnet.

Abbildung 6: Die dem Event beiliegende URL wird geöffnet.

Auf Server-Seite läuft das Skript in Listing 4 als Cronjob. Es unterhält einen persistenten Datenspeicher in der Datei »leases.dat« , mit dem es in Zeile 14 mittels »tie« den Hash »%leases« verknüpft. Nach dem Programmstart sucht »lease-notify« eine Datei namens »dnsmasq.leases« , liest sie mit dem CPAN-Modul »Path::Tiny« ein und wurstelt sich mit »lines()« in Zeile 20 durch dessen Textzeilen. Die durch Leerzeichen getrennten Felder spaltet »split()« in Zeile 21 auf in verbleibende Lease-Dauer, MAC-Adresse, vergebene IP-Adresse und den Gerätenamen.

Listing 4

lease-notify

01 #!/usr/bin/perl -w
02 use strict;
03 use lib '/home/perlsnapshot/perl5/lib/perl5';
04 use Path::Tiny;
05 use DB_File;
06 use WebService::Prowl;
07 use File::Basename;
08
09 my $in_file  = "dnsmasq.leases";
10 my $db_file  = "leases.dat";
11 my $API_KEY  = "xxxxxxxxxxxxxxxxxxxxxxx";
12 my $APP_NAME = "Wifi watch";
13
14 tie my %leases, 'DB_File', $db_file;
15
16 my $file  = path( $in_file );
17
18 my %found = ();
19
20 for my $line ( $file->lines ) {
21   my( $secs, $mac, $ip, $name ) =
22       split " ", $line;
23
24   $found{ $mac } = 1;
25
26   if( !exists $leases{ $mac } ) {
27     $leases{ $mac } = "$ip $name";
28     notify( "joined: $leases{ $mac }" );
29   }
30 }
31
32 for my $mac ( keys %leases ) {
33   if( !exists $found{ $mac } ) {
34     notify( "left: $leases{ $mac }" );
35     delete $leases{ $mac };
36   }
37 }
38
39 untie %leases;
40
41 ###########################################
42 sub notify {
43 ###########################################
44   my( $event ) = @_;
45
46   my $ws = WebService::Prowl->new(
47       apikey => $API_KEY );
48
49   $ws->verify || die $ws->error();
50
51   $ws->add(
52       application => $APP_NAME,
53       event       => $event,
54       description => "Home Wifi clients",
55       url         => "",
56   );
57 }

Kommen und gehen

Im persistenten Hash »%leases« speichert Listing 4 dann alle gefundenen MAC-Adressen und weist sie den zugehörigen IP-Adressen und Gerätenamen zu. So weiß es später, welche Geräte beim vorigen Durchlauf schon vorhanden waren und welche gerade neu dazugekommen sind. Letztere trägt Zeile 27 dann ebenfalls in den Speicher ein, und Zeile 28 schickt mit der ab Zeile 42 definierten Funktion »notify« Events ab. Ähnliches gilt für Geräte, die beim vorigen Durchlauf aktiv waren, damit in »%leases« stehen, aber nun beim aktuellen Durchlauf fehlen. Diese speichert der flüchtige Hash »%found« . Bei einer festgestellten Diskrepanz schickt Zeile 34 die Nachricht ab, dass sich ein Teilnehmer verabschiedet hat.

Die Funktion »notify()« ab Zeile 42 sieht im Prinzip genau so aus wie das vorher in Listing 2 vorgestellte Testskript. Es nutzt den am Skriptanfang in »$API_KEY« abgelegten Prowl-API-Key und füllt nur die Applikationsnamen, die Art des Events (»joined« beziehungsweise »left« ) und die Beschreibung aus, während es das URL-Feld leer lässt.

Da mein billiger Hostingservice mir keinen Rootzugriff erlaubt, habe ich die vom Skript benötigten CPAN-Module lokal im Homeverzeichnis unter »perl5« installiert, was »cpanm« automatisch erledigt, wenn es feststellt, dass es »/usr/lib« nicht manipulieren darf. Damit das Skript die dort installierten Module aber findet, muss Zeile 3 mit »use lib« diesen Pfad explizit definieren.

Noch keine Invasion

Zu meiner großen Beruhigung haben sich während der Testphase des Skripts nur bekannte Geräte im WLAN angemeldet, aber für den Fall einer Invasion über die Luftschnittstelle bin ich jetzt gut vorbereitet. Mir fiel noch auf, dass sich manche Geräte plötzlich mitten in der Nacht mit dem Wifi verbinden, obwohl sie ausgeschaltet in der Ecke liegen, wie zum Beispiel mein E-Book-Reader Kindle Paperwhite.

Das Skript ließe sich diesbezüglich noch relativ einfach verbessern, wenn es wüsste, welche MAC-Adressen zu welchem Gerät gehören, was sich durch einen einfachen Hash in »lease-notify« bewerkstelligen ließe. Die Kurznachrichten kämen dann mit den auf den persönlichen Haushalt maßgeschneiderten Gerätenamen daher und damit gleich klarer rüber.

Mein besonderer Dank geht an meinen Arbeitskollegen Tristan Horn, der die Idee hatte, Zu- und Abgänge im Netzwerk auf einem Telefon anzuzeigen, und dafür auch noch eine Anwendung geschrieben hat, um das Ganze schließlich in ein viel professionelleres Unifi-System einzubinden [2].

Infos

  1. Listings zu diesem Artikel:ftp://www.linux-magazin.de/pub/listings/magazin/2016/04/Perl
  2. Anbindung der Push-Notifications an ein Unifi-System:https://tris.net/software/unifi-logreader

Der Autor

Michael Schilli arbeitet als Software Engineer in der San Francisco Bay Area in Kalifornien. In seiner seit 1997 laufenden Kolumne forscht er jeden Monat nach praktischen Anwendungen der Skriptsprache Perl. Unter mailto:mschilli@perlmeister.com beantwortet er gerne Fragen.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 5 HeftseitenPreis €0,99
(inkl. 19% MwSt.)
LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben