‹ jan0sch.de

ZFS-Snapshots für die Datensicherung mit FreeBSD

2018-12-21

Das Thema Datensicherung (Backup) ist ein großes und jeder, der sich ernsthafter mit Computertechnik beschäftigt, sollte damit Berührung gehabt haben. Im Guten wie im Schlechten. ;-)

In dieser kleinen Anleitung möchte ich kurz beschreiben wie man mit wenig Aufwand in die Lage versetzt wird die Snapshots von ZFS zu nutzen, um verschlüsselte Sicherungen auf eine externe Festplatte zu ziehen.

Voraussetzungen

Diese Anleitung bezieht sich ausdrücklich auf die Nutzung von FreeBSD! Theoretisch dürfte das aber auch alles mit Linux funktionieren.

Das verwendete Dateisystem muß ZFS und via GELI verschlüsselt sein. Der Installer von FreeBSD bietet diese Option an, es ist also kein “Hexenwerk” ein solches System zu haben.

Einrichtung der externen Festplatte

Die externe Festplatte sollte ausreichend groß sein und eine entsprechend schnelle Schnittstelle haben (USB 3.0). Da im weiteren Verlauf die Sicherungen inkrementell durchgeführt werden, ist Geschwindigkeit nicht mehr ganz so wichtig, wenn der erste Datensatz erstmal gesichert ist und die Abstände zwischen den Sicherungen nicht zu groß sind.

Partitionierung

Für die folgenden Befehle gehe ich davon aus, daß die externe Platte als Gerät /dev/da4 erkannt wurde. Es werden alle etwaigen Daten auf der Platte gelöscht. Zusätzlich gelten die folgenden Annahmen:

  1. Name des zu sichernden Pools: zroot
  2. Name des Backup-Pools: backup

Zunächst legen wir ein GTP Partitionierungsschema auf der Platte an und erstellen eine Partition für ein FreeBSD-ZFS.

# gpart create -s GPT /dev/da4
# gpart add -t freebsd-zfs -l backup-vol da4
# gpart show

Der letzte Befehl gpart show sollte die Partitionierungsinformationen ausgeben.

Verschlüsselung

Der folgenden Befehl initialisert die angelegte Partition mit der Standardverschlüsselung von GELI bei einer Schlüssellänge von 256 Bit.

# geli init -l 256 /dev/gpt/backup-vol

Bitte verwendet ein gutes Paßwort und verliert es nicht! Es gibt noch alternative Methoden (Schlüsseldatei), die man in der Anleitung von GELI nachlesen kann.

ZFS einrichten

Jetzt muß die Partition eingebunden und ZFS entsprechend eingerichtet werden:

# geli attach /dev/gpt/backup-vol
# zpool create backup /dev/gpt/backup-vol.eli
# zfs create -o compress=lz4 backup/home

Diese Befehle entschlüsseln die Partition, erstellen einen Pool für ZFS darauf und einen ZFS-Datensatz, den wir später für die Sicherung von Snapshots verwenden. Falls die Option zstd für die Komprimierung verfügbar ist, sollte sie verwendet werden, da sie im Vergleich zu lz4 erheblich bessere Performance bietet!

Initialen Snapshot erstellen und sichern

Damit die zukünftigen Sicherungen inkrementell erfolgen können, muß erst ein initialer Datensatz angelegt werden.

Einen solchen erstellt man mittels des folgenden Befehls für den Datensatz /usr/home:

# zfs snapshot "zroot/user/home@2018-12-21"

Der Teil hinter dem Klammeraffen (@) ist der Name des Snapshots, hier ein Datum in ISO-Notation.

Anschließend muß der Snapshot auf die externe Platte überspielt werden, was eine Weile dauern kann und mittels des folgenden Befehls geschieht:

# zfs send "zroot/usr/home@2018-12-21" | zfs receive -F "backup/home@2018-12-21"

Vorhandene Snapshots kann man sich mit dem Befehl zfs list -t snapshot anzeigen lassen.

