Das Wichtigste, das einem heutzutage nach dem Geld wohl verloren gehen kann, sind die eigenen Daten. Ich hatte einmal vor vielen Jahren einen schmerzlichen Datenverlust hinnehmen müssen, als mein iBook (erster Generation) nicht mehr starten wollte und der Apple-Support noch nicht viel von Datensicherung hielt: Festplatte gelöscht und ein neues System aufgespielt. Was hatte ich mich geärgert!

Seitdem heißt das Credo: Backup, Backup, Backup!
Zumindest am Mac ist die Backup-Funktion dank TimeMachine schnell eingerichtet. Dazu braucht man nicht mal eine teure TimeCapsule – eine einfache Netzwerkfestplatte ist für ein Drittel des Preises einer Apple TimeCapsule zu haben.

Ein einfaches Backup ist aber auch nicht alles. Man sollte sich fragen, was nach einem Einbruchsdiebstahl, Brand oder Wasserschaden (mal abgesehen vom eigentlichen Unglück) schlimmer ist: Der Verlust der Hardware oder der damit eingehende Verlust aller darauf gespeicherten Daten.
Den Verlust meines MacMini-Servers könnte ich vielleicht noch verkraften (auch wenn ich mir wohl erst einmal Urlaub nehmen müsste, um alle Dienste auf einem neuen Computer wieder aufzusetzen), aber sämtliche Urlaubs- und Familien-Bilder seit den 80ern, die (sehr umfangreiche) Musik-Sammlung, diverse Dokumente, Skripte und Formulare...da würde ich wahrscheinlich auch 20 Jahre später noch fluchen.

Deshalb: Neben dem ersten Backup, das den einfachen Datenverlust (versehentliches Löschen, technischer Defekt der primären Daten-Festplatte, ...) absichert, sollte noch ein zweites Backup existieren, das an einem geographisch entfernten Ort aufbewahrt wird.

Die einfachste Lösung

...ist für viele wahrscheinlich "die klaut". Dropbox, Google Drive & Co. bieten gratis dermaßen viel Speicherplatz an, der den meisten Leuten für ihre paar wichtigen Dokumente ausreichen sollte.

Doch will man seine wichtigsten Dokumente, deren Verlust man unter allen Umständen absichern möchte, einem fremden Dienstleister in die Hände spielen?
Zumindest mal nicht unverschlüsselt.

TimeMachine x2

Jahrelang hatte ich zwei TimeMachine-Festplatten in Gebrauch, die ich hin und wieder gegeneinander tauschte. Die zweite Festplatte landete dann zur Aufbewahrung in den vertrauensvollen Händen meiner Eltern.

Problem war eigentlich nur das "hin und wieder". Schnell gehen wieder ein paar Monate ohne Festplatten-Tausch in's Land.

In Dauersynchronisation mit BitTorrent Sync

Einem selbstständigen Freund, bei dem ein Datenverlust der Ruin wäre, habe ich das sekundäre Backup mit Hilfe von BitTorrent Sync realisiert.

Sein Datenserver im Geschäft ist ein MacPro, zu Hause hat er noch einen macMini mit externer 4TB-Platte im Dauerbetrieb.
Auf beiden Computern läuft BitTorrent Sync und synchronisiert alle wichtigen Verzeichnisse vom Server im Büro zum heimischen MacMini über das Internet. Dies funktioniert nun schon seit einigen Jahren einwandfrei, benötigt bei keinem Dienstleister eine Registrierung und auch keine VPN-Verbindung.

Wichtig war, dass ich dem MacMini nur Lese-Rechte eingeräumt habe (so dass nicht etwas versehentlich gelöscht wird, nur weil man mal zu Hause aus versehen einen Ordner von der Backup-Platte in den Mülleimer zieht) und dass die Option "Gelöschte Dateien in SyncArchive speichern" de-aktiviert ist (da es sich um eine recht große Datenmenge handelt und dieses Backup ja nur den Zweck hat, einen Verlust des ersten Backups abzusichern und nicht eines versehentlichen Löschens der Daten auf dem Haupt-Server – dafür gibt es auf dem Server noch ein weiteres Backup).

Da es ab und zu schon vor kam, dass der heimische MacMini aus Versehen ausgeschaltet wurde oder sich das BitTorrent Sync-Programm auf Server oder MacMini unerwartet beendete (an beiden Macs wird nur selten "rein gesehen"), habe ich noch zwei kleine Skripte geschrieben, die die Synchronisierung überwachen.
Diese laufen auf dem Server (wenn der nicht eingeschaltet wäre, würde es sofort bemerkt werden), prüfen morgens, ob BitTorrent Sync aktiv ist und schreiben in einen bestimmten Ordner (welcher synchronisiert wird) eine Textdatei mit dem aktuellen Datum.

