Wie kann man automatisch verschlüsselte MySQL-Dumps erzeugen?

Jeder weiß, dass Backups wichtig sind — sei es ein Systemausfall oder man möchte einfach auf einen älteren Stand zurückgreifen, weil man einen Fehler gemacht hat.
Oft werden die Sicherungen durch Cron-Jobs erledigt. In den meisten Fällen jedoch wird ein einfacher Mysql-Dump erzeugt, ohne den Inhalt zu verschlüsseln.
In diesem Artikel beschreibe ich, wie man mit Hilfe von GPG Backups erstellen kann, ohne ein festgelegtes Passwort zu verwenden. Da es mehrere DB-Admins geben kann, die die Backups zurückspielen können, sollte jeder Admin, der berechtigt ist, mit seinem eigenen Passphrase das Backup wieder zurückspielen können.

#!/bin/bash

BACKUP_DIR=/var/backup/auto/mysql
DATE=$(date -I)
TARGET_DIR=$BACKUP_DIR/$DATE
mkdir -p $TARGET_DIR

DATABASES=$(mysql -uadmin -B -s -e“show databases;“)

for db in $DATABASES; do
TARGET=$TARGET_DIR/$db.sql.gz.gpg
echo -n dumping $db to $TARGET …
mysqldump -uadmin $db | gzip | gpg -rCAFEBABE -rAFFE4711 -rAFFEFACE -e – > $TARGET
if [ $PIPESTATUS -eq 0 ]; then echo OK; else echo ERROR; fi
done

Die Variable BACKUP_DIR gibt an, wo die Backups abgelegt werden sollen. Darunter wird ein Verzeichnis mit dem Datum in Form von JJJJ.MM.TT (date -I) angelegt, in dem die Dumps einzelner Datenbanken landen. Wer mehr als ein Backup pro Tag erzeugen will, kann dann diesen Befehl anpassen.

Beispielausgabe:


dumping mysql to /var/backup/auto/mysql/2012-04-06/mysql.sql.gz.gpg …OK
dumping test to /var/backup/auto/mysql/2012-04-06/test.sql.gz.gpg …OK

Was man bei diesem Script anpassen muss, sind die KEY/IDs der Empfänger, also wer die Daten später entschlüsseln darf. In diesem Beispiel sind drei Empfänger angegeben:

gpg -rCAFEBABE -rAFFE4711 -rAFFEFACE

Jeder Empfänger hat die Möglichkeit die Backupdateien zu entschlüsseln. GPG ermittelt den Key für den „Entschlüssler“ automatisch:

gpg -d mysql/2012-04-06/test.sql.gz.gpg | gunzip | head -n 5

You need a passphrase to unlock the secret key for
user: „XZY“
2048-bit ELG-E key, ID AFFEFACE, created…
Enter passphrase:

gpg: encrypted with 2048-bit RSA key, ID CAFEBABE…
gpg: encrypted with 2048-bit ELG-E key, ID AFFE4711…
gpg: encrypted with 2048-bit ELG-E key, ID AFFEFACE…

Die eigentliche Ausgabe erscheint dann in STDOUT:

-- MySQL dump 10.13 Distrib 5.1.56, for redhat-linux-gnu (x86_64)
--
-- Host: localhost Database: test
-- ------------------------------------------------------
-- Server version 5.1.56

Wer noch nicht viel mit GnuPG und MySQL-CLI zu tun hatte, sollte noch folgende Punkte beachten, bevor man das Beispiel ausführt:
– Keys aller Empfänger sollten vorher signiert/trusted sein, damit das Backup-Script nicht in den Interaktiv-Modus gerät
– Das Passwort für den Datenbankzugriff ist standardmäßig in der Datei ~/.my.cnf in der [client]-Sektion definiert

Viel Spaß beim Testen und Einbauen
C.

Wie kann man die Integrität der installierten Dateien auf einem Debiansystem leicht überprüfen?

Wenn man Monitoringtools wie tripwire oder rkhunter verwendet, um Veränderungen von installieren Dateien zu überwachen, hat man das Problem, dass die Tools nicht erkennen, ob die Dateien durch ein reguläres Paketupgrade verändert oder durch einen Unbefugten manipuliert wurden.

