Aus Linux-Magazin 12/2017

Mit Firewall, Appliance und Python-Skript das Heimnetz überwachen

© Andriy Popov, 123RF

Was flackern die Lichter am Router so aufgeregt? Ein Eindringling? Mike Schilli geht der Sache per PF-Sense und Micro-Appliance nach und lässt sich vom Screen Scraper auch außer Haus per Mail informieren.

Schade, dass es noch keine Router gibt, die einfach auf einer LED-Anzeige die Adressen durchrauschender Netzwerkpakete anzeigen. Weil ich Bescheid wissen will, was im Hausnetz so abgeht, habe ich mir auf Ratschlag eines Arbeitskollegen eine Micro-Appliance der chinesischen Firma Protectli (Abbildung 1, [2]) gekauft, auf der die Free-BSD-basierte Open-Source-Firewall PF-Sense läuft. Das Gehäuse ist etwa 10 mal 10 Zentimeter groß und passiv gekühlt, verursacht also keinen Lüfterlärm.

Abbildung 1: Die Micro-Appliance von Protectli als Router.

Abbildung 1: Die Micro-Appliance von Protectli als Router.

Die Installation war ein Klacks – einfach die Distribution von der Webseite der PF-Sense Community Edition [3] auf einen bootbaren USB-Stick geladen, eine M-SATA-Platte und RAM in das kleine Gehäuse des Protectli eingebaut und nach dem Booten noch die Installationsfragen abgenickt, schon kam das Web-GUI von PF-Sense hoch (Abbildung 2).i

Abbildung 2: Endlich ein Gerät, um den gesamten Netzwerkverkehr des Hauses zu überwachen.

Abbildung 2: Endlich ein Gerät, um den gesamten Netzwerkverkehr des Hauses zu überwachen.

Wächter am Portal

Die Appliance hängt direkt an der Schnittstelle zum Internet (in meinem Fall ein DSL-Modem zum ISP) und bietet auf der LAN-Seite allen im Netzwerk der Wohnung betriebenen Geräte (bei mir einer Reihe gerouteter Subnetze) den Weg ins Internet.

Mit einem vierkernigen Celeron ausgerüstet ist sie bullig genug, um jedes einzelne durchsausende Paket anzuschauen, Statistiken zu erstellen oder sogar bei Bedarf einzuschreiten und entsprechend eingestellter Firewallregeln bestimmte Kommunikationsversuche abzublocken. Will ich wissen, warum die Router-Lichter flackern, brauche ich nur das PF-Sense-GUI aufzurufen, um zu sehen, wer gerade Spotify streamt, Netflix-Filme schaut oder Bestellungen auf Amazon aufgibt (Abbildung 2).

Das Firewall-GUI bietet neben traditionell Terminal-basierten Tools wie PF-Top auch sehr elegante Zusatzpakete wie Ntop-NG, damit der Admin nach Herzenslust in Tortengrafiken und HTML-Tabellen herumstöbern kann, will er herausfinden, wer am meisten Bandbreite verbraucht oder zu Rechnern in dubiosen Ländern Kontakt aufnimmt (Abbildung 3).

Abbildung 3: Kuchengrafiken für Server und Client-Ports.

Abbildung 3: Kuchengrafiken für Server und Client-Ports.

Leider gibt es kein offizielles API zum GUI, nur ein so genanntes Faux-API [4], das als Zusatzpaket auf der PF-Sense-Distribution läuft und eingeschränkten Zugriff auf Interna bereitstellt.

Schlüsseli für Protectli

Um nun in regelmäßigen Zeitabständen zu kontrollieren, was auf dem Protectli-Kästchen abgeht, dachte ich mir, es wäre ganz einfach, einen Screen Scraper [5] zu schreiben, der sich periodisch und automatisch auf der Login-Seite des Kästchens einloggt (Abbildung 5), die im Dashboard dargestellten Daten einscannt und per Mail verschickt.

Abbildung 5: Erste Hürde: Login.

Abbildung 5: Erste Hürde: Login.

Als erste Hürde liegt aber zwischen einem Kommandozeilentool und den fruchtigen Netzwerkdaten die von PF-Sense trotzig präsentierte Login-Seite. Ein Blick auf den HTML-Code (Abbildung 4) offenbart, dass die beiden Felder zur Entgegennahme des Usernamens und des Passworts »usernamefld« und »passwordfld« heißen und der Button zum Absenden auf den Namen »login« hört. Der rasch zusammengeklopfte Python-Scraper in Listing 1, der das mittels »pip3« installierte Modul »selenium« nutzt, um einen Browser zu simulieren, sucht und findet diese Elemente mit der Funktion »find_element_by_name()«.

Listing 1

dash-scraper.py

01 #!/usr/bin/python3
02
03 from selenium import webdriver
04 import yaml
05
06 driver = webdriver.Firefox()
07
08 creds = yaml.safe_load(open('creds.yaml', 'r'))
09
10 driver.get('https://192.168.241.1')
11
12 user_field = driver.find_element_by_name("usernamefld")
13 pass_field = driver.find_element_by_name("passwordfld")
14
15 user_field.send_keys(creds['pfsense_user'])
16 pass_field.send_keys(creds['pfsense_password'])
17
18 driver.find_element_by_name('login').click()
19
20 driver.save_screenshot('saved.png')
21 driver.close
Abbildung 4: Im Sourcecode der Login-Seite finden sich die Felder für Usernamen und Passwort.

