Backups mittels Rsnapshot und Rsync unter Debian

Geschrieben von Eric Scheibler am 10.04.2013

In diesem Artikel beschreibe ich mein Vorgehen bei der Erstellung von Backups. Ziel ist, dass mein Notebook eine Kopie aller relevanten Daten auf meinem Server ablegt. Dabei kommt Rsnapshot zum Einsatz, welches auf einfache Art und Weise inkrementelle Backups erstellt ohne unnötig Platz zu belegen.

Bevor ich die eigentliche Lösung beschreibe muss ich zunächst meine Anforderungen an das Backup System erläutern und in dem Zusammenhang auf die Vor - und Nachteile hinweisen.

Anforderungen und Zielstellungen für das Backup

Ich verwalte zwei kleine Heimnetzwerke mit jeweils einem Server, auf denen Debian Wheezy läuft. Im Netzwerk n1 sind 3 Clients vorhanden, allesamt Debian Wheezy, wohingegen sich in n2 3 Windows 7 Clients und ein Debian Wheezy Rechner befinden. Die Festplatten beider Server sind verschlüsselt und laufen in einem RAID Verbund. Beide Netzwerke sind mit standard DSL Anschlüssen ausgestattet und besitzen daher je eine DynDNS Domain, um von Außen erreichbar zu sein. Als DynDNS Anbieter kann ich No-IP empfehlen (kostenlos) und die Verwaltung läuft unter Debian einwandfrei mit DDclient. Zusätzlich existiert noch ein angemieteter V-Server, auch Debian Wheezy.

Das Netzwerk n1 ist unter der Adresse network1.no-ip.org, das Netzwerk n2 unter network2.no-ip.org zu erreichen. Ferner wurde in den Routern von n1 und n2 jeweils eine Portfreigabe für den SSH Port der Server eingerichtet, d.h. alle, bei network1.no-ip.org ankommenenden Datenpakete auf Port 23456 leitet der Router an s1 mit der IP 192.168.1.100 weiter. Analog für n2 und s2. Somit sind die Server von außen via SSH auf Port 23456 erreichbar.

Ziel ist es, dass jeder Client und auch die Server inkrementelle Backups vorliegen haben. Auf Grund der Netzwerktopologie bietet es sich an, dass die Clients in n1 ihr Backup zunächst auf s1 ablegen. Analog gilt dies für n2. Beim V-Server ist’s egal, er wird seine Backups ebenfalls bei s1 abgeben.

Da die Serverfestplatten verschlüsselt sind und ich ausschließlichen Zugriff auf die Server habe, ist eine zusätzliche Verschlüsselung der Backups nicht erforderlich. Wer also auf Cloudspeicher sichern möchte muss entweder eine weitere Verschlüsselungsebene einbauen oder ein anderes Programm wie beispielsweise Duplicati in Erwägung ziehen. Da sich der V-Server dauerhaft und ein Notebook gelegentlich außerhalb von n1 und n2 befinden, ist allerdings eine gesicherte Verbindung zu den Servern erforderlich.

Im Kern besteht meine Lösung aus der Verwendung von Rsnapshot und Rsync. Rsnapshot erstellt ohne großen Konfigurationsaufwand inkrementelle Backups der gewünschten Ordner. So lässt sich beispielsweise eine stündliche oder tägliche Sicherung erstellen. Hat sich eine Datei zwischen zwei Backups nicht geändert, so wird lediglich ein Hardlink auf den neuen Ordner gesetzt. Bei einer Änderung hingegen kopiert Rsnapshot die Datei komplett. Der Vorteil ist, dass jederzeit auf alle Backup Versionen zugegriffen werden kann, denn es existiert für jede Version scheinbar ein Ordner mit einem vollständigen Backup. Nachteilig wirkt sich dies lediglich bei großen Dateien aus. Diese sollten mit einem anderen Verfahren, wie z.B. der Erstellung von Diff’s gesichert werden.

