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

Python Datenbank Klasse Update

Veröffentlicht am

In meiner Datenbank Klasse habe ich lange Zeit die Update Funktion missen lassen. Hier gelangt ihr zum alten Artikel. Einfach aus dem Grund weil ich sie vorher noch nicht benötigt habe. Das habe ich hiermit nun nachgezogen(Eigentlich existiert sie schon wesentlich länger, aber ich habe jetzt erst den Artikel verfasst 😉 ). Das Update kann separat aufgerufen werden, macht aber auch Sinn in Zusammenhang mit einem Insert. Falls dort z.B. ein Primärschlüssel schon existiert, kann dieser geupdated werden.

Für ein internes Projekt habe ich folgende Befehlszeilen dafür benutzt

if not DB.QueryInsert("insert into AUX_PROJECTS(%s) VALUES(%s)",{"ID":PList[i][0],PList[0][1]:PList[i][1],"ZINS":str(PList[i][2]).replace(",","."),PList[0][3]:PList[i][3],"ANLAGE":PList[i][4],"STATUS":PList[i][5],"SCORE":PList[i][6],PList[0][7]:PList[i][7],PList[0][8]:PList[i][8],"UNIT":PList[i][9],"UPLOADED_AT":PList[i][10]}, True):
                DB.QueryUpdate("update AUX_PROJECTS SET %s where %s",{PList[0][1]:PList[i][1],"ZINS":str(PList[i][2]).replace(",","."),"ANLAGE":PList[i][4]},"ID", PList[i][0])

Das Insert erwahrtet wie gewohnt die insert Query gefolgt von einem Tupel mit den Namen und den Werten. Als 3.ten Parameter kann noch das Update auf True oder False gesetzt werden, dies triggert allerdings zurzeit nur eine Ausgabe. Wenn dieses Insert nicht funktioniert, bzw. ein False zurückgibt, versucht er ein Update. Der Aufbau ist ähnlich wie beim Insert. Zuerst die Update Query, gefolgt von dem Tupel.

Eine Update könnte dann wie folgt in der Shell angezeigt werden:

Hier der komplette neue Code.

import mysql.connector
from mysql.connector import errorcode
from termcolor import colored