Abends (um der Synchronisation etwas Zeit zu lassen, die Datei auch tatsächlich zu synchronisieren) holt sich ein zweites Skript per ssh den Inhalt eben jener Datei vom MacMini und vergleicht ihn mit dem aktuellen Datum.
Weicht das Datum der Datei vom MacMini vom aktuellen Datum ab, so wurden alle geänderten/hinzugekommenen Daten über den Tag hinweg offensichtlich nicht synchronisiert und eine Warn-E-Mail wird versendet.

Benötigt wird für die Funktion der Skripte (man möge mich dafür schlagen; aber ich wollte es mir mit der ssh-Authentifizierung von Server zu MacMini ganz einfach machen) sshpass und natürlich ein funktionierendes Postfix-System.

Alle notwendigen Dateien für den Synchronisations-Check können bei Bedarf hier heruntergeladen werden.
Über die Installation von sshpass ist später noch mehr zu lesen.

Selbst gescriptet

Mein Anspruch für ein sekundäres Backup meiner eigenen Daten bezieht sich auf:

  • Zwei Laptops
  • Mein MacMini-Server inklusive mySQL-Datenbank
  • Teile meiner 6TB-Datenfestplatte

Die Lösung mit BitTorrent Sync ist zwar schön und gut, dafür aber nicht flexibel genug. Wenn man einen ganzen Computer mit BitTorrent Sync sichern will, kommt man auf ziemlich viele Ordner, die man in das Backup einschließen muss. Oder einen Ordner mit unnötig hoher Datenmenge.
Die gesamte Festplatte via BitTorrentSync zu sichern macht sowieso keinen Sinn, da man mit den System-Dateien auf der Backup-Seite nichts mehr anfangen kann und ständig irgendwelche Log-Dateien durch die Internet-Leitung gedrückt werden.

Meine Lösung ist ein Skript, welches via ssh und rsync alle gewünschten Dateien auf eine NAS-Platte, die bei Bekannten/Verwandten steht und auf die ich via VPN Zugriff habe, überträgt.

Grundsätzliches

Grundvoraussetzung ist also einmal eine Netzwerk-Festplatte wie etwa die WesternDigital MyCloud (deren "klaut-Dienste" ich aber alle de-aktiviert habe) oder alternativ auf "der anderen Seite" ein immer eingeschalteter Unix-Rechner.
Und zweitens eine VPN-Verbindung zum entfernten Netzwerk.
Da sowohl ich wie auch meine Eltern (bei denen ich die Platte abgestellt habe) eine Fritz!Box als Router einsetzen, stand die VPN-Verbindung zwischen beiden Heim-Netzwerken sehr schnell.
Man kann auf die VPN-Verbindung natürlich auch verzichten, wenn man den SSH-Port am Router einfach zur Platte weiterreicht. Natürlich wieder mal ein kleines Sicherheitsleck, das man nicht unbedingt haben will.

Wie ich die WesternDigital-Platte für ihre Aufgabe fit gemacht habe, habe ich in diesem Artikel beschrieben.

Der macMini auf "meiner Seite" ist sowieso immer eingeschaltet und fungiert auch als TimeMachine-Server für die beiden Laptops.

Da ich das Fern-Backup zu einem festen Zeitpunkt durchführe, an dem die Laptops nicht unbedingt eingeschaltet sind und ich sowieso verhindern will, dass das Fern-Backup unterbrochen wird, weil ich gerade das MacBook zugeklappt habe, werden die zu sichernden Laptop-Daten nicht vom Laptop selbst, sondern von dessen TimeMachine-Backup geholt.
Dieses steht dem macMini-Server jederzeit zur Verfügung und hat einen Stand, der aktuell genug ist.

Vorbereitung: ssh-Identifizierung am NAS

Ich hatte es erst mit der Authentifizierung durch einen SSH-Schlüssel versucht, was aber irgendwie nicht hin hauen wollte, wenn mein Backup-Skript via launchd lief.
Auch wenn die launchd-Ausführung über meinen lokalen Benutzer läuft.

