Archiv-Scriptlets (tar, openssl, mbuffer)

Manuell mit tar Daten auf Tapes zu schreiben erscheint mit Sicherheit archaisch und manche werden zu Recht an ihre Anfangsjahre in der IT vor vielen Jahrzehnten denken. Doch auch in Zeiten von VEEAM und Cloud-Sicherungen kann es durchaus sinnvoll (und spannend!) sein, Daten auf die althergebrachte Art zu sichern. Seien es Kostengründe, sehr spezifische Anforderungen an Archiv-Lösungen oder einfach der Spieltrieb und der Spaß an steilen Lernkurven, manchmal darf es einfach auch mal bash und Tape sein. Nachfolgend findet ihr ein paar spannende Codeschnipsel, die sich um Bandarchivierung auf ein oder mehrere Tapes, automatischen Tapewechsel und Inline-Verschlüsselung drehen.

TAR-Archiv inline verschlüsseln und auf Tape schreiben

Es gibt Situationen, in denen man nicht genug Speicher frei hat, um bestehende Dateien erst einmal in ein Archiv zu schreiben, dieses zu verschlüsseln und die resultierende Datei dann auf Band zu schreiben. Zeitweise belegt man je nach Vorgehensweise bis zu zwei- oder dreimal so viel Speicher auf dem Datenträger wie die ursprünglichen Daten groß sind. Eleganter ist es, alles in einem großen zusammengefügten Schritt auszuführen, doch hier gibt es einige Fallstricke. Da ich die inzwischen alle durchgespielt zu scheinen habe, kann ich an dieser Stelle einfach das Ergebnis verraten, wie es halbwegs problemlos funktioniert.

Zuerst generiert man ein 214 Byte langes Passwort (der scheinbar krumme Wert wird hier erklärt) und speichert es in einer Datei. Wahlweise kann auch das Passwort direkt verwendet werden, hierzu bitte im Manual von openssl nachschauen. Wichtig ist, eine Blockchiffre zu verwenden. AES-256 eignet sich besonders gut, da moderne Prozessoren die Verschlüsselung sehr schnell ohne großartige Geschwindigkeitseinbußen erledigen können.

Ich gehe im folgenden Beispiel davon aus, dass ein Verzeichnis Pfad/zu/Quellordner existiert, in dem der zu sichernde Ordner Source liegt, den ich sichern will. Sowohl in diesem als auch in den darauf folgenden Beispielen kann /Pfad/zum/Tapedevice sowohl ein Bandlaufwerk als auch eine reguläre Datei sein.

openssl rand 214 > "/Pfad/zu/Passwort/in/Datei"          # Passwort-File zum Verschlüsseln erstellen
tar -c \                                                 # -c: TAR-Archiv erstellen
  -C "/Pfad/zu/Quellordner" \                            # -C: Vor der Archivierung ins angegebene Verzeichnis wechseln
  -b 1024 \                                              # -b: Die Blockgröße der zu schreibenden Dateien
  -f - \                                                 # -f: In angegebenes Archiv schreiben, - steht für stdout
  "./Source" \                                           # Zu archivierende(s) Verzeichnis(se) oder Datei(en)
  | openssl enc \                                        # enc: openssl im Verschlüsselungsmodus starten
    -aes-256-cbc \                                       # -aes-256-cbc: Mit dem angegebenen Algorithmus verschlüsseln
    -e \                                                 # -e: Verschlüsseln
    -salt \                                              # -salt: Dem Passwort wird eine zufällige Zeichenfolge hinzugefügt
    -pbkdf2 \                                            # -pbkdf2: pbkdf2 wird genutzt, um vom Passwort-File den Key abzuleiten
    -iter 20000 \                                        # -iter: Die Key-Generierung wird so oft wiederholt
    -pass file:"/Pfad/zu/Passwort/in/Datei" \            # -pass: Passwort oder Keyfile zur Verschlüsselung angeben
  | mbuffer -v 0 \                                       # -v: Verbosität der Ausgaben
    -m 1G \                                              # -m: Wie viele Bytes an Buffer verwendet werden
    -s 256k \                                            # -s: Blockgröße der Daten festlegen
    -P 95% \                                             # -P: Angestrebte Buffer-Füllmenge, bei der das Schreiben gestartet wird
    -f \                                                 # -f: Ausgabedatei überschreiben, sollte diese schon existieren
    -q \                                                 # -q: Keine Statistik-Ausgabe während des Schreibens
    -o "/Pfad/zu/Tapedevice"                             # -o: Wohin der Stream geschrieben wird

Bitte entferne vor dem Verwenden dieses Codeschnipsels die Kommentare am Zeilenende! Diese dienen hier nur der Erklärung.

Verschlüsselte Daten von Tape wiederherstellen

Zur Entschlüsselung wird das im ersten Snippet generierte Keyfile verwendet. Bitte immer darauf achten, bestehende Keyfiles nicht zu überschreiben, sonst droht Datenverlust!