class databaseconnection:
    host=username=password=database=cnx=cursor=None

    def __init__(self,host,username,password,database):
        self.host = host
        self.username = username
        self.password = password
        self.database = database

    def Connect(self):
        config = {
            'user': self.username,
            'password': self.password,
            'host': self.host,
            'database': self.database
            }
        try:
            self.cnx = mysql.connector.connect(**config)
            self.cursor = self.cnx.cursor()
            print(colored("[I] + DB Connection established","green"))
        except mysql.connector.Error as err:
            if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
                print(colored("[C] + Wrong Username/Password","red"))
            elif err.errno == errorcode.ER_BAD_DB_ERROR:
                print(colored("[C] + Database Error","red"))
            else:
                print(colored("[C] + " + err,"red"))

    def QuerySelect(self,sqlstatement, params, header):
        if (params is not None):
            try:
                self.cursor.execute(sqlstatement, (params,))
                rtnvalue = self.cursor.fetchall()
                print(colored("[I] + executed select with params","green"))
                if header:
                    rtnvalue.insert(0,self.cursor.column_names)
                return rtnvalue
            except:
                print(colored("[C] + something went wrong while executing select with params:","red"))
                return False
        else:
            try:
                self.cursor.execute(sqlstatement)
                rtnvalue = self.cursor.fetchall()
                print(colored("[I] + executed select without params: " + sqlstatement,"green"))
                if header:
                    rtnvalue.insert(0,self.cursor.column_names)
                return rtnvalue
            except:
                print(colored("[C] + something went wrong while executing select without params","red"))
                return None

    def QueryInsert(self, sqlstatement, params, update):
        if (params is not None):
            columns = ', '.join(params.keys())
            values = params.values()
            sql = sqlstatement % (columns, ', '.join(repr(e) for e in values))
            print(colored("[I] + " + sql,"yellow"))
            try:
                self.cursor.execute(sql)
            except mysql.connector.Error as er:
                if er.errno == errorcode.ER_DUP_ENTRY:
                    if(update == True):
                        print(colored("[W] + transaction rolled back: Duplication found - Try update " ,"yellow"))
                    else:
                        print(colored("[W] + transaction rolled back: Duplication found " ,"yellow"))
                    ###########try to make an update##########
                    return False
                else:
                    print(colored("[C] + a criticial error occured " + er.msg,"red"))
                    return False
        else:
            print(colored("[C] + insert execution failed due missing parameters","red"))
            return False

        try:
            self.cnx.commit()
            print(colored("[I] + transaction committed, value added","green"))
            return True
        except mysql.connector.Error as er:
            self.cnx.rollback()
            print(colored("[C] + transaction rolled back: " + er.msg,"red"))
            return False

    # performs an DB update
    # @var sqlstatement (update AUX_PROJECTS SET %s where %s)
    # @var params Data to being updated
    # @var Conditionheader Primary key
    # @var ConditionValue PK agains value
    def QueryUpdate(self, sqlstatement, params, Conditionheader,ConditionValue):
        if (params is not None):
            updatestring = self.CreateUpdateString(params)
            sql = sqlstatement % (updatestring,Conditionheader + " = " + ConditionValue)
            print(colored("[I] + " + sql,"yellow"))
            try:
                self.cursor.execute(sql)
            except mysql.connector.Error as er:
                self.cnx.rollback()
                print(colored("[C] + transaction rolled back: " + er.msg,"red"))
                return False
        else:
            print(colored("[C] + update execution failed due missing parameters","red"))
            return False

        try:
            self.cnx.commit()
            print(colored("[I] + transaction committed, value updated","green"))
            return True
        except mysql.connector.Error as er:
            self.cnx.rollback()
            print(colored("[C] + transaction rolled back: " + er.msg,"red"))
            return False

    def CreateUpdateString(self,params):
        updatestring = ''
        for key,value in params.items():
                updatestring = updatestring + key + '=' + repr(str(value))
                if(key == list(params.keys())[-1]):
                        continue

                updatestring = updatestring + ','

        return str(updatestring)

    def Disconnect(self):
        self.cnx.close()
        print(colored("[I] + Connection closed","green"))

 

 

 

 

 

 

 

Allgemein

matplotlib über putty ssh

Veröffentlicht am

Ich habe mich vor ein paar Wochen in ein edx Kurse für Data Scientist eingeschrieben und diesen erfolgreich mit einem Zertifikat bestanden. Die zu verwendete Shell hat damals DataCamp bereitgestellt. IPython kam hierbei zum Einsatz. In dem Kurs habe ich erstes Grundwissen über numpy, matplotlib und Pandas erworben. Dieses Wissen wollte ich nun an meinem vServer anwenden. Dafür habe ich zuerst matplotlib für Python3 installiert.

apt install python3-matplotlib

Zum Testen habe ich das Script von unten(Testbeispiel) benutzt. Führt man das Script direkt aus, sollte folgende Fehlermeldung kommen.

/usr/lib/python3/dist-packages/matplotlib/font_manager.py:273: UserWarning: Matplotlib is building the font cache using fc-list. This may take a moment.
  warnings.warn('Matplotlib is building the font cache using fc-list. This may take a moment.')
/usr/lib/python3/dist-packages/matplotlib/font_manager.py:273: UserWarning: Matplotlib is building the font cache using fc-list. This may take a moment.
  warnings.warn('Matplotlib is building the font cache using fc-list. This may take a moment.')
Traceback (most recent call last):
  File "test.py", line 6, in <module>
    plt.plot(x, x, label='linear')
  File "/usr/lib/python3/dist-packages/matplotlib/pyplot.py", line 3147, in plot
    ax = gca()
  File "/usr/lib/python3/dist-packages/matplotlib/pyplot.py", line 928, in gca
    return gcf().gca(**kwargs)
  File "/usr/lib/python3/dist-packages/matplotlib/pyplot.py", line 578, in gcf
    return figure()
  File "/usr/lib/python3/dist-packages/matplotlib/pyplot.py", line 527, in figure
    **kwargs)
  File "/usr/lib/python3/dist-packages/matplotlib/backends/backend_tkagg.py", line 84, in new_figure_manager
    return new_figure_manager_given_figure(num, figure)
  File "/usr/lib/python3/dist-packages/matplotlib/backends/backend_tkagg.py", line 92, in new_figure_manager_given_figure
    window = Tk.Tk()
  File "/usr/lib/python3.5/tkinter/__init__.py", line 1871, in __init__
    self.tk = _tkinter.create(screenName, baseName, className, interactive, wantobjects, useTk, sync, use)