Zu guter Letzt speichern wir noch das Datum der letzten Sicherung als “User Property” im ZFS-Pool, den wir für die Backups verwenden.

# zfs set backup:latest=2018-12-21 backup

Insofern wir das alles auch als normale Benutzer ausführen wollen, sollten wir die entsprechenden Rechte setzen:

# zfs allow -u USER send,snapshot zroot
# zfs allow -u USER compression,mountpoint,mount,create,receive,userprop backup

Skript für die regelmäßige Sicherung

Das folgende Skript kann regelmäßig ausgeführt werden, um Daten zu sichern. Vorher muß jedoch der ZFS-Pool der externen Platte eingebunden werden.

#!/bin/sh
#
# Script for Snapshot based backups via ZFS.
#
# Permissions needed for source pool:
#   zfs allow -u USER send,snapshot SOURCE_POOL
# Permissions needed for target pool:
#   zfs allow -u USER compression,mountpoint,mount,create,receive,userprop BACKUP_POOL

SOURCE_POOL="zroot"
BACKUP_POOL="backup"
LATEST_PROP="backup:latest"

zpool get -Hp -o value free ${BACKUP_POOL} >/dev/null 2>&1 || {
  echo "ZFS backup pool (${BACKUP_POOL}) not found!"
  exit 1
}

LATEST=$(zfs get -H -o value ${LATEST_PROP} ${BACKUP_POOL})

if [ ${LATEST} == "-" ]; then
  echo "No property '${LATEST_PROP}' with last backup date found in backup pool ('${BACKUP_POOL}')!"
  echo "If you don't have a last backup please create one using the following commands:"
  echo '# SNAPSHOT=$(date +"%F")'
  echo "# zfs snapshot ${SOURCE_POOL}/usr/home@\${SNAPSHOT}"
  echo "# zfs send ${SOURCE_POOL}/usr/home@\${SNAPSHOT} | zfs receive -F ${BACKUP_POOL}/home@\${SNAPSHOT}"
  echo "# zfs set ${LATEST_PROP}=\${SNAPSHOT} ${BACKUP_POOL}"
  echo "If the script is run as a regular user then please ensure the following permissions are set:"
  echo "- send,snapshot on the SOURCE_POOL"
  echo "- compression,mountpoint,mount,create,receive,userprop on the BACKUP_POOL"
  exit 1
else
  SNAPSHOT=$(date +"%F")
  if [ ${LATEST} == ${SNAPSHOT} ]; then
    echo "Last backup is identical with current snapshot (${SNAPSHOT})!"
    echo "Please investigate and fix manually."
    exit 1
  else
    echo "Going to start incremental snapshot backup from ${LATEST} to ${SNAPSHOT} in 15 seconds."
    echo "If unsure, press Ctrl+C now..."
    sleep 15
    zfs snapshot "${SOURCE_POOL}/usr/home@${SNAPSHOT}"
    echo "Snapshot complete, sending to backup device..."
    zfs send -i ${LATEST} "${SOURCE_POOL}/usr/home@${SNAPSHOT}" | zfs receive -F "${BACKUP_POOL}/home@$SNAPSHOT"
    echo "Setting ${LATEST_PROP} on ${BACKUP_POOL} pool."
    zfs set ${LATEST_PROP}=${SNAPSHOT} ${BACKUP_POOL}
    echo "Operation complete. Please also backup important system files and directories like /boot /etc and /usr/local/etc."
    echo "Remember to export the zpool and detach the geli device before unplugging!"
  fi
fi

Aus- und wiedereinhängen der Platte und des ZFS-Pools

Nach erfolgter Datensicherung kann der Pool von ZFS exportiert, GELI ausgehängt und anschließend die Festplatte abgezogen werden.

Für die ersten beiden Punkte sind folgende Befehle zuständig:

# zpool export backup
# geli detach /dev/gpt/backup-vol.eli

Für das Einhängen gehen wir in folgendermaßen vor:

# geli attach /dev/gpt/backup-vol
# zpool import backup