Die extrahierten Dateien sollen im Ordner /Pfad/zu/Zielordner landen.

mbuffer -v 0 \                                           # -v: Verbosität der Ausgaben
  -m 1G \                                                # -m: Wie viele Bytes an Buffer verwendet werden
  -s 256k \                                              # -s: Blockgröße der Daten festlegen
  -P 95% \                                               # -P: Angestrebte Buffer-Füllmenge, bei der das Schreiben gestartet wird
  -f \                                                   # -f: Ausgabedatei überschreiben, sollte diese schon existieren
  -q \                                                   # -q: Keine Statistik-Ausgabe während des Schreibens
  -i "/Pfad/zu/Tapedevice" \                             # -i: Aus welcher Quelle der Stream gelesen wird
  -o - \                                                 # -o: Wohin der Stream geschrieben wird, - steht für stdout
  | openssl enc \                                        # enc: openssl im Verschlüsselungsmodus starten
    -aes-256-cbc \                                       # -aes-256-cbc: Mit dem angegebenen Algorithmus entschlüsseln
    -d \                                                 # -d: Entschlüsseln
    -iter 20000 \                                        # -iter: Die Key-Generierung wird so oft wiederholt
    -pass file:"/Pfad/zu/Passwort/in/Datei" \            # -pass: Passwort oder Keyfile zur Entschlüsselung angeben
  | tar -x \                                             # -x: TAR-Archiv entpacken
    -f - \                                               # -f: Von angegebenem Archiv entpacken, - steht für stdin
    -b 1024 \                                            # -b: Die Blockgröße der gelesenen Dateien
    -C "/Pfad/zu/Zielordner"                             # -C: Vor dem Entpacken ins angegebene Verzeichnis wechseln

Beim Entpacken braucht man nicht zwingend den openssl-Parameter -salt, denn dieser wird aus dem verschlüsselten File automatisch ausgelesen. Dasselbe gilt für -pbkdf2.

Bitte entferne vor dem Verwenden dieses Codeschnipsels die Kommentare am Zeilenende! Diese dienen hier nur der Erklärung.

Daten verschlüsselt auf mehrere Tapes schreiben

Das Schreiben auf mehrere Tapes (“Multitape”), wenn Daten nicht auf ein einzelnes Tape passen sollten, ist rein mit tar relativ simpel. Der Parameter -M aktiviert die Multitape-Funktionalität, die am Ende eines Tapes eine Nachricht anzeigt, man möge bitte das nächste Tape einlegen. Möchte man die maximale Tape-Größe manuell festlegen, ab der gewechselt wird, kann man dies mit dem Parameter -L angeben, der ohne Angabe einer Einheit in Kilobyte rechnet, oder ansonsten mit der Größe, die man angibt (z.B. 2G für zwei Gigabyte oder 10M für 10 Megabyte). Und möchte man das Tape nicht per Hand wechseln (oder einen neuen Dateinamen angeben, in den weiter geschrieben wird), gibt man mit dem Parameter -F ein Script an, das bei jedem vollgeschrieben Tape ausgeführt wird, um beispielsweise einen Tapewechsel mit einem Tape-Wechsler anstößt.

Doch auf mehrere Tapes zu schreiben, wenn wie oben beschrieben mit mehreren hintereinander geschalteten Kommandos gearbeitet wird, kann etwas tricky sein. Der angepasste Befehl (diesmal unkommentiert bis auf die neuen Parameter) sieht wie folgt aus, die Verantwortung für das Wechseln des Tapes liegt hier alleine bei mbuffer:

openssl rand 214 > "/Pfad/zu/Passwort/in/Datei"
tar -c \
  -C "/Pfad/zu/Quellordner" \
  -f - \
  "./Source" \
  | openssl enc \
    -aes-256-cbc \
    -e \
    -salt \
    -pbkdf2 \
    -iter 20000 \
    -pass file:"/Pfad/zu/Passwort/in/Datei" \
  | mbuffer -v 0 \
    -m 1G \
    -s 256k \
    -P 95% \
    -f \
    -q \
    -A "/Pfad/zu/Wechselscript PARAMETERS" \             # -A: Script, das für den Tapewechsel aufgerufen wird, inkl. Parametern
    -o "/Pfad/zu/Tapedevice"

Das Ganze ist noch relativ straightforward, man muss nur bedenken, dass tar in dieser Chain keinerlei direkte Verbindung zum eigentlichen Tape oder der Zieldatei hat und daher auch nicht auf Rückmeldungen über das Ende des Tapes oder der Datei reagieren kann - daher muss dies mbuffer übernehmen.

Bitte entferne vor dem Verwenden dieses Codeschnipsels die Kommentare am Zeilenende! Diese dienen hier nur der Erklärung.

Verschlüsselte Daten von mehreren Tapes wiederherstellen