Für die Sicherung auf einen Server gibt es generell zwei Möglichkeiten: Entweder kontaktiert der Server den Client und erstellt eine Sicherung oder die Initiative geht vom Client aus. Für Variante 1 spricht, dass es bei einem Einbruch auf dem Client keinerlei Möglichkeit gibt, dass Backup, welches auf dem Server liegt, zu zerstören. Dafür hat man speziell bei beweglichen Clients wie dem Notebook das Problem, dass der Server nicht weiß, wo sich der Client gerade befindet. Dies wäre zu lösen, indem der Client dem Server einen Backupauftrag erteilt und ihm so die aktuelle IP mitteilt. Gravierender ist allerdings, dass der Server einen root Zugang zum Client benötigt oder automatisch root werden können muss, wenn er auch Dateien sichern soll, die ausschließlich dem root Account des Clients zugänglich sind. Da ich das auf keinen Fall möchte, gehe ich den umgekehrten Weg. Damit der Client auf dem Server potentiell nicht mehr Schaden anrichten kann als nötig, baue ich serverseitig eine chroot Umgebung auf, dazu später mehr. So kann im schlimmsten Fall nur das Backup gelöscht werden.

Aus Rsnapshot heraus lässt sich allerdings kein entferntes Ziel angeben. Auch das mounten via SSHFS funktioniert nicht, da dort mindestens die Unterstützung für Hardlinks fehlt. Um die beschriebene Lösung trotzdem aufbauen zu können, wird das Backup zweistufig organisiert. Zunächst erstellt Rsnapshot das inkrementelle Backup auf der Client HDD. Im zweiten Schritt transferiert Rsync den Backup Ordner zum Server. Vorteil ist, dass eine versehentlich gelöschte Datei zunächst noch auf der Festplatte zu finden ist und nicht direkt der Backup Server kontaktiert werden muss. Dafür benötigt man etwa 1.5x so viel Speicherplatz auf der Client HDD wie das Backup groß ist.

Auf diese Art und Weise sichern alle Clients ihre Daten auf den Servern s1 und s2. Um auch die räumliche Trennung der Sicherungskopien sicher stellen zu können und das Problem der clientseitigen Löschung des Backups auf dem Server zu beheben tauschen auch die beiden Server ihre Backups zu festgelegten Zeiten aus. Somit existiert immer auch eine Sicherung, zu der der Client automatisiert keinen Zugriff hat.

Kurz noch ein Vergleich zum oben genannten Duplicati. Das Tool bietet plattformunabhängig die Möglichkeit, verschlüsselte, inkrementelle Backups auf entfernten Servern und gängigen Cloudspeicherdiensten abzulegen. Daher war es vor Rsnapshot meine erste Wahl. Allerdings dauerte die Erstellung eines vollständigen Backups bei mir mehrere Stunden. Des weiteren liegen die einzelnen Backups durch die Verschlüsselung natürlich nicht einfach vor sondern müssen zunächst einmal extrahiert werden. Auch dies dauerte bei der Recovery einer einzelnen Datei aus einem 10GB großen Backup (mein Home Verzeichnis mit vielen kleinen Dateien) ziemlich lange. Generell ist der Recovery Prozess ohne Verwendung der GUI nicht so komfortabel wie bei Rsnapshot. Somit blieb als einziger Vorteil zu meiner späteren Lösung die Verschlüsselung der Daten. Dies war mir aber, wie oben ausgeführt, nicht so wichtig, sodass ich Rsnapshot gewählt habe. Die Entscheidung für eines der Tools ist also von den jeweiligen Prioritäten abhängig. Gänzlich abraten kann ich hingegen von dem Programm Duplicity. Dort gelang mir der Recovery Prozess überhaupt nicht, da das Tool dauerhaft abstürzte. Ferner läuft es nur unter Linux.

Im folgenden beschreibe ich die Einrichtung von Rsnapshot auf einem Client und die nötigen Schritte auf der Serverseite. Der Sync von s1 und s2 ist dann lediglich ein Rsync Befehl, welcher sich aus dem Tutorial erschließt und daher nicht gesondert behandelt wird.

Rsnapshot auf dem Client

Zunächst wird Rsnapshot auf dem Client Rechner eingerichtet. Die Backups werden unter /backups gespeichert. Je nachdem, ob auch sensible Daten ins Backup eingehen, sollte ggf. nur root Zugriff auf den Ordner haben:

