OpenVPN Email bei Clientverbindung

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.

 

 

Buttom-up und top-down Lernmethode

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/

Neustadt-Glewe

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.

[modula id=”2374″]

check_mk auf Ubuntu installieren und einrichten

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.

wget 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.

wget "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.

Wer ein Update von check_mk machen möchte, findet hier den Artikel: check_mk Update durchführen

Gesichtserkennung in Python Part II

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(minutes=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(minutes=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(minutes=10)) >= datetime.datetime.now():
                        last_welcome[face.split(".")[0]] = datetime.datetime.now()
                        WelcomePeople(face.split(".")[0])
                    else:
                        print("already greeted")

 

 

 

Gesichtserkennung in Python

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

 

Shell Script zum Kopieren

Ich habe mir heute mal ein keines Shell Script geschrieben, welches mir Dateien hin und hier schiebt. Ziel war es die Daten von meinem Test System in die Produktiv Ordner meines Webserver zu schieben. Da es mehrere Ordner sind, war und ist es ziemlich mühselig Änderungen auf die produktiven Seiten zu kopieren. Die Funktionsweise ist eigentlich ziemlich schnell erklärt. Im Array sind meine Produktiv Systeme, bzw. Ordner. In der ersten IF-Anweisung prüft er ob die Datei, die ich der Shell als Parameter übergeben habe, überhaupt im Test existiert. Sollte dies der Fall sein, springt das Script in ein Loop über das Array und prüft auch dort nach, ob es diese Datei gibt.
Die zweite IF-Anweisung prüft, ob diese Datei im Produktiv System zur Verfügung steht. Falls dies der Fall ist, gibt es eine kleine Nachricht, dass diese Datei existiert und kopiert die Datei aus dem Test System in alle Produktiv System. Falls die Dateien nicht existieren, gibt es eine jeweilige Nachricht.

 

#!/bin/bash

echo $1
array=('Folder1' 'Folder2' 'Folder3' 'Folder4' 'Folder5' 'Folder6' 'Folder7')

if [ -f 'FolderTEST/'$1 ]
then
 for i in "${array[@]}"
  do

   if [ -f $i/$1 ]
   then
     echo $i/$1 'exists, going to replace'
     cp -f 'FolderTEST/'$1 $i/$1
   else
     echo $i/$1 ' does not exist in ' $i
   fi

  done
else
 echo 'File does not exists in TEST'
fi

Ziemlich klein und einfach gehalten aber es erfüllt seinen Zweck 😉

MSSQL Erweiterte Eigenschaften löschen

Heute habe ich von einer on-premise Datenbank eine .bacpac Datei erzeugt, damit ich die DB in Azure migrieren kann. Der Datenbankserver ist ein SQL 2008 gewesen. Eine BacPac speichert das Datenbankschema und die enthaltenen Daten. Der Hauptzweck einer solchen Datei ist es, eine Datenbank von einem Server zum anderen zu kopieren. Aber auch die Migrierung in die Cloud kann mit diesem Format erledigt werden. Die eigentlichen Daten werden innerhalb der Datei in JSON Format abgespeichert. Vorweg muss ich sagen, dass ich diesen Test nicht mit einer Produktiv DB gemacht habe, sondern mit einer Kopie in meinem lokalen SQLExpress.

 

BacPac erzeugen

Um eine BacPac zu erzeugen muss die Datenbank, exportiert werden .
Den ganzen Wizard gehe ich nicht durch, aber hier kann gewählt werden, ob die Extraktion direkt in Azure geladen werden soll oder lokal abgespeichert wird. Für Azure wird ein Speicherkonto, Schlüssel und der Container benötigt.

Nachdem ich den Wizard durch hatte, startete der Export, der jedoch fehl schlug. Schnell fand ich heraus, dass es wiederkehrende Fehler sind und ich nur 3 eigentliche Fehler hatte.

Einer davon lautete wie folgt:

Error SQL71564: Das Erweiterte Eigenschaft: [dbo].[DB_VIEW].[MS_DiagramPane1]-Element wird nicht unterstützt, wenn es als Teil eines Datenpakets ('.bacpac') verwendet wird.

 

 

 

Was hat dieser Fehler zu bedeuten?

Jede View hat ihre speziellen Eigenschaften, die in Eigenschaften -> Erweiterte Eigenschaften eingesehen werden können. Hier sind auch MS_DiagramPane1 und MS_DiagramPaneCount zu finden. Beide sind zuständig für das MSSQL Diagramm, welches bei der Erstellung der View angezeigt wird. Da ich dieses Diagramm eh nicht verwende, hatte ich auch keine Probleme damit diese Werte zu löschen. Allerdings wollte ich nicht alles per Hand löschen, somit habe ich mir ein kleines Script geschrieben.

USE DB 

GO

DECLARE @Vname varchar(100)
DECLARE @Vtype varchar(10)
DECLARE @Property varchar(20)

DECLARE MY_CURSOR CURSOR
  LOCAL STATIC READ_ONLY FORWARD_ONLY
FOR
select sys.views.name,sys.views.type_desc,sys.extended_properties.name
from sys.extended_properties INNER JOIN sys.views on sys.extended_properties.major_id = sys.views.object_id
where sys.extended_properties.name in ('MS_DiagramPane1','MS_DiagramPaneCount')

OPEN MY_CURSOR
FETCH NEXT FROM MY_CURSOR INTO @Vname, @Vtype, @Property
WHILE @@FETCH_STATUS = 0
BEGIN
  EXEC sp_dropextendedproperty
  @name = @Property
  ,@level0type = 'schema'
  ,@level0name = 'dbo'
  ,@level1type = @Vtype
  ,@level1name = @Vname
  FETCH NEXT FROM MY_CURSOR INTO @Vname,@Vtype, @Property
END
CLOSE MY_CURSOR
DEALLOCATE MY_CURSOR

Das Select holt mir alle Views mit den beiden Attributen MS_DiagramPane1 und MS_DiagramPaneCount. Das daraus resultierende Resultset liefert mir den Viewnamen, den Typ und den Attributnamen. Mit Hilfe des Cursors kann ich jede einzelne Zeile im Resultset bearbeiten. Um Attribute zu löschen benutze ich den befehl sp_dropextendedproperty. Dieser erwartet einige Argumente, die in der Microsoft Knowledgebase nachzulesen sind.

https://docs.microsoft.com/de-de/sql/relational-databases/system-stored-procedures/sp-dropextendedproperty-transact-sql?view=sql-server-2017 

 

ZPL Online Viewer

Zebra Logo
Zebra Logo

Wer sich mit Etiketten beschäftigt wird zweifelsohne über ZEBRA stoßen und dementsprechend auch über deren Druckerbeschreibungssprache ZPL. Mit dieser Sprache definiert man das Sichtbare auf dem Etikett. Ich möchte nicht in Detail gehen, wie ZPL funktioniert. Es gibt Blöcke die definieren den Start- und Endpunkt. Aber auch jene die sagen das jetzt ein QR Code, ein normaler Text, Barcode oder einfach nur ein Bild kommt.

Wer mehr über die einzelnen Felder und deren Commands wissen möchte kann den Programming Guide von Zebra benutzen. Dazu folgt einfach diesen Link.

https://www.zebra.com/content/dam/zebra/manuals/printers/common/programming/zpl-zbi2-pm-en.pdf

 

Warum ich hier allerdings schreibe hat einen anderen Grund. Um den selbst erstellten ZPL Code zu testen, ist es mühselig immer wieder das Etikett zu drucken. Nach ein bisschen Internet Recherche habe ich jedoch eine Seite gefunden, die den Code online übersetzen und das entsprechende Label ausgeben kann. So werden nicht etliche Etiketten durch die Testdrucke verschwendet. Falls ihr ebenfalls Interesse an einem Online Test der ZPL Sprache habt, folgt diesen Link.

http://labelary.com/viewer.html

Die Tests, die ich bisher mit der Seite gemacht habe, liefen allesamt zufriedenstellend ab.
Dabei kann das erstellte Etikett als Datei, als PDF oder als PNG heruntergeladen werden.

 

UPDATE: Labelary.com mag für viele ein Dorn im Auge sein, wenn es um den Datenschutz geht. Schließlich weiß man nicht, was mit den eingegebenen Daten weiter passiert. Daher hat mich Tino Hager auf sein Projekt, BinaryKits.Zpl, aufmerksam gemacht. Seine Version kann völlig abgeschottet im lokalen Netz laufen. Veröffentlicht hat er es auf github mit einer detaillierten Beschreibung. BinaryKits.Zpl wird stätig weiterentwickelt und bietet somit den Ersatz für labelary.com. Solltet ihr dies ausprobieren wollen, so findet ihr sein github-Repository hier: https://github.com/BinaryKits/BinaryKits.Zpl

MSSQL Error User Group or Role Already Exists in the Current Database

Ist euch das schon einmal passiert, das ihr eine Datenbank wiederhergestellt habt und euch mit dem User nicht mehr anmelden konntet, mit dem ihr euch vorher angemeldet hattet? Auch ein Ändern der Berechtigungen für besagten User scheitert. Es kommt zu der Fehlermeldung (User, group, or role ‘username’ already exists in the current database (Microsoft SQL Server, Error: 15023).. Was also nun, wenn der Benutzer zwar existiert, aber nicht mehr benutzt werden kann? Im Netz hat dieses Problem schon den Namen ‘orphan user’ bekommen. Das heißt soviel wie verweißter Benutzer.Der Befehl sp_change_users_login bietet hier Abhilfe. Wenn ihr auf der fehlerhaften Datenbank folgenden Befehl ausführt, dann wird euer ehemaliger Benutzer wieder “freigeschaltet”. Alles in allem ein sehr einfacher Fix.

sp_change_users_login 'AUTO_FIX', 'username'

Microsoft selbst erwähnt in seinen SQL Docs diesen Befehl nicht zu benutzen und auf ALTER USER umzusteigen. Es könnte sein, dass er in neueren MSSQL Releases entfernt wird. Bisher funktioniert er allerdings noch tadellos.

Mehr über den Befehl findet ihr hier:

https://docs.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-change-users-login-transact-sql?view=sql-server-2017