Backups mit Restic und Raspberry Pi
Leserkommentare zum letzten Artikel (Backups mit Borg und Raspberry Pi) haben mich dazu gebracht, das Setup noch etwas zu verfeinern. Dazu habe ich aus Geschwindigkeitsgründen Borg über Bord geworfen und durch Restic sowie den dazugehörigen REST-Server ersetzt. Außerdem kommt zum automatischen Einhängen der Backup-Festplatte nun systemd anstelle von autofs zum Einsatz.
REST-Server (Raspberry Pi)
Der REST-Server ist eine schnelle Alternative zu SSH als Übertragungsprotokoll und muss auf dem Raspberry Pi als Serverkomponente von Restic installiert werden. Im Heimnetz können wir auch erst mal auf eine TLS-Verschlüsselung verzichten – das Backup selbst ist ja immerhin schon verschlüsselt.
Damit der Dienst nicht mit root-Rechten laufen muss, erstellen wir einen User restic, der sich nicht anmelden können muss:
# useradd -s /usr/sbin/nologin restic
Glücklicherweise gibt es vom REST-Server auch Binaries für die ARM Architektur des Pi. Wir laden also das Binary herunter, entpacken es und kopieren es nach /usr/local/bin/:
# wget https://github.com/restic/rest-server/releases/download/v0.12.0/rest-server_0.12.0_linux_arm64.tar.gz
# tar xzf rest-server_0.12.0_linux_arm64.tar.gz
# mv rest-server_0.12.0_linux_arm64/rest-server /usr/local/bin/
Damit der REST-Server automatisch gestartet wird und mit den Berechtigungen des erstellten restic-Users läuft, erstellen wir eine systemd-Datei und plazieren sie unter /etc/systemd/system/rest-server.service. Die Datei hat folgenden Inhalt:
[Unit]
Description=Rest Server
After=syslog.target
After=network.target
# if you want to use socket activation, make sure to require the socket here
#Requires=rest-server.socket
[Service]
Type=simple
# You may prefer to use a different user or group on your system.
User=restic
Group=restic
ExecStart=/usr/local/bin/rest-server --path /srv/restic --no-auth
Restart=always
RestartSec=5
# The following options are available (in systemd v247) to restrict the
# actions of the rest-server.
# As a whole, the purpose of these are to provide an additional layer of
# security by mitigating any unknown security vulnerabilities which may exist
# in rest-server or in the libraries, tools and operating system components
# which it relies upon.
# IMPORTANT!
# The following line must be customised to your individual requirements.
ReadWritePaths=/srv/restic
# Makes created files group-readable, but inaccessible by others
UMask=027
# If your system doesn't support all of the features below (e.g. because of
# the use of an older version of systemd), you may wish to comment-out
# some of the lines below as appropriate.
CapabilityBoundingSet=
LockPersonality=true
MemoryDenyWriteExecute=true
NoNewPrivileges=yes
PrivateTmp=yes
PrivateDevices=true
PrivateUsers=true
ProtectSystem=strict
ProtectHome=yes
ProtectClock=true
ProtectControlGroups=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true
ProtectProc=invisible
ProtectHostname=true
RemoveIPC=true
RestrictNamespaces=true
RestrictAddressFamilies=AF_INET AF_INET6
RestrictSUIDSGID=true
RestrictRealtime=true
# if your service crashes with "code=killed, status=31/SYS", you probably tried to run linux_i386 (32bit) binary on a amd64 host
SystemCallArchitectures=native
SystemCallFilter=@system-service
# Additionally, you may wish to use some of the systemd options documented in
# systemd.resource-control(5) to limit the CPU, memory, file-system I/O and
# network I/O that the rest-server is permitted to consume according to the
# individual requirements of your installation.
#CPUQuota=25%
#MemoryHigh=bytes
#MemoryMax=bytes
#MemorySwapMax=bytes
#TasksMax=N
#IOReadBandwidthMax=device bytes
#IOWriteBandwidthMax=device bytes
#IOReadIOPSMax=device IOPS, IOWriteIOPSMax=device IOPS
#IPAccounting=true
#IPAddressAllow=
[Install]
WantedBy=multi-user.target
Wie man sieht, habe ich hier --path /srv/restic
und ReadWritePaths=/srv/restic
angegeben. Das ist der Pfad zum Restic-Repository bzw. zu der Festplatte, die wir – wie im letzten Artikel – automatisch dann mounten, wenn darauf zugegriffen wird. Anstelle von autofs machen wir das dieses Mal aber mit systemd-Bordmitteln. Dazu Bedarf es nur eines Eintrages in /etc/fstab:
LABEL="Restic" /srv/restic ext4 defaults,noatime,x-systemd.automount,x-systemd.idle-timeout=10 0 0
Das implizert natürlich, dass die Festplatte eine ext4-Partition mit dem Label „Restic“ hat (etwa: mkfs.ext4 -L Restic -m 2 /dev/sda1
– Vorsicht, Daten gehen verloren!). Außerdem muss der restic-User darauf Lese-/Schreibrechte haben.
Jetzt müssen systemd noch die Änderungen mitgeteilt und die Automountfunktion gestartet werden. Außerdem wird der REST-Server aktiviert und gestartet:
# systemctl daemon-reload
# systemctl start srv-restic.mount
# systemctl enable --now rest-server
Der REST-Server ist nun aktiv lauscht an Port 8000 auf Restic-Kommandos.
Restic (Client)
Um immer die aktuellste Resic-Version zu haben, bietet es sich an, diese ebenfalls als Binary von der Github-Seite herunterzuladen. Ausführbar gemacht wird die Datei dann nach /usr/local/bin/ verschoben:
# wget https://github.com/restic/restic/releases/download/v0.15.2/restic_0.15.2_linux_amd64.bz2
# bunzip2 restic_0.15.2_linux_amd64.bz2
# chmod +x restic_0.15.2_linux_amd64
# mv restic_0.15.2_linux_amd64 /usr/local/bin/restic
Das Backup-Skript aus dem Borg-Artikel muss natürlich etwas angepasst werden. Bei der Gelegenheit wurde es etwas verschlankt und das Passwort ist jetzt nicht mehr direkt enthalten. Vielmehr wurde es in die Datei /root/.restic.pw ausgelagert. Diese gehört root:root und ist mittels chmod 600 /root/.restic.pw
auch nur für diesen lesbar. Auf einem Notebook/PC mit verschlüsselter Platte ist das für mich akzeptabel. Das Backup-Skript selbst kann dann beispielsweise so aussehen:
#!/usr/bin/env bash
export RESTIC_REPOSITORY=rest:http://192.168.178.52:8000
# some helpers and error handling:
info() { printf "\n%s %s\n\n" "$( date )" "$*" >&2; }
usage() {
echo "usage: backup_restic.sh [-t [home | system]] | [-h]"
}
backup_home() {
export SNAPSHOT_PATH=/.snapshots/restic-home
btrfs subvolume snapshot -r /home $SNAPSHOT_PATH
sync
cd $SNAPSHOT_PATH
restic backup \
--host `hostname` \
--exclude-caches \
--exclude '*/.cache/*' \
--tag home \
--one-file-system \
--password-file $HOME/.restic.pw \
--cleanup-cache \
.
cd /tmp
btrfs subvolume delete $SNAPSHOT_PATH
restic forget \
--prune \
--host `hostname` \
--tag home \
--password-file $HOME/.restic.pw \
--keep-daily 7 \
--keep-weekly 2 \
--keep-monthly 3
}
backup_system() {
export SNAPSHOT_PATH=/.snapshots/restic-system
btrfs subvolume snapshot -r / $SNAPSHOT_PATH
sync
cd $SNAPSHOT_PATH
# Backup the home directories into an archive named after
# the machine this script is currently running on:
restic backup \
--exclude-caches \
--exclude 'root/.cache' \
--exclude 'lost+found' \
--tag system \
--one-file-system \
--password-file $HOME/.restic.pw \
--cleanup-cache \
. /boot /boot/efi
cd /tmp
btrfs subvolume delete /.snapshots/restic-system
restic forget \
--prune \
--host `hostname` \
--tag system \
--password-file $HOME/.restic.pw \
--keep-daily 7 \
--keep-weekly 2 \
--keep-monthly 3
}
## MAIN
bkuptype=""
while [ "$1" != "" ]; do
case $1 in
-t | --type ) shift
bkuptype=$1
;;
-h | --help ) usage
exit
;;
* ) usage
exit 1
esac
shift
done
if [ "$bkuptype" == "home" ]; then
backup_home
elif [ "$bkuptype" == "system" ]; then
backup_system
else
usage
fi
Das Repository muss vorher natürlich einmalig initialisiert werden. Das können wir gut händisch auf dem Client-Rechner erledigen:
# restic -r rest:http://mypiserver:8000 -p /root/.restic.pw init
Eine Bemerkung noch zu RESTIC_REPOSITORY
. Hier habe ich bewusst die IPv4-Adresse des Raspberry Pi angegeben. Zunächst hatte ich hier den Hostnamen angegeben. Die Verbindung für ein (umfangreiches) Backup wurde dann über die IPv6-Adresse aufgebaut. Über Nacht scheint sich diese jedoch geändert zu haben1 und der Backupvorgang konnte nicht abgeschlossen werden. Das war zwar kein großes Problem, weil eine erneute Ausführung an der unterbrochenen Stelle einfach weiter gemacht hat, aber unschön war das auf jeden Fall. Wenn man hier einfach die statische IPv4-Adresse angibt, umgeht man dieses Problem.
Übrigens kann man bei Restic problemlos ein und dasselbe Repository für mehrere Rechner verwenden. Restics Deduplizierung funktioniert dann auch über Rechnergrenzen hinweg. Ein Feature, das Borg meines Wissens nach so nicht bietet.
Für weitere Restic-Befehle sei auf die umfangreiche Dokumentation des Projekts verwiesen.
-
So jedenfalls meine Vermutung. Das Borg-Backup hatte übrigens das gleiche Problem. ↩︎