sudo mkdir /backups
sudo chmod 700 /backups

Als nächstes werden die benötigten Programme installiert:

sudo aptitude install rsnapshot rsync lftp

Nun kann die Config Datei von Rsnapshot angepasst werden:

sudo vim /etc/rsnapshot.conf
[...]
snapshot_root   /backups/
[...]
# EXTERNAL PROGRAM DEPENDENCIES
cmd_cp          /bin/cp
cmd_rm          /bin/rm
cmd_rsync       /usr/bin/rsync
cmd_logger      /usr/bin/logger
cmd_du          /usr/bin/du
cmd_rsnapshot_diff      /usr/bin/rsnapshot-diff
[...]
# BACKUP INTERVALS
interval        daily   7
interval        weekly  4
interval        monthly 6
[...]
# The include_file and exclude_file parameters
exclude_file    /etc/exclude_from_backup
[...]
### BACKUP POINTS / SCRIPTS ###
backup  /home/              ./
backup  /root/              ./
backup  /boot/              ./
backup  /etc/               ./
backup  /usr/local/bin/     ./
backup  /usr/local/sbin/    ./
backup  /var/               ./

Zusätzlich noch das exclude File anlegen und dort alle Dateien und Ordner spezifizieren, welche nicht mit in das Backup aufgenommen werden sollen:

sudo vim /etc/exclude_from_backup
/etc/network/run
lost+found/
/var/lock
/var/run
/var/cache

Das Rsnapshot Config file verlangt Tabstops statt Leerzeichen zwischen den Optionen und immer einen abschließenden “/” bei Ordnern. Ob die Datei valide ist, testet man mit:

sudo rsnapshot configtest

Um den Backup Vorgang zu testen, startet man Rsnapshot manuell mit dem kleinsten, im Config File angegebenen Intervall. Im obigen Fall:

sudo rsnapshot daily

Nach dem Initialen Backup findet man unter /backups einen Ordner daily0. Ein weiterer Start erzeugt daily1 und legt für alle unveränderten Dateien Hardlinks an. Nach einer Woche, wenn daily0-6 vorhanden sind, startet man Rsnapshot dann mit dem weekly Parameter. Daily6 wird zu weekly0 umbenannt und in der nächsten Woche werden die daily Ordner überschrieben. So hat man nach 6 Monaten maximal 7 daily, 4 weekly und 6 monthly Ordner auf der HDD liegen. Dabei sollte man “woche” nicht so ernst nehmen. Wenn man mal ein Backup Intervall verpasst, schiebt sich das Backup lediglich nach hinten.

Server für ankommende Backups konfigurieren

Als erstes wird der Ordner für alle Backups erstellt:

# mkdir /data/backups
# chmod 700 /data/backups
# chown root:root /data/backups

Der Ordner muss root gehören. Zum einen weil die User des Servers keinen Zugriff auf die Backups der Clients haben sollen und zum anderen, weil dies eine Bedingung für die chroot Umgebung ist.

Chroot

Eine chroot Umgebung beschränkt den, sich via SSH einloggenden User auf einige wenige nutzbare Programme und verhindert, dass selbiger Zugriff auf das serverseitige Dateisystem erlangt. Der Backup User benötigt lediglich Zugriff auf rsync und bash, um die Backups auf dem Server abzulegen. das Chroot Verzeichnis /data/backups bildet für den eingeschränkten Nutzer das Wurzelverzeichnis “/”. Er sucht jetzt z.B. das Binary von rsync wie gewöhnlich unter /bin. Damit es dort auch gefunden werden kann, muss es samt der verwendeten Libraries dort hinein kopiert werden.

cd /data/backups
# mkdir bin
# cp /bin/bash /usr/bin/rsync bin

Als nächstes die verwendeten Libraries mittels ldd ermitteln und unter lib und lib64ablegen. Das Beispiel zeigt ein Debian Wheezy 64Bit. Bei einen 32Bit System funktioniert dies prinzipiell genauso, nur die Pfade differieren.