Vielleicht hatte ich etwas übersehen, aber letztendlich habe ich nun zu sshpass gegriffen, das es einem ermöglicht – ja, ich weiß, das ist sicherheitstechnisch unschön – in einem Skript das Passwort für eine ssh-Verbindung mitzugeben.
Da das Standard-SSH-Passwort einer WesternDigital-Platte aber sowieso jedem bekannt sein sollte, sah ich da nun kein großes Problem.

sshpass via Homebrew installieren

#Falls noch kein Homebrew installiert sein sollte, ist dies schnell erledigt
ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

# sshpass installieren
brew install https://raw.github.com/eugeneoden/homebrew/eca9de1/Library/Formula/sshpass.rb
sudo chown root:admin /usr/local/bin/brew
sudo brew link https://raw.github.com/eugeneoden/homebrew/eca9de1/Library/Formula/sshpass.rb
sudo chown apfelz:admin /usr/local/bin/brew

Das Backup-Skript

Mein Backup-Skript besteht aus drei Teilen:
Das eigentliche Perl-Skript, eine Konfigurations-Datei, damit man nicht im Skript rumpfuschen muss und der launchd-Datei für die automatisierte Ausführung.
Alle drei Dateien können hier zusammen heruntergeladen werden.

Konfiguration (ein kleiner Auszug – vollständigeres Beispiel für die Backup-Befehle in der heruntergeladenen Backup-Konfiguration):

RSYNC_ARGUMENTS="-rltPz --delete --numeric-ids --delete-excluded" # Standard-rsync-Argumente
SSH_PASS="welc0me" # SSH-Passwort zur WD-NAS-Festplatte; standardmäßig welc0me
SSH_HOST="root@192.168.0.99" # IP-Adresse der WD-NAS-Platte, wie man sie via VPN erreicht
SSH_ARGUMENTS="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/Users/apfelz/.ssh/known_hosts" # Standard-SSH-Argumente
DESTINATION="/DataVolume/shares/Fernbackup" # Freigabe auf der WD-NAS-Platte, auf der die Backup-Daten angelegt werden sollen (kann im Web-Interface angelegt werden)
VERBOSE=0 # Mehr (1) oder weniger (0) Informationen in die Log-Datei schreiben

# Ab hier die eigentlichen "Backup-Befehle".

[Library]
< /Library/
> /macMini/Library/
- .*
- Application Support/Adobe
- Developers
GO

# ..

Alle zu sichernden Daten werden über den Block

[name] ... GO
spezifiziert.
Es können beliebige viele Backup-Befehle in der Konfiguration hinzugefügt werden.

Syntax:

[bezeichnungDesBackups]
< /Verzeichnis/innerhalb/DESTINATION/wo/die/Daten/gesichert/werden/sollen
> /lokales/Verzeichnis/aus/dem/die/Daten/geholt/werden/sollen
- Auszuschliessende Datei.txt
- auszuschliessendes/Verzeichnis
- nicht/erwuenschte/Verzei*
GO

Zu sichernde Daten können auch aus einer Image-Datei (z.B. TimeMachine-Backup, das nicht unbedingt auf einer TimeCapsule lagert) entnommen werden. Der Pfad zur Image-Datei wird dann ebenfalls dem Backup-Block mit einem vorangestellten ! übergeben, der Pfad zur Daten-Quelle bezieht sich dann auf den absoluten Pfad zum Image-Inhalt.

Syntax:

[bezeichnungDesTimeMachineBackups]
! /Volumes/TimeCapsule/apfelzBook\ Pro_2015-01-02-104916.sparsebundle
< /Volumes/Time\ Machine-Backups/Backups.backupdb/apfelzBook\ Pro/Latest/Macintosh\ HD/
> /lokales/Verzeichnis/aus/dem/die/Daten/geholt/werden/sollen
- Auszuschliessende Datei.txt
- auszuschliessendes/Verzeichnis
- nicht/erwuenschte/Verzei*
GO

Die Time-Machine-Image-Dateien musste ich allerdings mit einem

chmod 777 /pfad/zur/Imagedatei.sparsebundle
für alle Benutzer lesbar machen (was für die Integrität des Backups keine Nachteile mit sich brachte).

Skript

#!/usr/bin/perl

my $config = "/Library/apfelz/conf/backup.conf";
my $exclude = "exclude.tmp";
my ($name,$src,$dest,$mount,$rsync_args,$ssh_args,$ssh_pass,$ssh_host,$dest_root,$verbose) = undef;
my $done = 1;
my ($s, $i, $h, $d, $m, $y, $wd, $yd, $dl) = localtime(time);
$m+=1; $y+=1900; if ($m==13){$m=1;}

