Allgemein

Shell Backup Script erzeugen

Veröffentlicht am

Backups sind wohl eines der wichtigsten Dinge auf einem Server und so darf es natürlich auch nicht auf meinem Server fehlen. Statt einer Software Solution habe ich mir meine mit Hilfe von Shell selbst geschrieben. Das Script ist relativ schmal gehalten, erledigt aber alles mit bravour. Es hat eine Vorhalte Zeit von 5 Tagen welches in der Variable mindir abgespeichert ist. Es werden also insgesamt die letzten 5 Backups beibehalten, die älteren Backups werden automatisch gelöscht.  Die For Schleife beinhaltet alle Ordner die gesichert werden sollen.

Das Interessante ist der find Aufruf, um die ältesten Backups zu finden und diese aufzulisten

`find ${dir} -maxdepth 1 -type d -printf '%T+ %p\n' | sort -n | head -n ${t} | awk '{print $2}'`

Dieser Befehl geht in das Verzeichnis, welches in der Variable „dir“ angegeben worden ist. Mit maxdepth ist die Tiefe angegeben. Der Befehl bleibt also nur im obersten Ordner. Mit type suchen wir nur nach Ordnern. Anhand printf wird das Format der Ausgabe festgelegt. Diese Informationen werden nach sort gepiped, wo die Informationen noch sortiert werden, währen head dann die Ordner heraussucht, die gelöscht werden dürfen. Das awk sucht dann nur noch den Ordnerpfad aus dem String heraus.

Ich habe im ganzen Code noch jede Stelle einzeln kommentiert, damit es besser verstanden werden kann.

#!/bin/sh

#where to save the backup
dir="/root/backup/"
#min number of backups to keep
mindir=5

d=`date +%Y%m%d`
#get the current number of the backup existing
curnumber=`find /root/backup -maxdepth 1 -type d | wc -l`

#check if root backup dir exists
if [ ! -d "${dir}" ]
then
        #if not create
        mkdir ${dir}
        echo "backupfolder does not exists - created" >> /var/log/backup.log
fi

#check if backupfolder exists
if [ ! -d "${dir}${d}" ]
then
        #if not create
        mkdir ${dir}${d}/
        echo "create folder ${d}" >> /var/log/backup.log
        #add folders to be backed up here. Loop iterates over the folders then to back them up
        for f in "/root/Projekte/" "/root/client-configs/" "/root/openvpn-ca/"
        do
                #copy defined folders into the backup folder
                cp -p -R -P ${f} ${dir}${d}
                echo "cp -p -R -P ${f} ${dir}${d}" >> /var/log/backup.log
        done
fi

#to not overblowing the server capacity the backup system only keeps the last ${mindir} backups.
# checks if the current number is greater than or equal to ${mindir}
if [ $(($curnumber - 1 )) -ge ${mindir} ]
then
        #get the numbers of how many backups can be deleted
        t=$((${curnumber} - ${mindir} -1 ))
        #find the last ${t} backups
        lastbackup=`find ${dir} -maxdepth 1 -type d -printf '%T+ %p\n' | sort -n | head -n ${t} | awk '{print $2}'`
        echo "backups to delete: ${lastbackup}" >> /var/log/backup.log
        #delete those backups
        rm -r -f ${lastbackup}
fi

exit 0

Dieses Script kann dann z.B. als Cronjob angelegt werden, damit regelmäßig Backups gemacht werden. Bei mir läuft das Script z.B. jeden Tag um 0:55, dies ist jedem allerdings selbst überlassen.

Die Ausführung, bzw. die Ausgaben des Scripts werden dann unter /var/log/backup.log abgespeichert. Somit kann im Fehlerfall überprüft werden, was denn zu dem Fehler geführt hat. Diese Log Datei befindet sich in einer sogenannten Rotation, damit die Log nicht aufgebläht wird.

 

 

 

Allgemein

IPs mittels iptables automatisch blockieren lassen

Veröffentlicht am

Eigentlich wollte ich meinen vServer noch sicherer machen, indem ich von Seiten wie myip.ms eine vorgefertigte Blackliste herunterlade. Die gezogenen IPs sollten mittels iptables blockiert werden. Ich hatte auch schon soweit alles fertig geschrieben, ein Python3 Script, ein Shell Script und die SQL Scripte zum Anlegen der Datenbank.
Allerdings habe ich nach ein „paar“ Einträgen folgende Nachricht von iptables bekommen: iptables: Memory allocation problem.

Nach ein bisschen Internet Recherche fand ich heraus, dass mich mein Hoster auf eine bestimmte Anzahl an iptable Einträgen begrenzt.

Die maximal Möglichen Einträge findet ihr mit diesem Befehl.

egrep "failcnt|numiptent" /proc/user_beancounters

numiptent steht dabei für die NETFILTER Einträge. In diesem Beitrag steht, dass diese dem Schwellenwert von 200 – 300 Einträgen ebenfalls aufgrund eventueller Performanzprobleme nicht überschreiten sollte. Da ich von myip.ms über 500 Einträge übertragen habe, war ich auch schon weit über das Limit. Ebenfalls habe ich noch diesen Artikel gefunden: https://www.danami.com/clients/knowledgebase/92/How-can-I-fix-the-error-The-VPS-iptables-rule-limit-numiptent-is-too-low.html

Naja, ganz abschreiben wollte ich es noch nicht und vielleicht fällt mir auch noch ein automatischer Prozess ein, den ich integrieren kann. Ich möchte jedoch schon den bis dato geschriebenen Code veröffentlichen.

Da ist zum einen der Shell Code, der die beiden SQL Dateien CREATE_FIREWALL.sql und FIREWALL_SCHEMA.sql aufruft.

#!/bin/bash

if [ -f "CREATE_FIREWALL.sql" ] && [ -f "FIREWALL_SCHEMA.sql" ]
then
                mysql -uuser -ppassword < "CREATE_FIREWALL.sql"
                mysql -uuser -ppassword < "FIREWALL_SCHEMA.sql"
else
        echo "[C] CREATE_FIREWALL.sql OR FIREWALL SCHEMA.sql missing"
fi

