Verschlüsselte Remote-Dateisysteme

Zum Jahresanfang ein klein wenig Admin-Fu... gelegentlich kommt man in die Verlegenheit, auf einem entfernten Rechner Daten ablegen zu wollen, die vom jeweiligen Admin nicht lesbar sein sollen (beispielsweise weil man diesem nicht hinreichend vertraut). Anstoß für meine Überlegungen war die Notwendigkeit, ein Backup übers Netz zu machen, wobei die Daten auf dem entfernten Rechner nicht lesbar (sprich: verschlüsselt abgelegt) sein sollten.
Da es sich hierbei nur um ein "Reservebackup" handeln sollte, hätte eigentlich eine einfache Kopie der Dateien (ohne eine Backup-History) genügt - daher ging mein erster Gedanke in Richtung eines verschlüsselten Dateisystems. Gleich vorneweg: Der Gedanke stellte sich als Irrweg heraus - weshalb erkläre ich im Abschnitt über die nun eingesetzte Alternative, nämlich duplicity. Trotzdem möchte ich die benötigten Schritte zur Einrichtung hier kurz dokumentieren.

Einrichten ssh

Als Basis für den sicheren Zugriff auf den entfernten Rechner habe ich ssh gewählt. Mit Authentisierung über Public-Private-Keypaare läßt sich der Zugriff auch schön automatisieren - entweder indem man den Private Key auf dem zu sichernden System unverschlüsselt hinterlegt oder durch Speichern des zugehörigen Passphrases in einer Keyagent-Instanz, die dann natürlich nach einem Systemstart manuell neu einzugeben ist.
Der Zugriff auf das Zielsystem sollte jedoch so eingeschränkt sein, daß der Inhaber des Backup-Accounts zwar Daten sichern, aber keine interaktive Shell öffnen kann. Glücklicherweise läßt sich dies inzwischen bei openssh recht komfortabel konfigurieren.
Da ich die Datenablage für mehrere Rechner nutzen möchte, habe ich die Konfiguration gleich entsprechend generisch gemacht. Jeder Backup-Account wird Mitglied der Gruppe remotebak, die zugehörigen Backup-Verzeichnisse liegen in in /var/backups/remote/username.
In sshd_config des Zielsystems wird mit folgender Konfiguration nur der Zugriff via sftp auf das eigene Datenverzeichnis erlaubt, wenn der Benutzer Mitglied der Gruppe remotebak ist:

Match Group remotebak
        ChrootDirectory /var/backups/remote/%u
        ForceCommand internal-sftp
        AllowTcpForwarding no
        X11Forwarding no

Für meinen ersten Server habe ich einen User server1 angelegt, ihn der Gruppe remotebak zugewiesen und für ihn ein Verzeichnis /var/backups/remote/server1 angelegt. Dieses Verzeichnis muß dem Benutzer root gehören, sonst verweigert sich openssh an beim chroot-Kommando. In das Verzeichnis habe ich (ebenfalls als root, so daß der Benutzer keine Änderungen vornehmen kann) ein Verzeichnis .ssh und darin eine Datei authorized_keys für die Schlüsselauthentisierung angelegt. Zu guter letzt bekam das Verzeichnis noch einen Unterordner data, welcher nun dem User server1 gehört - hierin hat er nun Schreibrechte.

Mounten als verschlüsseltes Dateisystem

Mein erster Versuch war, das Verzeichnis mit ecryptfs zu verschüsseln; dazu mußte ich es zunächst lokal zur Verfügung stellen, um anschließend mit ecryptfs ein verschlüssltes Unterverzeichnis anzulegen.

Verbindung via sshfs

Am einfachsten erfolgt eine solche Einbindung via sshfs. Als Vorbereitung für das Mounten habe ich die ssh-Konfiguration in der .ssh/config zusammengeschrieben:

Host mein.backup.server
User server1
IdentityFile /root/.ssh/id_dsa-backup

Das Mounten auf den Mountpoint /mnt/remotebackup erfolgt nun mit dem Befehl
sshfs mein.backup.server: /mnt/remotebackup -C -o uid=0 -o gid=0

Verschlüsselungsoverlay mit ecryptfs

Nun besteht zwar eine verschlüsselte Verbindung zwischen Client und Server, jedoch werden die Daten auf dem Server unverschlüsselt abgelegt. Abhilfe kann ecrpytfs schaffen, welches ein verschlüsseltes Overlay über ein vorhandenes Dateisystem legt. Die Einrichtung geschieht beim ersten Mounten:

mount -t ecryptfs /mnt/remotebackup/data /mnt/remotebackup/data
Passphrase: (passwort eingeben)
Select cipher:
 1) aes: blocksize = 16; min keysize = 16; max keysize = 32 (not loaded)
 2) blowfish: blocksize = 16; min keysize = 16; max keysize = 56 (not loaded)
 3) des3_ede: blocksize = 8; min keysize = 24; max keysize = 24 (not loaded)
 4) twofish: blocksize = 16; min keysize = 16; max keysize = 32 (not loaded)
 5) cast6: blocksize = 16; min keysize = 16; max keysize = 32 (not loaded)
 6) cast5: blocksize = 8; min keysize = 5; max keysize = 16 (not loaded)