# Konfigurationen
open (CONF, "<$config");
while (<CONF>) {
        if ($_ =~ m/RSYNC_ARGUMENTS="(.*)"#?/) { $rsync_args = $1; }
        if ($_ =~ m/SSH_PASS="(.*)"#?/) { $ssh_pass = $1; }
        if ($_ =~ m/SSH_HOST="(.*)"#?/) { $ssh_host = $1; }
        if ($_ =~ m/SSH_ARGUMENTS="(.*)"#?/) { $ssh_args = $1; }
        if ($_ =~ m/DESTINATION="(.*)"#?/) { $dest_root = $1; }
        if ($_ =~ m/VERBOSE=(\d)/) { $verbose = $1; }
        }
close(CONF);

if ($rsync_args eq undef || $ssh_args eq undef || $dest_root eq undef || $ssh_pass eq undef || $ssh_host eq undef) {
        print "FEHLER. Konfiguration enthält nicht genügend Parameter\n";
        exit 0;
        }

printf "\nStarte Backup %02s.%02s.%04s %02s:%02s.\n", $d, $m, $y, $h, $i;
if ($verbose eq 1) {print "Starte Backup mit folgenden Konfigurationen:\nrsync $rsync_args\nssh $ssh_args $ssh_host:$dest_root\n";}

# Backup-Programme
open (CONF, "<$config");
while (<CONF>) {
        if ($_ =~ m/\[(.*)\]/) {
                # Neues Backup
                if (!$done) { print "FEHLER. Neues Backup-Programm, aber vorheriges nicht gestartet.\n"; unlink($exclude); exit 0;}
                $done  = 0;
                $name  = $1;
                $src   = undef;
                $dest  = undef;
                $mount = undef;
                `echo "# $name" > $exclude`;
                }
        if ($_ =~ m/^-\s?(.*)/) { `echo "$1" >> $exclude`; }
        if ($_ =~ m/^>\s?(.*)/) { $dest = $1; }
        if ($_ =~ m/^<\s?(.*)/) { $src = $1; }
        if ($_ =~ m/^!\s?(.*)/) { $mount = $1; }
        if ($_ =~ m/^GO$/ && $src ne undef && dest ne undef) {
                # Backup durchführen
                if ($verbose eq 1) {print "=============================\nBackup $name:\n$src -> $dest\n";}
                if ($mount ne undef) {
                        if ($verbose eq 1) {print "Mounte $mount\n";}
                        `sudo /usr/bin/hdiutil mount $mount`;
                        sleep 10;
                        }
                `/usr/local/bin/sshpass -p $ssh_pass ssh $ssh_host 'if [[ ! -d $dest_root$dest ]]; then mkdir -p $dest_root$dest; fi'`;
                `sudo rsync $rsync_args --exclude-from=$exclude -e '/usr/local/bin/sshpass -p $ssh_pass ssh $ssh_args' $src $ssh_host:$dest_root$dest`;
                if ($mount ne undef) {
                        sleep 10;
                        if ($verbose eq 1) {print "Werfe $mount aus...\n";}
                        `sudo /usr/bin/hdiutil eject '/Volumes/Time Machine-Backups'`;
                        `/usr/local/bin/sshpass -p $ssh_pass ssh $ssh_host 'chmod -R 777 $dest_root$dest'`;
                        sleep 10;
                        }
                $done = 1;
                }
        }
($s, $i, $h, $d, $m, $y, $wd, $yd, $dl) = localtime(time);

printf "Backup beendet um %02s:%02s Uhr.\n\n", $h, $i;
close(CONF);

unlink($exclude);

launchd-Datei

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>de.apfelz.backup</string>
    <key>ProgramArguments</key>
    <array>
        <string>/Library/apfelz/scripts/backup.pl</string>
    </array>
    <key>UserName</key>
    <string>apfelz</string>
    <key>StandardErrorPath</key>
    <string>/Library/Logs/apfelz/backup.log</string>
    <key>StandardOutPath</key>
    <string>/Library/Logs/apfelz/backup.log</string>
    <key>StartCalendarInterval</key>
    <array>
    <dict>
                <key>Hour</key>
                <integer>01</integer>
                <key>Minute</key>
                <integer>00</integer>
    </dict>
    </array>
</dict>
</plist>

Das Backup-Skript wird bei mir täglich um 0100h ausgeführt. Man sollte beachten, dass viele Internet-Provider eine tägliche Zwangstrennung des Internet-Zugangs durchführen, der dem Backup (das mitunter etwas länger laufen kann), nicht so gut tut (kurz gesagt: Es wird abgebrochen).
Ich habe auf den beiden FritzBoxen die Zwangstrennung auf 0400h verschoben und führe das Skript um 0100h aus, was mindestens drei Stunden Zeit gibt, die neuen Daten zu kopieren.

Wird die Ausführung unterbrochen, weil doch mal sehr viele neue Daten an einem Tag angefallen sind, so wird das Backup einen Tag später fortgesetzt und macht da weiter, wo es am Vortag unterbrochen wurde.

Man sollte die NAS-Backup-Platte natürlich für das erste Backup im lokalen Netzwerk betreiben, da wahrscheinlich Datenmengen im Giga- bis Terabyte-Bereich zusammen kommen.
Für die ersten Tests hatte ich die Platte zu Hause betrieben und dann später nur bei meinen Eltern angestöpselt und die IP in der Konfiguration angepasst.

launchd führt das Backup-Skript mit meinen lokalen Benutzer-Rechten aus – das bewirkt der Zusatz

<key>UserName</key>
<string>apfelz</string>

Somit sollte es keine Zugriffsrechte-Probleme bei den zu sichernden Daten geben.

Fazit

Das Backup funktioniert überraschend fehlerfrei. Ab und zu kann die TimeMachine-Image-Datei meines Laptops nicht gelesen werden und wird übersprungen (vermutlich, weil ich den Laptop während des TimeMachine-Backups zugeklappt habe und die Datei gesperrt wird, bis der Laptop das Backup abgeschlossen hat), am nächsten Tag wird das Backup dann aber nachgeholt.

Kamen mal mehrere hundert Megabyte an einem Tag neu dazu, konnte es sein, dass die Provider-Zwangstrennung das Backup unterbrach – wurde dann aber am nächsten Tag fortgeführt.
So kann es natürlich sein, dass es mehrere Tage dauert, bis auch wirklich alle Daten auf dem fernen Backup vorhanden sind, aber sollte ich das ferne Backup wirklich mal brauchen, werde ich froh sein, dass ich überhaupt alle meine Daten bis vor wenigen Tagen noch irgendwo habe. Mit einem kleinen Datenverlust von ein bis zwei Tagen werde ich da sicher leben können.

Was es in Sachen Daten-Menge auf jeden Fall zu beachten gibt: Ich habe alle möglichen Verzeichnisse, in denen sich jeden Tag viele neue Daten ansammeln, die mich allerdings nicht interessieren (z.B. Cache-Ordner oder die meisten ApplicationSupport-Verzeichnisse), vom Backup abgeschlossen.
Dass jeden Tag 100MB irrelevanter Cache-Dateien die Internetleitung verstopfen und den Backup-Abschluss verzögern, muss nicht sein.

Folgende Verzeichnisse empfehle ich, auszuschließen:

- Receipts
- Caches
- Sandbox
- Java
- Logs
- Spotlight
- Documentation
- Updates
- DropboxHelperTools
- Filesystems
- Printers
- PrivilegedHelperTools
- org.pqrs
- Frameworks
- /Users/me/Downloads
- /Users/me/Desktop
- /Users/me/TheVolumeSettingsFolder
- /Users/me/Library/Application Support/Adobe
- /Users/me/Library/Application Support/DVD Player
- /Users/me/Library/Application Support/Google
- /Users/me/Library/Application Support/Plex Media Server
- /Users/me/Library/Application Support/XBMC
- /Users/me/Library/Caches
- /Users/me/Library/Eye-Fi
- /Users/me/Library/iTunes/Device Support
- /Users/me/Library/iTunes/iPod Software Update
- /Users/me/Library/Mobile Backups
- /Users/me/Library/Safari
- /Users/me/Library/Logs
- /Users/me/Library/Cookies
- /Users/me/Library/Mail
- /Users/me/.Trash
- /Users/me/Dropbox
- net
- private
- .TemporaryItems
- sbin
- bin
- tmp
- usr
- dev
- var
- home
- System
- etc
- Applications
- Users/mirco/Library/Acrobat User Data
- Library/Application Support/Adobe
- Library/Application Support/GarageBand
- Library/Application Support/iPhoto
- Library/Application Support/Microsoft
- Library/Application Support/i*
- Library/Caches
- Library/Parallels
- Library/Java
- Library/Logs
- Library/CFMSupport
- Library/DropboxHelperTools
- Library/Google
- Library/Image Capture