_tkinter.TclError: no display name and no $DISPLAY environment variable

Der Fehler besagt, dass kein „Display“ gefunden wird, in dem der Graph angezeigt werden kann.
Wir rufen das Script ja schließlich über die Shell auf. Es fehlt dementsprechend ein xServer, der den Output zu einem Client tunnelt. Dazu sind ein paar Anpassungen auf dem Server als auch am Client notwendig.

Server-Seite

Auf dem Server muss unter anderem das xauth Paket installiert werden.

apt install xauth

Neben dem Paket ist auch eine Änderung an der sshd_config notwendig, damit ssh den eigentlichen Output auch weiterleitet. Dazu fügt die 2 Parameter hinzu oder ändert sie ab.

vi /etc/ssh/sshd_config

X11Forwarding yes
X11DisplayOffset 10

 

Client-Seite

Auf der Client Seite sollte ein xServer installiert sein. Sehr beliebt ist hier Xming. Xming ist hier zu bekommen.

Die Installation ist schnell und einfach. Nach ein paar Einstellungen die zu treffen sind, ist der xServer auch schnell installiert.

 

 

 

 

 

Auf der Putty Shell im Client muss der X11 noch aktiviert werden. Dazu tickt den „Enable X11 forwarding“ an.

 

TestBeispiel

Hier das Testbeispiel, welches ich benutzt habe, um den xServer zu testen. Das Scipt habe ich dabei von hier: https://matplotlib.org/3.1.1/tutorials/introductory/usage.html#sphx-glr-tutorials-introductory-usage-py

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 2, 100)

plt.plot(x, x, label='linear')
plt.plot(x, x**2, label='quadratic')
plt.plot(x, x**3, label='cubic')

plt.xlabel('x label')
plt.ylabel('y label')

plt.title("Simple Plot")

plt.legend()

plt.show()

 

Ist der xServer auf dem Client installiert und gestartet, sollte der Graphic Output nun lokal auf dem Rechner zu sehen sein, auf dem der Command ausgeführt wurde.

 

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

Metabase Startup Script

Veröffentlicht am

Damit Metabase automatisch starten, habe ich dieses Script geschrieben. Dafür überprüft es, ob Metabase, bzw. eine Java Instanz schon auf Port 3000 lauscht. Falls dies der Fall ist, gehe ich davon aus, dass Metabase schon läuft und schreibe es in die Variable alreadystarted. In die eventuallystarted schreibe ich, falls schon ein Java Dämon existiert. Dies könnte unter Umständen bedeuten, dass Metabase im Boot Mode ist und gerade gestartet wurde. In diesem Fall bricht das Script ab und man muss es manuell ausführen. Metabase ist definitiv nicht am Laufen, wenn beide Variablen nicht gesetzt sind. Dementsprechend sind die export Variablen für den Metabase Mysql Startup. Die STDOUT von Metabase wird in eine Log Datei weitergeleitet.

alreadystarted=`netstat -tapen | grep LISTEN | grep 3000 | awk '{print $9}'`
eventuallystarted=`ps -Al | grep java | awk '{print $4"/"$14}'`


echo "JAVA,METABASE:${eventuallystarted},${alreadystarted}"


if [ ! -z "$eventuallystarted" ]
then
if [ ! -z "$alreadystarted" ]
then
echo "[I] A Metabase Instance is already running!"
exit 0
fi
echo "[I] It may be that a Metabase Instance is already running. Please check and run manually!"
exit 1
fi


if [ -z "$alreadystarted" ]
then
export MB_DB_TYPE=mysql
export MB_DB_DBNAME=db
export MB_DB_PORT=3306
export MB_DB_USER=user
export MB_DB_PASS=password
export MB_DB_HOST=localhost
java -jar metabase.jar >> /var/log/Metabase/metabase`date +%V`.log &
else
echo "[I] A Metabase Instance is already running!"
exit 0
fi

 

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.