# ldd /bin/bash
# mkdir -p lib/x86_64-linux-gnu/
# mkdir lib64
# cp /lib64/ld-linux-x86-64.so.2 lib64
# cp /lib/x86_64-linux-gnu/libc.so.6 lib/x86_64-linux-gnu
# cp /lib/x86_64-linux-gnu/libdl.so.2 lib/x86_64-linux-gnu
# cp /lib/x86_64-linux-gnu/libtinfo.so.5 lib/x86_64-linux-gnu

# ldd /usr/bin/rsync
# cp /lib/x86_64-linux-gnu/libacl.so.1 lib/x86_64-linux-gnu
# cp /lib/x86_64-linux-gnu/libattr.so.1 lib/x86_64-linux-gnu
# cp /lib/x86_64-linux-gnu/libpopt.so.0 lib/x86_64-linux-gnu

# chmod -R 755 bin lib lib64
# chown -R root:root bin lib lib64

Neuen Backup Nutzer anlegen

Obiges muss natürlich nur einmal durchgeführt werden. Die folgenden Schritte hingegen bei jedem neuen Backup Nutzer. Exemplarisch soll der User für die Backups vom Notebook erstellt werden.

# useradd --shell /bin/bash --create-home --no-user-group --gid nogroup --home-dir /data/backups/backup_notebook backup_notebook

Passwort für den User festlegen:

# passwd backup_notebook

Nur der Nutzer backup_notebook darf Zugriff auf sein Home Verzeichnis, in welchem später die Backups abgelegt werden, haben. So schottet man die Backups der einzelnen Clients voneinander ab:

# chmod 700 backup_notebook

Dann brauche ich noch eine leere Textdatei, welche weiter unten in dem Sync Skript benötigt wird und den Ordner für die Backups.

# touch backup_notebook/loadme
# mkdir backup_notebook/backups
# chown backup_notebook:nogroup backup_notebook/loadme
# chown backup_notebook:nogroup backup_notebook/backups

Die Chroot Umgebung in der SSHD Config Datei definieren:

# vim /etc/ssh/sshd_config
[...]
# chrooted environment
Subsystem sftp internal-sftp
# my backup users
Match User backup_notebook
ChrootDirectory /data/backups
AllowTCPForwarding no
X11Forwarding no

Es sei nochmal darauf hingewiesen, dass das ChrootDirectory zwingend root gehören muss, sonst klappt’s nicht. Anschließend SSH Server neu starten:

# /etc/init.d/ssh restart

Danach auf dem Notebook testen, ob die Anmeldung funktioniert:

notebook# ssh -p PORT backup_notebook@network1.no-ip.org

Bekommt man nach der Eingabe des Passworts einen Prompt wie “-bash-4.2$” zu sehen, dann läuft die Chroot Umgebung. Außer rsync und ls sollte man nun kein Programm mehr aufrufen können. Mit “exit” meldet man sich wieder ab.

Automatisierte Anmeldung mittels public/private Key

Als nächstes brauchen wir ein SSH Zertifikat, um die Anmeldung des Clients am Backup Server automatisiert durchführen zu können. Zunächst serverseitig den SSH Ordner für den Public Key erstellen:

# mkdir backup_notebook/.ssh
# chmod 755 backup_notebook/.ssh
# chown backup_notebook:nogroup backup_notebook/.ssh

Jetzt kurz zum Notebook wechseln, um dort den SSH Key zu erzeugen, am besten auch als root, da das Backup Skript später auch mit root Rechten ausgeführt wird:

notebook# mkdir ~/.ssh
notebook# ssh-keygen -b 4096 -C "backup from notebook to server" -f ~/.ssh/backup_notebook_server -t rsa

Das Passwort Feld leer lassen, sonst funktioniert die automatische Anmeldung nicht. Anschließend den Public key zum Server transferieren. Für die Anmeldung kann der gerade erstellte Backup Nutzer nicht verwendet werden, da dieser serverseitig scp ja nicht kennt. Statt dessen gehe ich davon aus, dass es mindestens noch einen anderen User gibt, mit dem man sich via SSH auf den Server einloggen kann:

notebook# scp -P PORT ~/.ssh/backup_notebook_server.pub USERNAME@network1.no-ip.org:/home/USERNAME

