Automatisiertes externes Backup für Linux

· by Jörg Marx · Read in about 5 min · (981 Words)

Um die wertvollen Daten an einen sicheren Ort bringen zu können, müssen diese ab und zu auf einem mobilen Datenträger landen. Dabei ist auch hier ein hoher Automatisierungsgrad der Schlüssel zur Regelmäßigkeit.

Das Vorhandensein eines Offline-Backups ist gerade in Zeiten von Krypto-Trojanern oft die letzte Chance für das Wiederherstellen der Daten auf einem System. Online per Netzwerkverbindung erreichbare Sicherungsorte werden im worst-case gleich mit vom Trojaner verschlüsselt und sind damit nutzlos.

Das Kopieren auf den externen Datenträger wird von einem Skript erledigt.

/backup/dobackup.sh:

#!/bin/bash

_DIR="$(cd $(dirname $0) && pwd)"
_SELF="$(basename $0)"
_LOG="${_DIR}/log/${_SELF}_$(date '+%Y%m%d-%H%M%S').log"
[ -d "${_DIR}/log" ] || {
    mkdir "${_DIR}/log"
}

_LABEL=

mylog()
{
    echo "$(date '+%Y%m%d-%H%M%S'): $*" >>${_LOG}
}

if [ $(ps -p 1 -o comm=) != "systemd" ]; then
    # fork into a new group to detach from udev
    if [ "$1" != "for_real" ]; then
        /usr/bin/setsid ${_DIR}/${_SELF} for_real &
        exit
    fi
fi

prepare()
{
    [ -d /mnt/backup ] || {
        mylog "mkdir '/mnt/backup'"
        mkdir /mnt/backup
    }

    [ -r ${_DIR}/NOBACKUP ] && {
        mylog "NOBACKUP marker found. Exiting."
        exit
    }

    # just to be sure...
    umount /dev/backup
    umount /mnt/backup

    mount /dev/backup /mnt/backup

    [ -r /mnt/backup/BACKUP ] || {
        mylog "No backup medium, file marker 'BACKUP' not found."
        umount /mnt/backup
        exit
    }

    _LABEL=$(head -n 1 /mnt/backup/BACKUP)

    rm /mnt/backup/${_SELF}*.log
}

finish()
{
    cp ${_LOG} /mnt/backup/
    sync
    umount /mnt/backup
}

############### main ##################

mylog "backup started."

prepare

mylog "External medium is '${_LABEL}'"

