Append-Only Backups mit restic

Ich wollte meine Daten sichern. Klingt einfach, aber man kann einiges falsch machen.

Im Text geht es um folgende Fragen:

  1. Welche Sicherungsstrategie passt zu meinen Bedürfnissen?
  2. Welche Backup-Software ist die richtige?
  3. Wo kann ich mein Backup speichern?

Am Ende werde ich meine aktuelle Konfiguration vorstellen.

Strategie

Eine Backup-Strategie beschäftigt sich zwangsläufig mit der Wiederherstellung von Daten. Daher ist der erste Schritt die Überlegung, vor welchen Verlusten man sich schützen will: Ein Datenträger funktioniert nicht mehr? Das Backup des Datenträgers auch nicht mehr? Vielleicht wurde der Datenträger gestohlen?
Abgesehen vom Festplattenausfall gibt es noch andere Überlegungen, wie z. B. den Ort an dem die Datenträger oder Backups aufbewahrt werden, oder Umweltkatastrophen oder andere Unglücke.

Man sollte daher überlegen wie lange welche Daten gespeichert werden sollen und dann eine Lösung finden, die das Risiko des Datenverlustes im akzeptablen Bereich hält.

Ein guter Ausgangspunkt für eine Backup-Strategie ist die 3-2-1-Regel  1 : Drei Kopien, die Version auf der man arbeitet und zwei Backups; dabei sollte man zwei verschiedene Typen an Datenträgern verwenden; und eine Kopie an einem anderen Ort aufbewahren.

Für mich bedeutet das, dass die Daten, die sich auf den Festplatten meines Computers und meines Servers befinden, auf meinem NAS und einem Cloud-Speicher gespiegelt werden sollen.

Auf diese Weise verfüge ich über mindestens drei Kopien (PC/Server, NAS, Cloud) auf zwei Medien (Festplatten und Cloud-Speicher) sowie über Offsite-Backups. Dies sollte mich vor Datenverlusten, dem Ausfall des Cloud-Anbieters und Katastrophen bewahren.

Software

Überblick

Nachdem der Speicherort geklärt ist, sollte man sich Gedanken über die Software machen.

Es gibt viele Möglichkeiten, wie man Backups speichern kann: Eine einfache Kopie, komprimierte Archive, inkrementelle Backups oder deduplizierte Archive.

Eine einfache Kopie nimmt viel Speicherplatz in Anspruch. Komprimierte Archive sparen etwas davon, aber man muss immer noch jede Version der Dateien separat speichern, was eine enorme Ressourcenverschwendung darstellt. Inkrementelle Backups sparen Platz, da nur die Änderungen an einem vollständigen Backup gespeichert werden, aber wenn ein inkrementelles Backups fehlschlägt, kann das gesamt Backup von diesem Zeitpunkt an beschädigt sein.

Ein guter Kompromiss sind deduplizierende Backups. Dabei handelt es sich im Grunde um Vollsicherungen, bei denen die Daten jedoch in Stücke, sogenannte Chunks, aufgeteilt werden. Wenn es doppelte Chunks gibt, z. B. dieselbe Datei zu zwei verschiedenen Zeitpunkten, werden sie nur einmal gespeichert. [1] Wenn man seine Daten wiederherstellen will, werden die Chunks gelesen und zu den ursprünglichen Dateien zusammengesetzt. Auch hier gibt es einige Gefahren, wie z.B. Bitflips, die einige Chunks verändern können. Allerdings sollten sie nur eine Datei betreffen (und können manchmal repariert werden). Ein weiterer Punkt den man in Betracht ziehen sollte ist die Verschlüsselung des Backups, vor allem wenn man die Backups auf fremden Computern (z.B. in der Cloud) speichert. Eine Verschlüsselung ist eine gute Möglichkeit um sicherzustellen, dass niemand gesicherte Daten lesen kann. Sollte man den Schlüssel verlieren, ist es allerdings auch eine gute Möglichkeit, die eigenen Daten unlesbar zu machen.

Restic

Die Wahl fällt also auf ein dedupliziertes und verschlüsseltes Backup. Es gibt einige Optionen, für mich kamen aber nur zwei in Frage: borg oder restic. Letztendlich habe ich mich für restic entschieden, da die Verschlüsselung von borg etwas schwächer ist, wenn mehrere Clients dasselbe Repository aktualisieren.  2

Restic macht alles, was ich brauche, ist eine einzige Binärdatei und hat ein explizites Threadmodel, das mit meinem übereinstimmt.

Ein großer Nachteil ist die Schlüsselverwaltung. In Restic verschlüsselt das Passwort die Daten nicht direkt, sondern eine Schlüsseldatei. Diese Schlüsseldatei enthält die Daten zur Erstellung des Schlüssels für die eigentliche Verschlüsselung der Dateien. Das bedeutet, dass ein Angreifer, sobald er eine entschlüsselte Schlüsseldatei hat, alle Daten im Repository für immer lesen kann, selbst wenn sein Schlüssel widerrufen wird. Das ist natürlich nur dann ein Problem, wenn der Angreifer Zugriff auf das Backups hat, aber dennoch sollte man dies im Hinterkopf behalten.

Speicheranbieter

Da ein Angreifer, der auf meine Restic-Schlüssel zugreifen kann, bereits auf alle Daten auf meinem System zugreifen kann, ist ein Schutz vor dem Lesen des Repositorys ziemlich sinnlos. Ich kann mich aber vor Löschen oder Überschreiben mit einem Backup schützen, dass eben das nicht zulässt.