Selection [aes]:
Select key bytes:
 1) 16
 2) 32
 3) 24
Selection [16]:
Enable plaintext passthrough (y/n) [n]:
Enable filename encryption (y/n) [n]:
Attempting to mount with the following options:
  ecryptfs_unlink_sigs
  ecryptfs_key_bytes=16
  ecryptfs_cipher=aes
  ecryptfs_sig=hashwert
WARNING: Based on the contents of [/root/.ecryptfs/sig-cache.txt],
it looks like you have never mounted with this key
before. This could mean that you have typed your
passphrase wrong.
Would you like to proceed with the mount (yes/no)? : yes
Would you like to append sig [hashwert] to
[/root/.ecryptfs/sig-cache.txt]
in order to avoid this warning in the future (yes/no)? : yes
Successfully appended new sig to user sig cache file
Mounted eCryptfs

Ich habe jeweils die Standardeinstellungen verwendet. Um diese Fragen nicht jedesmal neu eingeben zu müssen, kann man die Parameter in der Datei ~/.ecryptfsrc speichern. Wichtig ist hierbei den angegebenen Hashwert zu übernehmen. Außerdem besteht hier die Möglichkeit, die Quelle für das Passwort aus einer Datei zu behziehen. Letztere kann beispielsweise auf einem USB-Stick gespeichert sein, in meinem Fall ging es ja nur darum, die Dateien auf dem Zielsystem unlesbar zu machen, weshalb ich den Schlüssel mit auf dem Rechner ablegte.
key=passphrase:passphrase_passwd_file=/root/.ecryptfs-backup
ecryptfs_sig=hashwert
ecryptfs_cipher=aes
ecryptfs_key_bytes=16
ecryptfs_passthrough=n
ecryptfs_enable_filename_crypto=n

Die erwähnte Schlüsseldatei .ecryptfs-backup sieht folgendermaßen aus:

passphrase_passwd=(passwort)

Backup mit duplicity

Das Procedere stellte sich leider für ein Backup mit rsync als ungeeignet heraus: Normalerweise verwendet rsync für den Dateiabgleich über das Netz ein Verfahren, welches die Differenzen zwischen den Dateien feststellt (und anschließend nur diese überträgt). Dafür wird auf der Gegenseite ebenfalls ein rsync-Prozeß gestartet, die beiden Programminstanzen handeln dann die zu übertragenden Daten aus. In diesem Fall ist das Backup-Ziel aber ein (vermeintlich lokales) Dateisystem; das bedeutet, daß rsync für die Suche nach veränderten Dateien sämtliche Daten vom entfernten Rechner lesen muß, was eine Übertragung über das Netz plus den Overhead durch die Entschlüsselung bedeutet. Dazu kommt noch, daß einige Attribute wie Owner oder Zeitstempel nicht ohne weiteres abgeglichen werden können. Für meine Backupzwecke war das Vorgehen also ungeeignet; für andere Einsatzzwecke ("erweitertes Homedirectory" auf einem entfernten Server, Verschlüsselung der eigenen Daten in der Dropbox) taugt der Ansatz jedoch sicherlich.
Letztlich habe ich für das Backup doch wieder auf duplicity zurückgegriffen. Duplicity verschlüsselt das Backup mit gpg; intern nutzt Duplicity die Bibliotheken von rdiff, jedoch ohne eine interaktive Gegenstelle zu benötigen. Man erhält so eine vollständige verschlüsselte Backuplösung mit inkrementiellen Backups. Eigentlich wollte ich auf den Platzverbrauch durch die Inkrements verzichten... nun habe ich ein Skript am Start, welches per Cron-Job nächtlich läuft und alle drei Monate ein neues Vollbackup erstellt:

REMOTE_DEST="scp://server1@mein.backup.server/data"
REMOTE_OPTIONS='--encrypt-key=0xersterschlüssel --encrypt-key=0xzweiterschlüssel --ssh-options="-oIdentityFile=/root/.ssh/id_dsa-backup"'

for dir in /etc /home /root ; do
        DESTDIR=`echo $dir | sed "s/^\///;s/\/$//;s/\//_/g"'
        LOGFILE="/var/log/duplicity/${DESTDIR}.log"
        (
                echo "++++++++++++++++++++++++++++++++++++++++"
                date
                echo "++++++++++++++++++++++++++++++++++++++++"
                duplicity --full-if-older-than=3M $REMOTE_OPTIONS $dir ${REMOTE_DEST}/${DESTDIR}
                duplicity remove-older-than 4M --force $REMOTE_OPTIONS ${REMOTE_DEST}/${DESTDIR}
                echo
                echo
        ) 2>&1 | tee $LOGFILE
done

Gesichert werden die Verzeichnisse der for-Schleife (etc, home und root); verschlüsselt wird das Backup exemplarisch für zwei gpg-Schlüssel, deren Key-IDs in der Variable REMOTE_OPTIONS angegeben werden.

Soviel zu den Recherche-Ergebnissen des Tages zum Thema verschlüsselte Daten auf entfernten Systemen, ohne dort das Schlüsselmaterial deponieren zu müssen :-) In einem weiteren Artikel wird beschrieben, wie man den Datenablage-Server weiter absichern kann (und sollte), um diesen im Fall eines Systemeinbruchs nicht mitzugefährden.