Abbildung 4: Im Sourcecode der Login-Seite finden sich die Felder für Usernamen und Passwort.

Damit der Aufruf »webdriver.Firefox()« auch funktioniert und den Firefox-Browser öffnet, braucht die Linux-Distribution das Programm »geckodriver«, das es auf [6] als Tar-File gibt, das der Admin entpackt und das herausfallende Binary in einem unter »$PATH« auffindbaren Pfad ablegt. Das Skript öffnet den Browser, fährt zur Login-Seite, befüllt die Formularfelder und klickt dann auf den »Login«-Button. Das Modul »selenium« kommt häufig zum Testen von Web-GUIs zum Einsatz und macht es wirklich einfach, einen vor dem Browser sitzenden User zu simulieren.

Die nach dem Login-Screen von PF-Sense angezeigte Dashboard-Seite mit den Übersichtsdaten zur Firewall speichert der Aufruf von »save_screenshot()« als Abzug in der Datei »saved.png« (Abbildung 6). Die einzufüllenden Werte liest das Skript aus der Yaml-Datei »creds.yaml« ein, und im Dictionary »creds« stehen anschließend Username und Passwort (Abbildung 7).

Abbildung 6: Statusinformationen der PF-Sense-Firewall.

Abbildung 6: Statusinformationen der PF-Sense-Firewall.

Abbildung 7: Sensitive Daten in der Yaml-Datei <code>creds.yaml</code>.

Abbildung 7: Sensitive Daten in der Yaml-Datei »creds.yaml«.

Hoch auf dem gelben Wagen

Um die eingesammelten Daten periodisch dem Admin zuzuschicken, kommt Listing 2 zum Zug, das die von Listing 1 erzeugte PNG-Datei als Attachement in eine E-Mail packt und sie über einen SMTP-Server verschickt. Um die sicherheitsrelevanten Variablen für den SMTP-Server, den Usernamen und das Passwort dort einzulesen, holt Zeile 10 dieselbe Yaml-Datei von vorher ein und legt ihren Inhalt im Dictionary »creds« ab.

Listing 2

mail.py

01 #!/usr/bin/python3
02
03 import smtplib
04 import yaml
05 from email.mime.multipart import MIMEMultipart
06 from email.mime.text import MIMEText
07 from email.mime.image import MIMEImage
08 from email import encoders
09
10 creds = yaml.safe_load(open('creds.yaml', 'r'))
11
12 attachment    = 'saved.png'
13 body          = "The latest pfSense Dashboard."
14
15 msg = MIMEMultipart()
16 msg['From']=creds['from_email']
17 msg['To']=creds['to_email']
18 msg['Subject']='pfSense Status'
19
20 msgText = MIMEText(
21         '<b>%s</b><br><img src="cid:%s"><br>'
22         % (body, attachment), 'html')
23 msg.attach(msgText)
24
25 fp = open(attachment, 'rb')
26 img = MIMEImage(fp.read())
27 fp.close
28 img.add_header('Content-ID', "<{}>".format(attachment))
29 msg.attach(img)
30
31 server = smtplib.SMTP(creds['smtp_server'], 587)
32 server.starttls()
33 server.login(creds['smtp_user'],creds['smtp_password'])
34 server.sendmail(creds['from_email'],
35         creds['to_email'], msg.as_string())
36 server.quit()

Das Skript baut dann einen HTML-Body zusammen – mit einleitendem Text und IMG-Link auf das anhängende Image, damit der Webmail-Client es auch grafisch darstellt. Ab Zeile 31 nimmt Listing 2 Verbindung zum SMTP-Server auf, dessen Adresse ebenfalls in »creds.yaml« unter dem Schlüssel »smtp_server: mail.provider.net« liegt. Es nutzt Port 587 und überträgt die Daten TLS-verschlüsselt. Den Abzug hängt es im MIME-Format an und fügt einen E-Mail-üblichen Content-ID-Header mit dem Namen der Image-Datei in eckigen Klammern hinzu.

Abbildung 8 zeigt, wie die Mail auf Gmail ankommt. Als Cronjob einmal täglich aufgerufen, bleibt der Admin auch außer Haus auf dem Laufenden über das, was im heimischen Netz abläuft.

Abbildung 8: Das vom Bildschirm gekratzte Dashboard kommt als E-Mail an.

Abbildung 8: Das vom Bildschirm gekratzte Dashboard kommt als E-Mail an.

Online PLUS

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

Infos

  1. Listings zu diesem Artikel: https://www.linux-magazin.de/static/listings/magazin/2017/12/snapshot/
  2. Firewall-Micro-Appliance: https://www.amazon.com/gp/product/B01GIVQI3M
  3. PF-Sense Community Edition Download: https://www.pfsense.org/download/
  4. PF-Sense Faux-API: https://github.com/ndejong/pfsense_fauxapi
  5. Katharine Jarmul, Richard Lawson, “Python Web Scraping (2nd edition)”: Packt 2017
  6. Geckodriver zum Hochfahren des Firefox: https://github.com/mozilla/geckodriver/releases/tag/v0.19.0

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 verschiedener Programmiersprachen. Unter mailto:mschilli@perlmeister.com beantwortet er gerne Fragen.

DIESEN ARTIKEL ALS PDF KAUFEN
EXPRESS-KAUF ALS PDFUmfang: 3 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
Nach oben