Danach die SSH Verbindung inkl. Private Key in der clientseitigen SSH Config Datei festhalten:

notebook# vim ~/.ssh/config
# define aliases for ssh connection to the backup server
# for connection in the LAN
# server has example IP 192.168.1.100
Host backup_192.168.1.100
HostName 192.168.1.100
Port 23456
IdentityFile /root/.ssh/backup_notebook_server

# connection via dyndns
Host backup_s1
HostName network1.no-ip.org
Port 23456
IdentityFile /root/.ssh/backup_notebook_server

Nun wieder auf dem Server den gerade transferierten Public Key in den SSH Ordner des Backup Nutzers kopieren und die rechte anpassen:

# mv /home/USERNAME/backup_notebook_server.pub /data/backups/backup_notebook/.ssh/authorized_keys
# chown backup_notebook:nogroup /data/backups/backup_notebook/.ssh/authorized_keys
# chmod 644 /data/backups/backup_notebook/.ssh/authorized_keys

SSH Server neu starten:

# /etc/init.d/ssh restart

Schließlich erneut auf dem Notebook prüfen, ob die Anmeldung jetzt passwortlos erfolgt:

notebook# ssh backup_notebook@backup_s1

Es muss der gleiche Prompt wie oben zu sehen sein.

Backup vom Client mittels rsync auf den Server transferieren

Nachdem das inkrementelle Backup auf der Client HDD liegt und der Server vorbereitet wurde, folgt schließlich der Transfer des Backups zum Server. Dies erläutere ich anhand eines kleinen Shell Skripts:

Zu Beginn wird auf ein gültiges Backup Intervall und eine vorhandene Rsnapshot Config Datei geprüft.

#!/bin/bash

if [[ $1 == "hourly" || $1 == "daily" || $1 == "weekly" || $1 == "monthly" ]]; then
interval="$1"
else
echo "Unknown interval $1"
exit 1
fi

config_file="/etc/rsnapshot.conf"
if [[ ! -f "$config_file" ]]; then
echo "The config file $config_file does not exist"
exit 1
fi

Bei einem Fehler möchte ich per Mail benachrichtigt werden:

# define mail address and bodies
address="email@example.org"
success="backup from client to server was successful"
error="backup from client to server failed"

Ein paar Konfigurationsparameter:

rsync_options="--archive --update --delete --compress --delete-excluded --hard-links --partial -e "ssh""
backup_source="/backups"
backup_user="backup_notebook"
log_file="/var/log/rsnapshot_backup_to_s1.log"
upload_limit=0     # in kb/s, 0 = unlimited

Jetzt wird zunächst das lokale Backup mittels Rsnapshot erstellt. Das Kann natürlich auch außerhalb dieses Skripts beispielsweise mittels eines Cronjobs getan werden aber ich finde es praktischer, wenn alles in einem Skript erledigt wird.

# start rsnapshot local backup
rsnapshot $interval
rsnapshot_rc=$?
if [[ $rsnapshot_rc != 0 ]]; then
msg="rsnapshot $interval backup: error, return code $rsnapshot_rc"
echo "$msg at $(get_timestamp)" >> "$log_file"
echo "$msg at $(get_timestamp)" | mail -s "$error" "$address"
exit 3
fi

Nach der Fertigstellung des lokalen Backups wird geprüft, ob der Server erreichbar ist. Dazu versucht das Skript mit dem FTP Client lftp eine Datei zu laden. Zunächst wird das im LAN versucht, da der Umweg über die DynDNS Domain einen erheblichen Geschwindigkeitseinbruch verursacht. Klappt dies nicht, so wird schließlich die DynDNS Domain verwendet. Bei dem lftp Befehl muss ein Passwort nach dem “:” angegeben werden. Das ist auf Grund des SSH Key’s natürlich nicht nötig, ein leerer String wird allerdings nicht akzeptiert. Es ist vollkommen egal, was dort steht, bei mir ist’s jetzt “bla”.