Etwas bösere Fallstricke hat das Ganze bei der Wiederherstellung verschlüsselter Daten von mehreren Tapes. Denn hier hat zwar tar als letztes Kommando in der Reihe direkten Kontakt zum Tape-Laufwerk, doch auch hier muss der eigentliche Wechsel von mbuffer angestoßen werden. Klingonisch - is aber so!

mbuffer -v 0 \
  -m 1G \
  -s 256k \
  -P 95% \
  -f \
  -q \
  -A "/Pfad/zu/Wechselscript PARAMETERS" \               # -A: Script, das für den Tapewechsel aufgerufen wird, inkl. Parametern
  -i "/Pfad/zum/Tapedevice" \
  -o - \
  | openssl enc \
    -aes-256-cbc \
    -d \
    -iter 20000 \
    -pass file:"/Pfad/zu/Passwort/in/Datei" \
  | tar -x \
    -f - \
    -b 1024 \
    -M \                                                 # -M: Ermöglicht es tar, das Ende des Tapes zu erkennen
    -C "/Pfad/zum/Zielordner"

mbuffer ist an dieser Stelle darauf angewiesen, dass tar das Ende eines Tapes oder einer Datei erkennt und nach unten weiterleitet, hierfür muss der Parameter -M bei letzterem zwingend gesetzt sein.

Bitte entferne vor dem Verwenden dieses Codeschnipsels die Kommentare am Zeilenende! Diese dienen hier nur der Erklärung.

Ein beispielhaftes Tapewechsel-Script

Doch wie sieht so ein Tapewechsel-Script nun eigentlich aus? Die Dokumentation von tar ist für Laien eher kryptisch, die von mbuffer kaum der Erwähnung wert. Beide Tools setzen zur Laufzeit beim Aufruf des Wechselscripts mehrere Umgebungsparameter, die beispielsweise erlauben, zu erkennen, der wievielte Wechsel gerade stattfindet. Dies kann helfen, aus einer Reihe von bekannten Tape-Slots oder Dateien die nächste auszuwählen.

Leider ist gerade bei mbuffer, das in den vorangehenden Beispielen den Tapewechsel übernimmt, entwicklungstechnisch ziemlich viel in Bewegung; je nach Linux-Distribution erhält man mal die eine, mal die andere Version, die eventuell die entsprechenden Parameter schon setzt - oder eben auch nicht. Um dieses Problem zu umgehen, habe ich in meinen Wechselscripten eine manuelle Version der entsprechenden Funktionen implementiert, die unabhängig von der verwendeten Programmversion funktioniert. Auch für Aufrufe eines Wechselscripts via tar taugt sie daher ohne weitere Anpassungen.

Der Aufruf des Scripts erfolgt wie gehabt über den mbuffer-Parameter -A oder den tar-Parameter -F, wenn kein Chaining verwendet wird. Hier übergeben wir einen zusätzlichen Parameter, der auf eine Datei zeigt, in der wir vor dem Aufruf unserer Befehlszeile die Zahl 1 geschrieben haben: -A "./change-archive-tape.sh /Pfad/zu/Volume_tracker"

Das Script könnte wie folgt aussehen (ich gehe von einem Tapewechsler aus, der mehrere Tapes fasst und dessen erstes Tape aus Slot 1 bereits in Slot 0 (Tape-Laufwerk) geladen ist; die weiteren Tapes liegen in Slot 2 bis 4:

#! /usr/bin/env bash

VOLTRACKER="$1"
readonly VOLTRACKER

unload_tape() {
  mt -f /Pfad/zu/Tapelaufwerk rewind
  mtx -f /Pfad/zu/Tapewechsler unload
}

load_tape() {
  local -i tape
  tape="$1"
  mtx -f /Pfad/zum/Tapewechsler load "${tape}"
  mt -f /Pfad/zu/Tapelaufwerk rewind
}

main() {
  # VOLTRACKER sollte 1 beim ersten Wechsel sein, 2 beim zweiten, ...
  local -i current_volume
  current_volume="$(head -n 1 "${VOLTRACKER}" | xargs)"

  # Erhöhen wir den Wert um 1 und schreiben ihn nach VOLTRACKER
  local -i next_volume
  next_volume=$(( current_volume + 1 ))
  echo "${next_volume}" > "${VOLTRACKER}"

  # Das aktuell geladene Tape wird in seinen ursprünglichen Slot entladen
  unload_tape
  
  # Und schließlich laden wir das neue Tape vom nächsten Slot
  load_tape "${next_volume}"
}

main

Dies ist eine wirklich sehr simple Datei ohne Fehlerbehandlung, ohne Output und sie geht davon aus, dass immer alle Tape-Slots mit leeren Tapes belegt sind und das erste immer den Anfang macht. Man kann tatsächlich beliebig viel Komplexität einbauen, um mit verschiedensten Anforderungen umzugehen, aber ich denke, für eine schnelle Demonstration sollte es auchreichen. Mein produktiv eingesetztes Wechselscript nimmt aktuell beispielsweise neun Parameter an.