Es gibt zwei Möglichkeiten, dies zu erreichen: mit einem VPS mit genügend Speicherplatz und rclone (siehe: ruderich.org) oder mit einem Speicheranbieter mit den richtigen Sicherheitsoptionen. Ein VPS nur für Backups hätte zu einer Menge manueller Arbeit geführt, da ich ihn aus meiner üblichen Infrastruktur heraushalten wollte. Daher habe ich mich für einen verwalteten Speicher entschieden.

Ich habe eine ganze Reihe von Anbietern in Betracht gezogen, bei denen ich meine Backups speichern wollte. Auf der Liste standen:

  • Hetzner Storage Box
  • Scaleway
  • OVH
  • rsync.net
  • Backblaze
  • Wasabi
  • AWS
  • Ein VPS

Mein Ziel war es, einen europäischen Speicheranbieter mit S3-Versionierung und IAM-Zugriffsrichtlinien zu finden. Eine weitere Anforderung war die Möglichkeit, das Löschen auf Unterverzeichnisse (locks/*) und das Löschen nicht aktueller Objektversionen zu beschränken.

Leider (und ich lasse mich hier gerne eines besseren belehren) verfügt nur AWS über diese Funktionen. Zwei kamen nahe:

  • OVH verfügt über IAM-Zugriffsrichtlinien (die allerdings noch etwas überarbeitet werden müssen), aber keine Versionierung.
  • Scaleway stellt Versionierungen und grundlegende IAM-Verwaltung bereit (allerdings ohne Berechtigungen auf bestimmte Pfade zu beschränken), aber keine DeleteObjectVersion-Beschränkung. Das bedeutet das ein versioniertes Objekt von jeder Person mit DeleteObject-Berechtigung gelöscht werden kann. Das war mir etwas zu wenig.

Es wird also AWS. Ich bin nicht ganz zufrieden, aber da restics Verschlüsselung in Ordnung zu sein scheint, werde ich es für meine Server-Backups verwenden. Mein persönliches Backup möchte ich AWS nicht anvertrauen.

Endgültige Konfiguration

Die endgültige Konfiguration besteht aus einem S3 Bucket mit aktivierter Versionierung und einem entsprechenden Benutzer mit den folgenden Berechtigungen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "DeleteLocks",
            "Effect": "Allow",
            "Action": "s3:DeleteObject",
            "Resource": "arn:aws:s3:::my-restic-bucket/locks/*",
            "Condition": {
                "ForAnyValue:IpAddress": {
                    "aws:SourceIp": [
                        "1.2.3.4/32",
                        "a:b:c:d::/64"
                    ]
                }
            }
        },
        {
            "Sid": "AllowListings",
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket",
                "s3:GetBucketLocation"
            ],
            "Resource": "arn:aws:s3:::my-restic-bucket",
            "Condition": {
                "ForAnyValue:IpAddress": {
                    "aws:SourceIp": [
                        "1.2.3.4/32",
                        "a:b:c:d::/64"
                    ]
                }
            }
        },
        {
            "Sid": "AllowReadWrite",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::my-restic-bucket/*",
            "Condition": {
                "ForAnyValue:IpAddress": {
                    "aws:SourceIp": [
                        "1.2.3.4/32",
                        "a:b:c:d::/64"
                    ]
                }
            }
        }
    ]
}

Diese Konfiguration ermöglicht das Schreiben (und Überschreiben, aber dafür ist die Versionierung da) und Auflisten, aber das Löschen von Objekten nur im Ordner locks. Außerdem wird nach der Quell-IP gefiltert, so dass nur der sichernde Server auf das Repository zugreifen kann.

Wenn ich restics forget nutzen will, also ein Backup löschen möchte, erhalte ich die folgende Fehlermeldung: Remove(<snapshot/7642d7e379>) returned error, retrying after 1.080381816s: client.RemoveObject: Access Denied.

Das automatische Backup erfolgt über einen Cron-Job, der einmal pro Tag folgendes Skript ausführt:

1
2
3
4
5
6
7
8
#!/bin/bash

for env in "/root/backup-envs"/*; do
    echo "Backup for $env"
    source $env

    restic backup your/backup/directory
done

Die Dateien in backup-envs sehen so wie folgt. Prinzipiell kann natürlich jedes beliebige Backend verwendet werden. Das Repository muss nur bereits initialisiert sein.

1
2
3
4
5
6
7
#!/bin/bash

export AWS_ACCESS_KEY_ID='xxxxx'
export AWS_SECRET_ACCESS_KEY='xxxxxxx'
export AWS_DEFAULT_REGION='eu-central-1'
export RESTIC_REPOSITORY='s3:https://s3.amazonaws.com/my-restic-repo'
export RESTIC_PASSWORD='xxxxx'

Abschließend habe ich auch einige S3 lifecycle rules implementiert: Ich lösche alle Versionen nicht aktueller Locks, entferne mehrteilige Uploads, die älter als eine Woche sind, und verschiebe alle non-current Objekte und das Verzeichnis data in die Glacier Instant Retrieval-Klasse.

Die Zugriffsprotokollierung ist ebenfalls aktiviert.

Möchte ich auf die Daten zugreifen melde ich mich entweder von meinem Server aus an, oder deaktiviere die IP-Beschränkung für AWS kurzzeitig.

Die Kosten bleiben aktuell im Cent-Bereich, allerdings ist mein Backup noch unter 10 GB groß.

Anmerkungen

1.
Das bedeutet auch, dass es manchmal besser ist die Dateien nicht zu komprimieren, bevor sie gesichert werden.

Literatur

1:
Ruggiero & Heckathorn (2012)a
2:
BorgBackup (n.d.)a