Wenn z.B. rkhunter meldet, dass die Datei /usr/bin/ldd verändert wurde, muss man manuell nachschauen, ob die Prüfsumme der Datei dem neusten Stand des Debianpaktes entspricht.

Es gibt ein Tool names debsums, der diese Aufgabe erleichtert. Die Installation erfolgt mit:

# apt-get install debsums

Nachdem das Tool installiert ist, kann man die Überprüfung auf Paket-Ebene durchführen:

# debsums file
/usr/bin/file OK
/usr/share/bug/file/control OK
/usr/share/bug/file/presubj OK
/usr/share/doc/file/README.Debian OK
/usr/share/doc/file/README.gz OK
/usr/share/doc/file/changelog.Debian.gz OK
/usr/share/doc/file/changelog.gz OK
/usr/share/doc/file/copyright OK
/usr/share/lintian/overrides/file OK
/usr/share/man/man1/file.1.gz OK

aber nicht auf Dateiebene:

# debsums /usr/bin/ldd
debsums: invalid package name ‚/usr/bin/ldd‘

Man muss zuerst herausfinden, zu welchem Paket die Datei /usr/bin/ldd gehört.

# dpkg -S /usr/bin/ldd
libc-bin: /usr/bin/ldd

Damit man die Suche nicht jedes ausführen muss, hab ich ein kleines Shellscript geschrieben, der die diese Aufgabe übernimmt und mit den Ergebnissen debsums aufruft:

#!/bin/bash
EC=0
PKGS=$(dpkg -S „$@“ | awk -F‘:‘ ‚{print $1}‘ | sed -e ’s/, /\n/g‘ | sort | uniq)
for i in $PKGS; do
debsums „$i“ | grep -v OK
dec=$PIPESTATUS
echo -n „=== Package ‚$i‘ : “
if [ $dec -eq 0 ]; then echo OK; else echo MODIFIED; EC=$dec; fi
done
exit $EC

In diesem Beispiel habe ich das Skipt check-inst.sh genannt. Die Nutzung ist einfach:

# ./check-inst.sh /usr/bin/ldd
=== Package ‚libc-bin‘ : OK

Man kann das Skript mit mehreren Dateinamen aufrufen:

# ./check-inst.sh /usr/bin/file /usr/bin/ldd
=== Package ‚file‘ : OK
=== Package ‚libc-bin‘ : OK

Wenn man keinen absoluten Pfad verwendet bekommt man Suchergebnisse von installierten Paketen:

# ./check-inst.sh apache2
=== Package ‚apache2‘ : OK
=== Package ‚apache2.2-bin‘ : OK
=== Package ‚apache2.2-common‘ : OK
=== Package ‚apache2-doc‘ : OK
=== Package ‚apache2-mpm-prefork‘ : OK
=== Package ‚apache2-suexec‘ : OK
=== Package ‚apache2-utils‘ : OK
=== Package ‚bash-completion‘ : OK
=== Package ‚libapache2-mod-fcgid‘ : OK
=== Package ‚libapache2-mod-php5‘ : OK
=== Package ‚libapache2-mod-ruby‘ : OK
=== Package ‚libapache2-mod-suphp‘ : OK

Wenn die Ergebnisse OK sind, kann man mit ruhigem Gewissen sein Monitoringtool auf den neuen Stand bringen. Z.B. bei rkhunter:

# rkhunter –propupd –update

Hier ist eine Beispielausgabe, die die vom Benutzer modifizierten Dateien anzeigt:

# ./check-inst.sh /usr/bin/pear
/usr/bin/pear FAILED
/usr/bin/peardev FAILED
/usr/bin/pecl FAILED

/usr/share/php/pearcmd.php FAILED
/usr/share/php/peclcmd.php FAILED
=== Package ‚php-pear‘ : MODIFIED

Das Skript gibt den ExitCode 0 zurück, wenn alle Überprüfungen erfolgreich waren, ansonsten den Fehlercode des erst fehlgeschlagener Überprüfung. Dies ist nützlich, wenn man zunächst nur an ExitCode interessiert ist und die Fehlergebnisse von einer Logdatei auslesen möchte.

Viel Spaß beim Ausprobieren
C.