for l in /backup/* ; do 
    if [ -L $l ] ; then
        mylog "*******************************************************************"
        mylog "# backing up $(readlink -f $l)"
        rsync -xaH --delete --info=STATS2 $(readlink -f $l) /mnt/backup >>${_LOG} 2>&1
        # flush the buffers just to be sure
        sync
        mylog "# finished $(readlink -f $l)"
        mylog "###################################################################"
    fi
done

mylog "backup finished."

finish

Angestoßen wird das Skript beim Anstecken eines Mediums über eine udev-Regel.

/etc/udev/rules.d/70-my-backup.rules

# Platte erkennen und Backup-Partition einbinden und Backup starten

KERNEL=="sd?1", SUBSYSTEMS=="usb", ATTRS{idVendor}=="152d", ATTRS{idProduct}=="2509", SYMLINK+="backup", ACTION=="add", RUN+="/backup/dobackup.sh"

Damit nicht jedes externe Medium Gefahr läuft, durch den Backup-Mechanismus als Ziel verwendet zu werden, sind zwei Vorkehrungen dagegen eingebaut.

  1. Die udev-Regel ist Geräte-spezifisch: alle verwendeten externen Backup-Platten müssen mit ihrer USB-Geräte-ID explizit eingetragen werden. Diese ID wird beim Einrichten der Platte mit dem Kommando lsusb ausgelesen und in einer neuen Zeile der udev-Regeldatei hinzugefügt (idVendor und idProduct).

  2. Auf der externen Platte muss sich im Wurzelverzeichnis eine Datei mit dem Namen BACKUP befinden. In dieser Datei ist ein eindeutiger Name für die Platte zu hinterlegen, dieser landet dann auch in den Logs der Sicherung und dient der Identifikation und Nachvollziehbarkeit, sowie als Marker für ein gültiges Backup-Medium.

Die udev-Regel legt beim Anstecken einer bekannten Backup-Platte noch einen symbolischen Link /dev/backup an, damit braucht das eigentliche Backup-Skript auch nicht in den Blockdevices suchen, welches davon denn nun die Backup-Platte ist. Auch das erhöht die Sicherheit, dass die Daten auch wirklich auf dem richtigen Medium gesichert werden.

Die Backup-Festplatte wird immer nach /mnt/backup eingebunden, damit ist auch für das im Skript folgende rsync-Kommando alles klar. Die Logdatei der Sicherung wird sowohl lokal, als auch auf der externen Platte abgelegt, dies vereinfacht die Nachvollziehbarkeit und Suche nach der letzten Sicherung ungemein, wenn es mal drauf ankommt :-)

Um zu verhindern, dass der hier lästige Auto-mounter sich die Partitionen einbindet, hilft zumindest bei Ubuntu folgender Eintrag in /etc/fstab:

# Prevent mounting backup partitions on external hd
UUID=944358b6-b1fd-42a3-9d4c-be0b8d69e51f    none    ext3    ro,noauto

Die UUID der Partition lassen sich mittels des blkid Kommandos leicht ermitteln.

Die Sicherung selbst bearbeitet alle im Verzeichnis /backup vorhandenen symbolischen Links. Dadurch können die eigentlichen Daten irgendwo im Dateisystem liegen. Es reicht, im Verzeichnis /backup einen Symlink auf das zu sichernde Verzeichnis zu setzen. Das kann ein Home-Verzeichnis sein oder eine dirvish-Bank oder sonst irgendwas. Natürlich wird nicht der Symlink selbst gesichert, sondern das Verzeichnis/die Datei auf das/die der Symlink zeigt.

Wichtig bei der Sicherung von Verzeichnissen mit vielen Links (dirvish arbeitet zur Deduplizierung mit hardlinks) ist, dass auch das Dateisystem auf der externen Platte eine genügend große I-Node Anzahl bei der Formatierung bekommen hat (Parameter -N bei mkfs.ext{2|3|4}).

Im Skript ist noch auf das setsid Kommando hinzuweisen. Durch den Hotplug-Mechanismus von udev ausgeführte Kommandos/Skripte – hier das RUN-Argument in der udev-Regel – sind in ihrer Ausführungszeit per default auf 120s begrenzt und werden bei Überschreitung dieser Zeit vom udev-daemon hart beendet. Durch setsid und den folgenden Selbst-Aufruf des Skriptes wird eine neue, von udev unabhängige Prozessgruppe gestartet. Erst damit funktioniert es auch bei längeren Laufzeiten des Backups :-)


Vorbereitung eines Mediums

  1. externe USB-Platte/externen Memory-Stick anstecken
  2. Partition anlegen (die Sicherung erfolgt immer auf die erste Partition)
  3. ext-Dateisystem aufbringen, Anmerkungen zur I-Node Anzahl beachten
  4. Markerdatei schreiben, Platte ggf. vorher mounten, danach umounten
  5. Hardware-spezifische udev-Regel eintragen, danach sudo udevadm control --reload

Das Backup in Aktion

  1. externe Platte anstecken
  2. Genügend lange warten, bis die Platte nicht mehr aktiv ist
  3. externe Platte abziehen und sicher lagern

Auf eine Signalisierung des Backup-Endes wurde verzichtet, weil es derzeit einfach nicht benötigt wird. Die externe Platte wird morgens angesteckt und abends abgezogen, das muss reichen.

Update: und dann kam systemd…

Das Update auf ein Ubuntu 16.04 LTS funktionierte zunächst reibungslos, Probleme beim Backup hatte ich nicht erwartet. Um so größer das Erstaunen, dass der Backup-Job mittendrin abbrach. Das Problem war schnell identifiziert, das Verlagern des Jobs in eine neue Prozessgruppe mit setsid funkionierte irgendwie nicht mehr, dadurch killte udev das Skript genau nach 180s Laufzeit :-(

Stattdessen kommt nun eine zu systemd kompatible Lösung zum Einsatz. Das Skript wurde nur leicht angepasst, ebenso die udev-Regel und eine service-datei für systemd kam hinzu.

udev-Regel (mit systemd Support)

# Platte erkennen und Backup-Partition einbinden und Backup starten
KERNEL=="sd?1", SUBSYSTEMS=="usb", ATTRS{idVendor}=="152d", ATTRS{idProduct}=="2509", SYMLINK+="backup", ACTION=="add", TAG+="systemd", ENV{SYSTEMD_WANTS}="dobackup.service"

Service-Definition für systemd: /etc/systemd/system/dobackup.service

[Unit]
Description=backup script

[Service]
Type=oneshot
ExecStart=/backup/dobackup.sh

Update2: einfaches Ausschalten des Automatismus

Manchmal möchte man ja manuell irgendwas auf einer der benutzten externen Platten erledigen. Dazu reicht es nun, im /backup-Verzeichnis eine Datei mit dem Namen NOBACKUP anzulegen. Solange diese Datei vorhanden ist, wird das automatische Backup nicht ausgeführt.