in der CREATE_FIREWALL steht die Einrichtung der Datenbank,

CREATE Database IF NOT EXISTS Firewall;
GRANT ALL PRIVILEGES ON Firewall.* TO 'user'@'localhost' IDENTIFIED BY 'password';

während in der FIREWALL_SCHEMA das Schema der Tabelle, der gerade erzeugten DB, beinhaltet.

use Firewall;

SET FOREIGN_KEY_CHECKS = 0;

DROP TABLE IF EXISTS FW_IPS;

SET FOREIGN_KEY_CHECKS = 1;

CREATE TABLE IF NOT EXISTS FW_IPS(
IP varchar(16) NOT NULL,
ORIGIN varchar(100),
SOURCE varchar(100),
STATUS boolean not null default 0,
NEW boolean not null default 0,
PRIMARY KEY(IP)
);

Kurz eine kleine Beschreibung der einzelnen Spalten:

IP zu blockierende IP
ORIGIN Herkunftsland der IP
SOURCE Resource wo diese IP steht, in diesem Fall myip.ms
STATUS Soll dieser Block aktiv sein?(True/False)
NEW Ist diese IP neu dazu gekommen?(True/False)
Python Code

Kommen wir hier zu dem Python Code, der eine Verbindung zu myip.ms aufbaut. Der Aufbau wird mit dem urllib.request Module vollzogen. Als Response bekomme ich eine Byte String, den ich mit .data.read().decode() umkonvertiere. In der Schleife benutze ich regex und entferne bloße Kommentare(gekennzeichnet mit #) oder IPv6 Einträge. Somit bleiben nur die von mir erforderlichen IPv4 Einträge. Diese verbleibenden Einträge kippe ich dann in die Firewall Datenbank. Das PDB ist ein von mir geschriebenes Modul. Die QueryInsert Methode benötigt die SQL-Abfrage, die Werte als Tupel und als letztes ein FLAG, das bestimmt ob ein Update versucht wird, wenn der Private Schlüssel schon existiert. Jenes ist jedoch nicht der Fall, daher habe ich ihn auf False gesetzt. Der NEW FLAG wird bei den Werten als 1 übergeben, da es sich hier immer um eine neue IP handeln wird, wenn die IP in die DB geschrieben wird.

try:
        import urllib.request as req
        import re
        import PDB
except:
        print("could not load modules")
        exit(1)
try:
        #try to open URL
        data = req.urlopen("https://myip.ms/files/blacklist/csf/latest_blacklist.txt")
except urllib.error.URLError as e: print(e.reason)

#decode received data from URL
ips=data.read().decode()

#Open DB Connection
DB= PDB.databaseconnection('localhost','user','password','Firewall')
DB.Connect()

#for each line in ip collected from URL
for line in ips.splitlines():
        #reject lines with a # and a total length of < 15
        if  not re.findall("\A#", line) and len(line) < 15:
                #also check if not empty
                if not line == '':
                        #DB Insert without update process
                        DB.QueryInsert("insert into FW_IPS(%s) VALUES(%s)",{"IP":line,"SOURCE":"myip.ms","STATUS":0,"NEW":1},False)
#Close DB
DB.Disconnect()
Shell Code

In dem Shell Script lege ich zuerst die BLACKLIST Chain an und platziere die bei der INPUT Chain an erster Stelle. Danach geht die Loop über die Einträge in der DB, die das FLAG NEW noch auf 1 stehen haben. Sobald es die IP erfolgreich mit iptables registriert hat, kümmern sich 2 weitere SQL Befehle darum, die FLAGS STATUS und NEW auf jeweils 1 bzw. 0 zu setzen.

 

iptables -N BLACKLIST 
iptables -I INPUT 1 -j BLACKLIST 
for ip in `mysql -s -N -e "select IP from Firewall.FW_IPS where NEW = 1"` 
do 
  iptables -A BLACKLIST -s $ip -j DROP 
  if [ $? -eq 0 ] 
  then 
    mysql -s -N -e "update Firewall.FW_IPS set STATUS = 1 where IP = '${ip}'" 
    mysql -s -N -e "update Firewall.FW_IPS set NEW = 0 where IP = '${ip}'" 
  fi 
done

Der komplette Prozess hat wie gesagt funktioniert, jedoch durch den Lock mehr oder weniger unbrauchbar. Zusätzlich verwende ich nämlich noch fail2ban, der auch Einträge in die iptables tätigt. Alles in allem muss ich mir etwas neues überlegen, damit ich die IPs von bestimmten Herkunftsländern blockieren kann.

 

 

Allgemein

SSH Email bei Client Verbindung

Veröffentlicht am

Mein vServer hat zurzeit den SSH Port offen. Dieser befindet sich zwar nicht mehr standardmäßig auf dem Port 22, um sogenannte Bots größtenteils vom Server zu halten, allerdings wollte ich bei jedem Connect informiert werden, damit ich im Falle einer Kompromittierung direkt reagieren kann.

Der Port kann in der /etc/ssh/sshd_config angepasst werden.

Die eigentliche Mail Benachrichtigung, in der es in diesem Beitrag auch geht, muss in pam.d eingerichtet werden. Eine, wie ich finde gute Seite, die pam.d beschreibt, ist

https://web.archive.org/web/20180303034326/http://www.tuxradar.com/content/how-pam-works

Kommen wir weiter zu den Änderungen der Datei.

Öffnen wir die ssh Datei mit einem beliebigen Editor, in meinem Fall ist es der vim Editor.

vi /etc/pam.d/sshd

Dort muss folgende Zeile eingetragen werden.

session optional pam_exec.so seteuid /etc/ssh/login-notify.sh

Am Anfang sollte der Flag „optional“ benutzt werden, damit bei falscher Konfiguration eine Verbindung trotzdem noch möglich ist. Als letztes Argument wird das Script übergeben.

Das Script zum Versenden der Mail sieht schließlich wie folgt aus.

#!/bin/bash

if [ "$PAM_TYPE" != "close_session" ]; then

body="<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN' 'http://www.w3.org/TR/html4/loose.dtd'>
    <html>
    <head><title>SSH Mail</title>
    </head>
    <body>
    <table>
    <tr>
    <th>Tag</th>
    <th>Value</th>
    </tr>
    <tr>
    <td>service</td>
    <td>SSH</td>
    </tr>
    <tr>
    <td>direction</td>
    <td>connected</td>
    </tr>
    <tr>
    <td>Timestamp</td>
    <td>`date '+%T %F'`
    </tr>
    </table>
    <p></p>
    <p>`env`</p>
    </body>
    </html>"

echo $body | /usr/bin/mail -a "From: ssh@server.com" -a "MIME-Version: 1.0" -a "Content-Type: text/html" -s "SSH Client Connection established" mail@dns.de >> /dev/null
fi
exit 0

Verbindung sich nun jemand per SSH auf dem Server, wird direkt eine Mail generiert und versendet.

Der Mail Body sieht dann genau so aus, wobei RHOST die Remote Addresse enthält.

Allgemein

User löschen in Mysql

Veröffentlicht am

Einen Benutzer in Mysql zu löschen ist nicht sonderlich schwer. Es wird lediglich ein root Benutzer auf der Mysql Datenbank benötigt.

mysql -uroot -ppasswort

Sobald man sich erfolgreich angemeldet hat, tippe folgenden Befehl ein, um all Benutzer der Datenbank zu sehen.

select user,host from mysql.user;
Mysql User
Mysql User

Sucht dabei euren Benutzer aus, den ihr löschen wollt und merkt euch den User + Host. Diese Informationen werden beim Entfernen der Privilegien und des eigentlichen Benutzers benötigt.
Ich hab mir den remote User ausgesucht. Ihr müsst unbedingt aufpassen, dass ihr keinen Mysql oder den Benutzer einer anderen DB erwischt! Das % Zeichen bedeutet im übrigen, dass sich dieser Benutzer von überall anmelden darf. Zumeist ist localhost vorzufinden.

Löschen wir nun mit dem REVOKE Befehl alle Privilegien vom Benutzer.
Der Aufruf des REVOKE sieht wie folgt aus.

 REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'user'@'host';

Es kommen nun die User und Host Informationen ins Spiel, die wir uns zuvor gemerkt hatten.
Bei mir würde es wie folgt aussehen

 REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'remote'@'%';

Falls alles richtig war, wird dieser Befehl mit einem OK quittiert.

Query OK, 0 rows affected (0.00 sec)

Gefolgt wird das Entfernen der Privilegien vom Löschen des Benutzers. Auch hier muss der User und Host angegeben werden.

DROP USER 'remote'@'%';

So wie der vorherige Befehl, wird auch der DROP User quittiert.

Query OK, 0 rows affected (0.00 sec)

Zeigt man sich nun die SQL Benutzer an, so sollte der gelöschte Benutzer nicht mehr auftauchen.

select user,host from mysql.user;

War die Benutzer Löschung im Zuge einer Entfernung von WordPress oder ähnliches, kann die vollständige WordPress Datenbank mit folgenden Befehl gelöscht werden.

DROP DATABASE <dbname>

 

Allgemein

OpenVPN Email bei Clientverbindung

Veröffentlicht am

Ich benutze OpenVPN auf meinem vServer, um eigene Services von der Außenwelt abzuschotten. Sobald ich mich allerdings mit OpenVPN authentifiziert habe, sollen eben jene Services erreichbar sein. Der Vorteil dieser Lösung ist, dass ich nur einen Port, den von OpenVPN, öffentlich stellen muss. Alle anderen Dienste verweilen im internen Netz und sind soweit von außen nicht mehr erreichbar. Dies habe ich durch Firewall regeln festgelegt. Für meinen SSH Dienst hatte ich schon einen Prozess geschrieben, der mich per Mail informiert, sobald eine Verbindung erfolgreich hergestellt wurde. Dies wollte ich nun auch für OpenVPN nachziehen.

Dafür installieren wir eine Abhängigkeit:

apt install auditd

In der server.conf von OpenVPN fügen wir folgende Zeile hinzu

client-connect /etc/openvpn/client-connected.sh

client-connected.sh ist das untere Script, welches ich für die Benachrichtigung geschrieben habe. Da es zu OpenVPN gehört, habe ich es in das Verzeichnis von OpenVPN gelegt.

Zum Erstellen des Scripts kann z.B. der vim Editor benutzt werden. Nano oder andere würden natürlich auch gehen, je nach belieben (Ich benutze meistens vim).

Erstellen wir nun das Script

vi /etc/openvpn/client-connected.sh

und fügen diesen Code Snippet ein. Bitte beachtet, dass ich die Mail Adressen aus Sicherheitsgründen umbenannt habe. Diese müssen nach euren Gegebenheiten angepasst werden.

#!/bin/bash
body="<!DOCTYPE HTML PUBLIC '-//W3C//DTD HTML 4.01 Transitional//EN' 'http://www.w3.org/TR/html4/loose.dtd'>
<html>
<head><title>OpenVPN Mail</title>
</head>
<body>
<table>
<tr>
<th>Tag</th>
<th>Value</th>
</tr>
<tr>
<td>service</td>
<td>openVPN</td>
</tr>
<tr>
<td>direction</td>
<td>connected</td>
</tr>
<tr>
<td>Timestamp</td>
<td>`date '+%T %F'`
</tr>
</table
</body>
</html>"

echo $body | /usr/bin/mail -a "From: mail@dns.com" -a "MIME-Version: 1.0" -a "Content-Type: text/html" -s "OpenVPN Client Connection established" mailadresse@mail.de >> /dev/null

exit 0

Fehlt nur noch der Neustart des OpenVPN Daemon`s

systemctl restart openvpn@server

Nun sollte Mails verschicken werden, sobald eine Verbindung erfolgreich hergestellt wurde. Da es sich um einen HTML Email Body handelt, kann dieser nach belieben angepasst werden.

 

 

Allgemein

Buttom-up und top-down Lernmethode

Veröffentlicht am

Top-Down und Buttom-UpIch bin über einen sehr interessanten Artikel gestoßen, den ich bei LinkedIn gefunden habe. Es geht um das Lernen. In dem Artikel speziell für Machine Learning. Diese Methoden greifen aber auch für fast alles, was persönlich erlernt werden möchte. Dabei fielen die Begriffe Buttom-up und Top-down. Diese geben soweit die Lernrichtung wieder, auf die ich etwas in diesem Beitrag eingehen möchte.

Für die Buttom-Up Methode wird das Lernen des Autofahren herangezogen. Ohne das wir wissen wie ein Auto eigentlich wirklich funktioniert, fahren viele Menschen Auto. Danach kommt die Kenntnis wie ein Motor funktioniert und wie ein Auto überhaupt ins Rollen gerät. Kurz gesagt wir fangen mit der Praxis an und erweitern unser Wissen mit der Theorie nach und nach. Ganz anders ist die Top-Down Methode. Diese wird zumeist in Schulen und Unis praktiziert. Zuerst erlernt man die Theorie, um danach in der Praxis sein theoretische Wissen an zu wenden. Wie ich fälschlicherweise finde, halten viele die Buttom-Up Methode aber trotzdem für unanfechtbar, denn wie willst du etwas richtig machen, wenn du die Theorie nicht beherrschst? Hier kommt der Zwiespalt für viele, denn die Theorie ist meist trocken. Wie schon vorher erwähnt, ist es hier schwer die Motivation dabei hoch zu halten. Bei der Programmierung und Machinen Learning würde es bei der Top-Down Methode wie folgt aussehen. Zuerst lernt man die Sprache kennen, z.B. Python und was sie auszeichnet. Dann kommt die Struktur, Datentypen, etc. All das kann am Anfang Frustration erzeugen und im Nachhinein eventuell zum Entrüsten der Begeisterung für diese Sprache. So schleicht es sich für einige Zeit weiter, bis die Motivation, was neues zu Lernen, komplett verflogen ist – man hat aufgehört bevor es überhaupt angefangen hat. Allerdings kann so z.B. eine Liste von einem Tupel unterschieden werden.

Top-Down und Buttom-Up
Ganz anders die Top-Down Methode. Hier liegt der Fokus auf Ergebnisse und der Praxis selber. Es wird sich ein Projekt ausgesucht – dies sollte die Motivation stabilisieren. Dann fängt man an, dass Programm zu schreiben. Selbstverständlich wird in der Anfangsphase der Code nicht aussehen, als würde er von einem erfahrenen Programmierer kommen, allerdings fördert das Abschließen eines Projekts die Motivation ein neues zu starten, mit eventuell der gleichen Sprache und dem dazugewonnenen Wissen. Bei der Top-Down Methode ist mit dem Projekt ein Grund vorhanden, sich das theoretische Wissen anzueignen. Das Projekt ist emotional an dich gebunden, denn schließlich willst du etwas damit erreichen, was für dich zählt. Dies ist der Grund, weswegen die Theorie trotz alledem motiviert angegangen wird. Wie ich finde, haben beide Methoden ihre Daseinsberechtigung. Ich denke die beste Lernmethode wäre irgendwo in der Mitte von beiden Methoden. Top-Down kann als dass „ins Kalte Wasser schmeißen“ angesehen werden, während bei der Buttom-Up Methode erst erlernt wird, wie sich Wasser verhält, die Physik und vieles mehr. Was allerdings nützt einem die Theorie wenn sie nie in der Praxis angewandt wird? Man also nie ins Wasser geht?

Müsste ich mich allerdings für eine entscheiden, wäre es die Top-Down Methode. Ich lerne wesentlich effektiver durch diese Methode, da ich mir Projekte aussuche, die mir persönlich selber gefallen. Damit habe ich eine Verbindung dazu und möchte dementsprechend auch die Theorie dahinter lernen und das so schnell wie möglich. Ich baue mir im Einklang mit dem Projekt mein Wissen auf. Viel besser als etwas zu Lernen, wo man sich überlegt wofür das überhaupt gut ist, oder nicht 😉

 

Falls jemand Interesse hat, den Link zum eigentlichen Artikel findet ihr hier: https://machinelearningmastery.com/youre-wrong-machine-learning-not-hard/

Allgemein

Neustadt-Glewe

Veröffentlicht am

Neustadt-Glewe BurgAm 13.07.2019 verschlug es mich nach Neustadt-Glewe, eine Stadt in Mecklenburg-Vorpommern. Zu diesem Zeitpunkt lief auch das Airbeat-One Festival im besagten Ort. AirBeat-One ist das größte Elektro-Festival im Nordosten und wird jährlich von rund 40.000 Elektrofans besucht, dass nur als kleine Randnotiz. Ein paar von Fans liefen auch in der Stadt herum 😉 . Mein Tagestrip umfasste allerdings nicht das Festival. Viel mehr war ich auf die Stadt fixiert und was diese zu bieten hat. Im Vorfeld hatte ich mir ein paar Informationen über die Stadt besorgt. Neustadt-Glewe bietet ein Schloss, eine Burg und eine Kirche zur Besichtigung an. Allerdings fand ich die Gegend ebenfalls sehr interessant. Vor allem durch die Müritz-Elde-Wasserstraße und den teils älteren Hausbauten.

Meine Tour

Meine Tour startete allerdings bei den Schrebergärten, die ich hinter einem Restaurant neben der Wasserstraße gefunden habe. Aus diesen Gärten stammen auch die Blumen aus der Galerie für Neustadt-Glewe. Ich hatte nicht viel Zeit, denn es folgte ein kleiner Regenschauer, den ich unter einem nahegelegenen Baum verbracht habe. Der Tag wurde stetig von kleinen Schauern verfolgt, die im nachhinein aber nicht sonderlich gestört haben. Ich musste dennoch die Kamera Einstellungen stetig anpassen, um auf die Schauer zu reagieren. So stimmte die Belichtung teilweise nicht mehr, wenn es einen Wetterumschwung gab. Ich durfte auch nicht zu lange Belichtungszeiten wählen, da ich freihand fotografiert habe. Einen von den Schauern habe ich jedoch in der evangelische Kirche überstanden. Ich war verwundert, dass die Kirche für die Öffentlichkeit zugänglich war, ohne dass eine Messe stattfand. Aus Respekt habe ich hier keine Fotos gemacht. Nach dem Schauer habe ich die Burg erneut aufgesucht und ein paar Fotos gemacht. Inmitten der Burg ist ein kleines Restaurant, welches Kuchen und Kaffee serviert. Führungen werden dort auch angeboten, jedoch schien am meinem Tag keine zu sein. So bin ich in die Hinterlandschaft der Burg geschlendert. Immer auf der Suche nach neuen Möglichkeiten. Sehr schön fand ich auch die Innenstadt mit ihren alten Charm und den alten Häusern. Als ich so durch die Straßen streifte, habe ich das ein oder andere schöne Gebäude aufgefunden. Zwischenzeitlich habe ich mich in einem Cafe ausgebreitet um mir den ersten Berg an Bildern angeschaut. Leider bliebt es mir verwehrt, Fotos in der Nacht zu schießen. Dort hatte es die ganze Zeit über geregnet. So verblieb ich nur mit den Bildern, die ich tagsüber gemacht habe.

 

Bilder von Neustadt-Glewe

Alle Bilder habe ich noch leicht mit Lightroom verändert, um den Bildern noch das gewisse Extra zu verleihen.

Blog

check_mk auf Ubuntu installieren und einrichten

Veröffentlicht am

check_mkcheck_mk ist, wie nagios und icinga2, ein Monitoring Tool. Vorerst basierte es auf dem nagios Kern, profitiert mittlerweile aber schon von seinem eigenen Monitoring Kern, genannt checkmk MultiCore(CMC). Der Kern ist das Herzstück eines jeden Monitoringsystems, da es die Plugins anstößt, die Ergebnisse sammelt und aufbereitet. Laut Hersteller sind über 1700 „intelligenter Plugins“ verfügbar, die die Hosts überwachen können. Dabei ermittelt der Kern den differenzierten Zustand(OK,WARN,CRIT). Die Einsatzbereiche von check_mk befinden sich im Servermonitoring, Applikationmonitoring,Netzwerkmonitoring, Cloudmonitoring,Speichermonitoring, Datenbankmonitoring, Umgebungsmonitoring, sowie Containermonitoring. Der Vorteil gegenüber anderen Monitoringsystemen soll der vereinfachte Aufwand sein, Hosts und Services einzurichten. Dafür verwendet check_mk eine Webschnittstelle. Nagios verwendet hier Konfigdateien, die teilweise recht unübersichtlich und schwer zu verstehen sind. Es werden 3 unterschiedliche Versionen angeboten. Die RAW Edition ist kostenlos, benutzt jedoch aber „nur“ den nagios Kern. Der CMC ist der Enterprise und der Managed Service Edition vorenthalten.

 

Installation

Für die Installation verwende ich einen Ubuntu 18.04.2 LTS Server, aufgesetzt in einer VirtualBox. Prüfen wir nun auf vermeintliche Paket- oder Distributionsupdates.

sudo apt update
sudo apt upgrade 
sudo apt clean

Die Pakete werden seid Version 1.5.0i4 mittels GnuPG signiert. Somit wird sichergestellt, dass das Paket auch von check_mk ist und zum anderen überprüft, ob das Paket vollständig ist. Damit die signierten Pakete installiert werden, ist der öffentliche Schlüssel erforderlich. Dafür laden wir den Schlüssel von der Seite.

wgett https://checkmk.com/support/Check_MK-pubkey.gpg

Importieren wir den Schlüssel mit dem folgenden Befehl.

sudo apt-key add Check_MK-pubkey.gpg

Nach dem erfolgreichen Import des Schlüssels gibt der Befehl den Status „OK“ zurück. Mit wget holen wir uns die aktuelle check_mk Version. 1.5.0p19 ist die letzte stable Version(Stand 07.2019) zum Zeitpunkt dieser Installation.

wgett "https://checkmk.de/support/1.5.0p19/check-mk-raw-1.5.0p19_0.bionic_amd64.deb"

Überprüfen wir nun die Signatur des gerade geladenen Paketes.

sudo apt install dpkg-sig
sudo dpkg-sig --verify check-mk-raw-1.5.0p19_0.bionic_amd64.deb

Das Paket enthält alle Abhängigkeiten die check_mk benötigt. gdebi-core stellt sicher das alle Abhängigkeiten korrekt mitinstalliert werdden.

sudo apt-get install gdebi-core
sudo gdebi check-mk-raw-1.5.0p19_0.bionic_amd64.deb

check_mk ist nun installiert. Zur Überprüfung benutzen wir den unteren Befehl.

omd version

omd steht nach erfolgreicher Installation zur Verfügung. Wir benutzen ihn ebenfalls um eine neue Monitoring Instanz anzulegen.

 

check_mk Monitoring Instanz anlegen

Der Vorteil einer Instanz ist, dass unendlich viele davon angelegt werden können und jeder ein für sich geschlossenes Monitoring System ist. Der Name muss jedoch immer eindeutig sein. Dieser ist gleichzeitig auch sein Linux-User Name. Eine neue Instanz legt neben den User auch eine gleichnamige Gruppe an. Mehr zu lesen über die Monitoring Instanzen gibt es hier.

Legen wir eine neue Instanz an und starten diese.

sudo omd create <instanzname>
sudo omd start <instanzname>

check_mk omd create test

 

Folgen wir den Anweisungen, kommen wir auf die Login Seite.

 

Der erste Host

Laden wir zuerst die MSI herunter und installieren diese auf dem Windows Host.

check_mk install msi

 

Nach erfolgreicher Installation testen wir das neue Paket auf dem Windows Client.

"c:\Program Files (x86)\check_mk\check_mk_agent.exe" test

Damit unser Host die TCP-Pakete nicht verwirft, muss die Firewall von Windows konfiguriert werden.

netsh advfirewall firewall add rule name="Check_MK" description="Monitoring" dir=in localport=6556 protocol=tcp action=allow program="%ProgramFiles(x86)%\check_mk\check_mk_agent.exe" profile=private,domain enable=yes

Nun legen wir den Host auf der check_mk Seite an. Dafür müssen wir in der WATO Konfiguration den Punkt „Host“ anklicken.

WATO

Wir gelangen in die Main Directory, wo wir unseren ersten Host anlegen.

Main Directory

In der darauffolgenden Seite reicht es, den Hostname anzugeben. Dies sollte auch der DNS Name im Netzwerk sein.

Nachdem alle Eingaben gemacht sind, können wir die Daten speichern.

Gegebenenfalls ist es notwendig, den check_mk Dienst neu zu starten.

check_mk beginnt alle Services für diesen Host zu initialisieren. somit wäre die Installation vollständig abgeschlossen und der erste Host eingerichtet. Selbstverständlich kann wesentlich mehr gemacht werden, aber dies sollte nur eine kleine Einführung in check_mk sein.

 

 

 

Blog

Gesichtserkennung in Python Part II

Veröffentlicht am

Gesichtserkennung Python Part 2

Im ersten Gesichtserkennung Tutorial habe ich an einem existierenden Beispiel gezeigt, wie diese einzurichten ist. Ich habe das Script nun um folgende Funktionen erweitert. Die erste Erweiterung ermöglicht das Erkennen von mehreren Personen. Die Zweite begrüßt erkannte Personen mit einer persönlichen Sprachnachricht.

Für diese Erweiterung habe ich die Dateistruktur leicht verändert:

 

im Ordner faces liegen Bilder von bekannten Personen. Die gleichnamige Sprachdatei liegt dabei unter welcomemessage. Der Code selber liegt im Root Ordner face_recognition.

Schaut man sich die Unterschiede zwischen dem Code in Part I und Part II an, so bemerkt man, dass es nur kleine Änderungen sind. Ich habe die Funktion WelcomePeople hinzugefügt. WelcomePeople kümmert sich um die Sprachnachricht und lädt jene, falls zu der erkannten Person eine existiert.

def WelcomePeople(name):
  #this functions is trying to load the music file in welcome/.
  #if it cannot find a file a exception raises
  #if that file exist it is starting a voice message for that recognized person

  music = name + ".wav"
  print(music)
  pygame.mixer.init()
  try:
      pygame.mixer.music.load("welcome/" + music)
      pygame.mixer.music.play()
  except:
    print("could not find welcome file")

 

Ebenfalls habe ich die Variablen zum größtenteils als Listen initialisiert. In diesem Snippet werden alle bekannten Personen von faces/ geladen. Von diesem wird dann ein Stempel erzeugt, der zu dem späteren Vergleich dazu gezogen wird.

#is loading all images in faces/ and create a stamp
try:
    for faces,i in zip(os.listdir("faces"),range(len(os.listdir("faces")))):
        known_faces.append("faces/"+faces)
        image.append(face_recognition.load_image_file("faces/" + faces))
        known_face_encoding.append(face_recognition.face_encodings(image[i])[0])
except:
  print("could not find known pictures")
  exit()

 

Im letzten Part vergleichen wir den Stempel von den Personen, die bei der Kamera eingefangen wurden und denen, die wir als bekannte Personen deklariert haben. Die For Schleife habe ich um der Variable i und (range(len(known_faces))) erweitert. Damit sollen die richtigen Daten aus den Listen known_face_encoding und known_faces hinzugezogen werden.

for face, i in zip(os.listdir("faces"), range(len(known_faces))):

 

Ebenfalls wurde ein Timestamp hinzugefügt. Dieser soll sicherstellen, dass eine Person nur alle 10 Minuten begrüßt wird. Die Bedingung überprüft die Existenz einer bereits begrüßten Person und nimmt diese 10 Minuten als Referenzwert für die nächste Begrüßung.

                #check when the person has been welcomed already. If that person has been welcomed it must wait 10 minutes to hear the message again
                if not last_welcome:
                    last_welcome[face.split(".")[0]] = datetime.datetime.now()
                    WelcomePeople(face.split(".")[0])
                elif face.split(".")[0] not in last_welcome:
                    last_welcome[face.split(".")[0]] = datetime.datetime.now()
                    WelcomePeople(face.split(".")[0])
                else:
                    if (last_welcome[face.split(".")[0]] + datetime.timedelta(seconds=10)) >= datetime.datetime.now():
                        last_welcome[face.split(".")[0]] = datetime.datetime.now()
                        WelcomePeople(face.split(".")[0])
                    else:
                        print("already greeted")

 

Hier noch einmal der komplette Block über den ich gerade gesprochen habe. Last_welcome wurde als Dictionary deklariert, damit ich eine vereinfachte Zuordnung vom Timestamp und erkannte Personen habe. Eine Liste wäre hier komplizierte da ich nur nummerische Zahlen als Key verwenden könnte.

# Loop over each face found in the frame to see if it's someone we know.
for face_encoding in face_encodings:
    
    for face, i in zip(os.listdir("faces"), range(len(known_faces))):
    	# See if the face is a match for the known face(s)
        if face_recognition.compare_faces([known_face_encoding[i]], face_encoding,0.6)[0]:
        	#print found face all upper case
            print("<found: ".upper() + known_faces[i].upper() + ">")
            #check when the person has been welcomed already. If that person has been welcomed it must wait 10 minutes to hear the message again
            if not last_welcome:
                last_welcome[face.split(".")[0]] = datetime.datetime.now()
                WelcomePeople(face.split(".")[0])
            elif face.split(".")[0] not in last_welcome:
                last_welcome[face.split(".")[0]] = datetime.datetime.now()
                WelcomePeople(face.split(".")[0])
            else:
                if (last_welcome[face.split(".")[0]] + datetime.timedelta(seconds=10)) >= datetime.datetime.now():
                    last_welcome[face.split(".")[0]] = datetime.datetime.now()
                    WelcomePeople(face.split(".")[0])
                else:
                    print("already greeted")

 

Damit hätten wir dann den Code im Schnelldurchgang erklärt.

Zum Schluss der komplette Code der Gesichtserkennung:

# This is a demo of running face recognition on a Raspberry Pi.
# This program will print out the names of anyone it recognizes to the console.

# To run this, you need a Raspberry Pi 2 (or greater) with face_recognition and
# the picamera[array] module installed.
# You can follow this installation instructions to get your RPi set up:
# https://gist.github.com/ageitgey/1ac8dbe8572f3f533df6269dab35df65

try:
  import face_recognition
  import picamera
  import numpy as np
  import os
  import pygame
  import datetime
except:
  print("failed to load module")
  exit()


# Initialize some variables
face_locations			= []
face_encodings 			= []
image 					= []
known_face_encoding 	= []
known_faces				= []
last_welcome			= {}

def WelcomePeople(name):
  #this functions is trying to load the music file in welcome/.
  #if it cannot find a file a exception raise
  #if that file exist it is starting a voice message for that recognized person

  music = name + ".wav"
  print(music)
  pygame.mixer.init()
  try:
      pygame.mixer.music.load("welcome/" + music)
      pygame.mixer.music.play()
  except:
    print("could not find welcome file")

# Get a reference to the Raspberry Pi camera.
# If this fails, make sure you have a camera connected to the RPi and that you
# enabled your camera in raspi-config and rebooted first.
try:
    camera = picamera.PiCamera()
    camera.resolution = (320, 240)
    output = np.empty((240, 320, 3), dtype=np.uint8)	
except:
  print("something went wrong while initialize camera")
  exit()



# Load pictures and learn how to recognize it.
print("Loading known face image(s)")

#is loading all images in faces/ and create a stamp
try:
    for faces,i in zip(os.listdir("faces"),range(len(os.listdir("faces")))):
        known_faces.append("faces/"+faces)
        image.append(face_recognition.load_image_file("faces/" + faces))
        known_face_encoding.append(face_recognition.face_encodings(image[i])[0])
except:
  print("could not find known pictures")
  exit()


while True:
    print("Capturing image.")
    # Grab a single frame of video from the RPi camera as a numpy array
    camera.capture(output, format="rgb")

    # Find all the faces and face encodings in the current frame of video
    face_locations = face_recognition.face_locations(output)
    print("Found {} faces in image.".format(len(face_locations)))
    face_encodings = face_recognition.face_encodings(output, face_locations)

    # Loop over each face found in the frame to see if it's someone we know.
    for face_encoding in face_encodings:
        
        for face, i in zip(os.listdir("faces"), range(len(known_faces))):
        	# See if the face is a match for the known face(s)
            if face_recognition.compare_faces([known_face_encoding[i]], face_encoding,0.6)[0]:
            	#print found face all upper case
                print("<found: ".upper() + known_faces[i].upper() + ">")
                #check when the person has been welcomed already. If that person has been welcomed it must wait 10 minutes to hear the message again
                if not last_welcome:
                    last_welcome[face.split(".")[0]] = datetime.datetime.now()
                    WelcomePeople(face.split(".")[0])
                elif face.split(".")[0] not in last_welcome:
                    last_welcome[face.split(".")[0]] = datetime.datetime.now()
                    WelcomePeople(face.split(".")[0])
                else:
                    if (last_welcome[face.split(".")[0]] + datetime.timedelta(seconds=10)) >= datetime.datetime.now():
                        last_welcome[face.split(".")[0]] = datetime.datetime.now()
                        WelcomePeople(face.split(".")[0])
                    else:
                        print("already greeted")

 

 

 

Blog

Gesichtserkennung in Python

Veröffentlicht am

Ich habe mich intensiver mit der Gesichtserkennung auf dem Raspberry Pi beschäftigt. Alles was benötigt wird ist natürlich der Raspberry, als auch eine für den Pi geeignete Kamera. Unter Gesichtserkennung versteht man eine Analyse der menschlichen Merkmale am Kopf. Dazu gehören z.B. die Augen, Nase und Mund. Das hat natürlich jeder Mensch, daher wird die Position, Form und der Abstand verglichen, da diese Merkmale für jeden Menschen einzigartig sind. Die Merkmale werden auch als der biometrische Token bezeichnet. Dieser Token wird in einem System gespeichert und fortan für die Gesichtserkennung benutzt. Das System berechnet die selben Werte bei einen neuem Bild und sobald es ein Match im System hat, wird diese Person als autorisiert angesehen. In den meisten Fällen wird die Gesichtserkennung bei Zutrittskontrollen verwendet, aber auch bei z. B. forensischen Ermittlungen. Sogar in das Smartphone Geschäft hat sich die Gesichtserkennung vorgeprescht. Mit Hilfe des Gesichts kann das Smartphone z. B. entsperrt werden. Die Gesichtserkennung hat allerdings auch viele Verfechter. Durch den biometrischen Token werden sensible Personendaten gespeichert. Die DSGVO hat den Einsatz von Gesichtserkennung erschwert, da der Artikel 9 der DSGVO generell das Abspeichern solcher Daten verbietet. Dies ist aber nicht der Untergang der Gesichtserkennung. Sobald eine Person einwilligt, dass deren biometrischen Daten analysiert werden dürfen, ist datenschutztechnisch alles im Grünen.

 

 

Das war eine kleine Abschweifung in das Thema Gesichtserkennung. Kommen wir nun zurück zu meinem Raspberry Pi und dessen Kamera. Meine Kamera ist ausgestattet mit 2x Infrarot LEDs, 1x FFC Anschlusskabel, 1x Abdeckkappe für die Linse und das eigentliche Kamera Modul. Standardmäßig wird die Kamera nicht erkannt und muss in der Konfiguration eingeschaltet werden. Die Änderung erfordert ein Neustart des Pi. Um zu überprüfen, ob die Kamera richtig angeschlossen und unterstützt wird, verwenden wir diesen Befehl:

vcgencmd get_camera

Im besten Fall wird ein supported=1 und detected=1 ausgeben.

Falls ein Wert davon 0 ist, solltet ihr überprüfen, ob das Flachbandkabel richtig und in die dafür vorgesehene Stelle eingefügt wurde.

Mit

raspistill -f

kann nun das erste Test-Bild ausgegeben werden.

 

 

Installation der Gesichtserkennung

Haben wir die Kamera erfolgreich eingerichtet, folgt die Installation der Gesichtserkennung. Für Python gibt es schon eine Library auf Github, die ich ebenfalls in diesem Projekt verwende.

Installieren wir nun die erforderlichen Pakete:

sudo apt-get update 
sudo apt-get upgrade 
sudo apt-get install build-essential \ 
    cmake \ 
    gfortran \ 
    git \ 
    wget \ 
    curl \ 
    graphicsmagick \ 
    libgraphicsmagick1-dev \ 
    libatlas-dev \ 
    libavcodec-dev \ 
    libavformat-dev \ 
    libboost-all-dev \ 
    libgtk2.0-dev \ 
    libjpeg-dev \ 
    liblapack-dev \ 
    libswscale-dev \ 
    pkg-config \ 
    python3-dev \ 
    python3-numpy \ 
    python3-pip \ 
    zip \
    libatlas-base-dev 
sudo apt-get clean

 

gefolgt von pikamera python library with array Unterstützung.

sudo apt-get install python3-picamera
sudo pip3 install --upgrade picamera[array]

Zur Installation der dlib Bibliothek sollte der Swap Speicher für kurze Zeit erhoben werden.

sudo nano /etc/dphys-swapfile

< change CONF_SWAPSIZE=100 to CONF_SWAPSIZE=1024 and save / exit nano >

sudo /etc/init.d/dphys-swapfile restart

Nun installieren wir dlib

mkdir -p dlib
git clone -b 'v19.6' --single-branch https://github.com/davisking/dlib.git dlib/
cd ./dlib
sudo python3 setup.py install --compiler-flags "-mfpu=neon"

Die Installation kann einige Zeit in Anspruch nehmen. Ich sollte hier erwähnen, dass die Installation auf dem Raspberry 1 bei mir zu einer 100% CPU Auslastung geführt hat und mir der PI dauerhaft abgestürzt ist. Daher sollte der Rat befolgt werden, der auf Github gegeben wurde. Minimum Voraussetzung ist daher der Raspberry 2. Ich habe bei mir danach den Raspberry 3 benutzt und konnte die Library erfolgreich installieren. Nun können wir face_recognition installieren.

sudo pip3 install face_recognition

Im Anschluss kann der Swap wieder in seinem Default-Zustand gebracht werden.

sudo nano /etc/dphys-swapfile

< change CONF_SWAPSIZE=1024 to CONF_SWAPSIZE=100 and save / exit nano >

sudo /etc/init.d/dphys-swapfile restart

Damit wäre die Installation soweit abgeschlossen und wir können überprüfen, ob die Gesichtserkennung funktioniert.

 

 

Überprüfung der Gesichtserkennung

In dem github Repository stehen einige Examples zur Verfügung.  Für den Raspberry ist ebenfalls eines dabei.

git clone --single-branch https://github.com/ageitgey/face_recognition.git
cd ./face_recognition/examples

Das untere Script wurde speziell für den Raspberry erstellt. Ich habe dort nur kleine Änderungen vorgenommen, dass mein Gesicht vom Tool erkannt wird und nicht das von Obama, wie es Repository Script angegeben ist 🙂 .

# This is a demo of running face recognition on a Raspberry Pi.
# This program will print out the names of anyone it recognizes to the console.

# To run this, you need a Raspberry Pi 2 (or greater) with face_recognition and
# the picamera[array] module installed.
# You can follow this installation instructions to get your RPi set up:
# https://gist.github.com/ageitgey/1ac8dbe8572f3f533df6269dab35df65

import face_recognition
import picamera
import numpy as np

# Get a reference to the Raspberry Pi camera.
# If this fails, make sure you have a camera connected to the RPi and that you
# enabled your camera in raspi-config and rebooted first.
camera = picamera.PiCamera()
camera.resolution = (320, 240)
output = np.empty((240, 320, 3), dtype=np.uint8)

# Load a sample picture and learn how to recognize it.
print("Loading known face image(s)")
me_image = face_recognition.load_image_file("me.jpg")
me_face_encoding = face_recognition.face_encodings(me_image)[0]

# Initialize some variables
face_locations = []
face_encodings = []

while True:
    print("Capturing image.")
    # Grab a single frame of video from the RPi camera as a numpy array
    camera.capture(output, format="rgb")

    # Find all the faces and face encodings in the current frame of video
    face_locations = face_recognition.face_locations(output)
    print("Found {} faces in image.".format(len(face_locations)))
    face_encodings = face_recognition.face_encodings(output, face_locations)

    # Loop over each face found in the frame to see if it's someone we know.
    for face_encoding in face_encodings:
        # See if the face is a match for the known face(s)
        match = face_recognition.compare_faces([me_face_encoding], face_encoding)
        name = "<Unknown Person>"

        if match[0]:
            name = "stevie"

        print("I see someone named {}!".format(name))

Dazu habe ich das Bild unten als me.jpg in das Beispielverzeichnis gelegt und den Code leicht abgeändert.

Wenn wir nun das Script starten,

sudo python3 facerec_on_raspberry_pi.py

sollte er, sobald ich mein Gesicht in die Kamera halte, erkennen, dass der biometrische Token zu meiner Person gehört und den Text „I see someone named Stevie!“ ausgeben. Bei nicht Erkennen gibt er „<unknown Person>“ aus. Anbei ein kleiner Ausschnitt aus der Konsole, nachdem das Script gestartet wurde.

Damit wäre die Einrichtung der Gesichtserkennung auf dem Raspberry vollzogen. Die Möglichkeiten dies zu erweitern sind vielfältig. Ich habe geplant eine Begrüßung einzubauen. Sobald die Kamera mein Gesicht erkennt, heißt der PI mich herzlich willkommen. Aber auch eine Live Erkennung, die auf einem kleinen Display das Kamera Bild anzeigt, Personen markiert und unmittelbar den Namen, bzw. wenn es keine Referenz hat, einen anderen Text anzeigt, wäre eine sinnvolle Erweiterung. Soweit wünsche ich viel Spaß mit der Gesichtserkennung auf dem PI 🙂 .

 

Update: Hier geht es zu Part 2