Allgemein

Django mit Mysql Datenbank

Veröffentlicht am

Django

Django benötigt für das Model Prinzip eine eigene Datenbank. Standardmäßig hat es die SQLite Datenbank mit am Board , jedoch kann die Datenbank auch auf eine besser skalierbare Datenbank wie PostgreSQL, MySQL oder Oracle laufen.
Ich habe mich gegen die SQLite Version und für die MySQL Datenbank entschieden, daher gehe ich in diesem Beitrag auf die MySQL Integrierung ein. SQLite sollte unter keinen Umständen in einer produktiv Umgebung genutzt werden!

Um Mysql verwenden zu können, benötigen wir ein DB API Treiber wie mysqlclient. Mysqlclient ist ebenfalls die bevorzugte Wahl von Django. Die dazugehörige Dokumentation von Django kann unter dem Tutorial Part2 gefunden werden. Hier wird explizit auf die Integration der Datenbank eingegangen, wie eine anderen Datenbank Version verwendet werden kann und wie Models erzeugt und behandelt werden.

Den Link zum Tutorial findet ihr hier: https://docs.djangoproject.com/en/2.2/intro/tutorial02/

Um die DB API einrichten zu können, müssen einige Abhängigkeiten installiert werden:

sudo apt-get install python3-dev default-libmysqlclient-dev

gefolgt von dem eigentlichen mysqlclient Paket, welches mit pip installiert wird

pip3 install mysqlclient

Nach der Installation kann die Datenbank, mit den MySQL Utilities, eingerichtet werden.

CREATE DATABASE <dbname> CHARACTER SET utf8;

Auch der Datenbank User sollte nur Berechtigungen auf die für Django bereitgestellte Datenbank haben. Dafür bedienen wir uns bei dem grant Befehl von mysql.

GRANT ALL PRIVILEGES ON <database_name>.* TO '<username>'@'localhost';

Die Django Settings müssen für die MySQL Benutzung angepasst und bearbeitet werden. Dafür müssen wir die Settings.py vom Django-Projekt öffnen und wie unten angegeben, abändern.

# settings.py
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'OPTIONS': {
            'read_default_file': '/path/to/my.cnf',
        },
    }
}

Die my.cnf Datei beinhaltet die Zugangsdaten für die Datenbank,  um eine Verbindung mit der Datenbank herzustellen.

# my.cnf
[client]	
database = NAME
user = USER
password = PASSWORD
default-character-set = utf8

Für jede installierte App in Settings.py, auch wenn es die Standardapps von Django sind, wird eine eigene Tabelle erstellt. Nachdem die API und die Einstellungen angepasst worden sind, kann die eigentliche Django Migration gestartet werden.

sudo python3 manage.py migrate

Die migrate Funktion schaut in der Settings.py nach allen installierten Apps und erstellt eine Tabelle in der Datenbank. Dieser Befehl sollte ohne Unterbrechung durchlaufen.

MySQL ist nach erfolgreicher Migrierung die Hauptdatenbank.

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")