# try to contact host
loadme="/tmp/loadme"
# if the test file still exists from the last backup, try to remove it
if [ -e "$loadme" ]; then
rm "$loadme"
if [[ $# != 0 ]]; then
echo "The loadme file can't be removed at $(get_timestamp)" >> "$log_file"
echo "The loadme file can't be removed at $(get_timestamp)" | mail -s "$error" "$address"
exit 3
fi
fi

host="backup_192.168.1.100"
lftp -c "set cmd:at-exit; set net:timeout 10; set net:max-retries 3;    get sftp://$backup_user:bla@$host/$backup_user/loadme -o /tmp"
if [ ! -e "$loadme" ]; then
host="backup_s1"
lftp -c "set cmd:at-exit; set net:timeout 10; set net:max-retries 3;         get sftp://$backup_user:bla@$host/$backup_user/loadme -o /tmp"
if [ ! -e "$loadme" ]; then
echo "server s1 not accessible, no backup! at $(get_timestamp)" >> "$log_file"
echo "server s1 not accessible, no backup! at $(get_timestamp)" | mail -s "$error" "$address"
exit 2
fi
fi
rm /tmp/loadme

Wenn der Server erreichbar ist, kann der Kopierprozess beginnen. Rsync wird bis zu fünf Mal aufgerufen. So kann der Sync z.B. nach einer verlorenen Internetverbindung oder den Standby des Notebooks fortgesetzt werden. Durch die Rsync Option “–partial” muss nicht von vorn begonnen werden. Statt dessen wird nur der fehlende Teil des Backups noch übertragen.

# sync to server s1
backup_destination="$backup_user@$host:/$backup_user/"
if [[ $upload_limit > 0 ]]; then
rsync_options="$rsync_options --bwlimit=$upload_limit"
fi

echo "rsync started"
i=0
max_restarts=5
last_exit_code=1
while [ $i -le $max_restarts ]
do
i=$(( $i + 1 ))
rsync $rsync_options "$backup_source" "$backup_destination"
last_exit_code=$?
if [ $last_exit_code -eq 0 ]; then
break
fi
sleep 300
done

Die Skripte schließlich in die jeweiligen “bin” Ordner kopieren und ausführbar machen:

unzip rsnapshot_backup.zip
sudo mv rsnapshot_backup_to_s1.sh /usr/local/sbin/
sudo chmod +x /usr/local/sbin/rsnapshot_backup_to_s1.sh
sudo helper_functions.sh /usr/local/bin
sudo chmod +x /usr/local/bin/helper_functions.sh

Nun muss nur noch dafür gesorgt werden, dass es auch den jeweiligen Intervallen entsprechend ausgeführt wird. Dazu kann zum einen jeweils ein Cronjob angelegt werden. Nachteil: dieser wird stets zu einer fixen Uhrzeit gestartet. Bei Rechnern, welche 24/7 laufen ist das kein Problem aber bei einem Notebook wird die Uhrzeit gern mal verpasst. Daher kommt man in diesem Fall besser, wenn man Anacron nutzt. Dazu legt man jeweils ein Startskript in die Ordner /etc/cron.daily/, /etc/cron.weekly/ und /etc/cron.monthly. So ist sichergestellt, dass das Skript auf jeden Fall ausgeführt wird, es sei denn, der Rechner wird einen ganzen Tag lang nicht eingeschaltet.

sudo vim /etc/cron.daily/rsnapshot_daily
#!/bin/bash
/usr/local/sbin/rsnapshot_backup_to_s1.sh daily > /dev/null 2>&1

Ausführbar machen:

sudo chmod +x /etc/cron.daily/rsnapshot_daily

Nun kann man von Zeit zu Zeit mal ins Log File schauen, bekommt aber auch immer eine Mail, wenn ein Fehler auftritt.

Unter Zuhilfenahme von Cygwin lässt sich das clientseitige Backup auch problemlos auf Windows PC’s einrichten. Die Cygwin Umgebung muss einmal mittels des Setup Programms eingerichtet, mindestns rsnapshot, rsync, lftp und ein Texteditor installiert und anschließend die obige Anleitung analog durchgeführt werden.

Für das Backup zwischen den Servern s1 und s2 kann das Skript angepasst werden, sodass der gesamte /backups Ordner übertragen wird.

Download

Download des Sync-Skripts