Daten unter GNU/Linux verschlüsseln

Daten unter GNU/Linux verschlüsseln

Durch Verschlüsselung können vertrauliche Daten vor unbefugtem Zugriff geschützt werden. Im Folgenden beschreibe ich, wie man dies unter Gentoo GNU/Linux erreichen kann.

Achtung: Ich hafte nicht für Schäden, die durch das Befolgen dieser Anleitung entstehen.

Die Schäublone

Ausgangssituation

Für mein Home-Verzeichnis (/home/karsten) auf meinem Rechner (athlon) möchte ich eine 60GB Festplatte (/dev/hdb) verwenden, deren Inhalt verschlüsselt werden soll. Die Festplatte soll nach der Anmeldung am System automatisch aufgeschlossen und in den Verzeichnisbaum eingehängt werden; nach der Abmeldung soll die Festplatte automatisch ausgehängt und zugeschlossen werden. Aufgeschlossen heißt: Der Klartext (die Daten) kann von der Festplatte gelesen werden. Von der zugeschlossenen Festplatte kann nur der Schlüsseltext (die verschlüsselten Daten) gelesen werden.

Konfiguration

Um dies einzurichten, verwende ich die Kernel-Optionen dm-crypt, loop und lrw; sowie die Pakete cryptsetup-luks und pam_mount.

Kernel anpassen

Zuerst muss der Kernel, ich verwende Version 2.6.22, angepasst werden; folgende Optionen sind zu aktivieren:

Device Drivers ---> Block devices
<*> Loopback device support
Device Drivers ---> Multi-device support (RAID and LVM)
<*> Device mapper support
<*>   Crypt target support
Cryptographic options --->
<*> LRW support (EXPERIMENTAL)

Jede Option kann auch als Modul übersetzt werden. Nach dem Neustart des Systems (mit dem neuen Kernel) müssen die betreffenden Module geladen werden.

Pakete installieren

Für die Schlüssel-Verwaltung wird cryptsetup-luks benötigt:

root> emerge cryptsetup-luks

Um die Platte nach der Anmeldung einhängen bzw. nach der Abmeldung aushängen zu können, braucht man pam_mount.

root> emerge pam_mount

Achtung: pam_mount ist bei Gentoo maskiert (MASKED). Die Datei /etc/portage/package.keywords muss um folgende Zeilen ergänzt werden:

# für x86 erforderlich
sys-auth/pam_mount ~*
# für ppc erforderlich
sys-auth/pam_mount **
sys-libs/libhx **

Festplatte schreddern

Um Altlasten auf der Festplatte loszuwerden, wird die gesamte Platte mehrmals mit Zufallswerten und abschließend mit Nullen beschrieben.

root> shred -vz -n 4 /dev/hdb

Ist shred endlich fertig, wurde die Festplatte viermal mit Zufallswerten und abschließend mit Nullen beschrieben. Eine Wiederherstellung von persönlichen Daten sollte nun nahezu unmöglich sein.

Das ganze dauert sehr lange; alternativ kann man sich mit dem Programm dd auch einen Zufallspool erstellen und diesen für das Beschreiben der Festplatte benutzen.

root> dd bs=10M count=100 if=/dev/urandom of=/tmp/random.img

Mit dd und einem kleinen Skript kann man /tmp/random.img nun hintereinander nach /dev/hdb schreiben; das ist wesentlich schneller als shred. Hierzu sollte man sich die dd-Optionen skip und seek anschauen (oder mein Ruby-Script verwenden).

Initialisieren der Festplatte

Die Festplatte kann jetzt initialisiert werden:

root> cryptsetup -c aes-lrw-benbi -s 256 -y luksFormat /dev/hdb

Nun wird man darüber informiert, dass sämtliche Daten verloren gehen (bestätigen mit YES). Anschließend muss man noch eine Passphrase, den Hauptschlüssel eingeben. Hier hat man die freie Wahl, man sollte jedoch eine Phrase eingeben, die zumindest Wörterbuchangriffen standhält. Zur Orientierung ein kleines Beispiel:

Wenn’sch das gewusst hätte, achtsch Mark für Nüscht §$%!

Zur Erläuterung: Es wird die Block-Chiffre AES mit der Betriebsart LRW verwendet; die Schlüssellänge beträgt 256Bit. Es kann laut Dokumentation von lrw auch ein längerer Schlüssel (bis 384Bit) verwendet werden. Ich hatte allerdings keinen Erfolg: Die Festplatte ließ sich anschließend nicht aufschließen. Ein Länge von 256Bit sollte jedoch ausreichen, denn die Wahrscheinlichkeit ist sehr gering, dass ein Angreifer den richtigen aus 2^256 möglichen Schlüsseln in angemessener Zeit ermittelt.

Jetzt können wir die Festplatte aufschließen.

root> cryptsetup luksOpen /dev/hdb _dev_hdb

Die aufgeschlossene Festplatte ist jetzt unter /dev/mapper/_dev_hdb zu erreichen. Was dort fehlt, ist ein Datei-System; ich habe mich für Ext3 entschieden.

root> mkfs.ext3 -j /dev/mapper/_dev_hdb

Die Festplatte kann jetzt verwendet werden, wir schließen sie allerdings ersteinmal zu.

root> cryptsetup luksClose _dev_hdb

Festplatte in /etc/fstab eintragen

Folgende Zeile muss hinzugefügt werden:

/dev/hdb    /home/karsten    crypt    noauto,defaults,noatime    0 0

Jetzt lässt sich die Festplatte ganz einfach aufschließen und einhängen.

root> mount /home/karsten

Nach Eingabe der Passphrase sollte die Festplatte in /etc/mtab auftauchen.

root> mount | grep /home/karsten
/dev/mapper/_dev_hdb on /home/karsten type ext3 (rw,noatime)

Für das Aushängen und Zuschließen der Platte sind zwei Befehle notwendig:

root> umount /dev/mapper/_dev_hdb
root> cryptsetup luksClose _dev_hdb

pam_mount einrichten

Zuerst machen wir die Platte dem PAM-Modul pam_mount bekannt. Dazu wird der Datei /etc/security/pam_mount.conf.xml folgende Zeile hinzugefügt:

<volume user="karsten" fstype="crypt" path="/dev/hdb" mountpoint="/home/karsten" />

Anschließend aktivieren wir das Modul in /etc/pam.d/system-auth.

# hier sind nur die auth- und session-Einträge dargestellt
auth   required pam_env.so
auth   optional pam_mount.so
auth   sufficient pam_unix.so likeauth nullok use_first_pass
auth   required pam_deny
# ...
session required pam_limits.so
session required pam_unix.so
session optional pam_mount.so use_first_pass

Login-Passwort als Schlüssel verwenden

Da die Festplatte automatisch nach der Anmeldung aufgeschlossen werden soll, muss das Login-Passwort ein gültiger Schlüssel für die Festplatte sein. Das Login-Passwort sollte, genau wie die Passphrase, mit Bedacht gewählt werden.

root> cryptsetup luksAddKey /dev/hdb

Jetzt gibt man die Passphrase ein und anschließend zweimal das Login-Passwort. Wenn ich mich nun als karsten anmelde, wird die Festplatte automatisch aufgeschlossen und eingehängt, nach dem Abmelden wird die Platte ausgehängt und zugeschlossen — fein.

Anmerkung: Man kann auch die Passphrase unter Verwendung des Login-Passworts verschlüsseln und als Datei unter /home ablegen. Nach der Anmeldung wird die Passphrase automatisch entschlüsselt und die Festplatte mit ihr aufgeschlossen. Diese Variante bietet allerdings keine Vorteile: Hat ein Angreifer das Login-Passwort, kann er auch die Passphrase entschlüsseln und damit die Festplatte aufschließen.

SSH einrichten

Die Anmeldung von einem entfernten Rechner funktioniert nun leider nicht mehr. Dieses Problem ist bekannt, wie ein Blick in die bugs.txt von pam_mount zeigt:

When interactively asking for the password, the ssh client opens
/dev/tty and ignores stdin over which pam_mount passes the password.

Dort wird auch eine Lösung angegeben.

When public key authentication is used,
the PAM auth stage is entirely skipped.
...
"UseLogin yes" may be used to enable pam_mount -- 
irrespective of public key authentification, ...

Für die folgenden Schritte muss auf dem Rechner der SSH-Dämon sshd laufen. Desweiteren benötigt man auf dem Rechner, von dem der Zugriff erfolgen soll (bei mir ibook), ein RSA-Schlüsselpaar. Der private Schlüssel wird dort im Home-Verzeichnis unter .ssh/id_rsa abgelegt. Den öffentlichen Schlüssel .ssh/id_rsa.pub kopiert man nun auf den Rechner mit der verschlüsselten Festplatte (bei mir athlon). Die verschlüsselte Festplatte darf nicht eingehängt sein!

root@ibook> scp /home/karsten/.ssh/id_rsa.pub athlon:/home/karsten

Auf dem anderen Rechner (athlon) gehe ich in das Verzeichnis /home/karsten, lege dort das Verzeichnis .ssh an und kopiere den öffentlichen Schlüssel dort hin. Er bekommt den Namen authorized_keys.

root@athlon> mkdir /home/karsten/.ssh
root@athlon> mv /home/karsten/id_rsa.pub /home/karsten/.ssh/authorized_keys

Jetzt muss die Datei /etc/ssh/sshd_config angepasst werden. Dort hängt man folgende Zeile an:

UseLogin yes

Der sshd muss die Änderung der Konfiguration noch übernehmen.

root@athlon> /etc/init.d/sshd reload

Das war’s. Vom Rechner ibook kann ich mich nun via ssh auf dem Rechner athlon anmelden.

karsten@ibook> ssh athlon
reenter password for pam_mount:

Mit dem richtigen Passwort wird die Festplatte aufgeschlossen und eingehängt. Bei falschem Passwort landet man zwar auch in /home/karsten, allerdings ist dort nicht die verschlüsselte Festplatte eingehängt. Um Verwechslungen auszuschließen, habe ich, bei ausgehängter Festplatte, das Home-Verzeichnis inklusive aller darin enthaltenen Dateien root zugeordnet und dort eine Datei YOUR_HOME_IS_NOT_MOUNTED angelegt. Nach einem ls sieht man sofort, ob was falsch gelaufen ist. Hier die notwendigen Schritte:

root@athlon> mount | grep /home/karsten | wc -l
0
root@athlon> chown -R root:root /home/karsten
root@athlon> touch /home/karsten/YOUR_HOME_IS_NOT_MOUNTED

Achtung: Ist die Festplatte aufgeschlossen und eingehängt, findet sshd die Datei .ssh/authorized_keys nicht mehr, da sich jetzt unter /home/karsten die Festplatte befindet. Will man eine weitere ssh-Session starten, muss die Datei erst angelegt werden.

Probleme beim Aushängen beseitigen

Schließt man eine SSH-Verbindung oder meldet man sich ab, nachdem man mit su die Identität des Festplatten-Eigentümers angenommen hat, wird die Festplatte nicht ausgehängt, da die erforderlichen root-Rechte fehlen. Das Aushängen schlägt auch fehl, falls noch Programme auf der Festplatte arbeiten. Mit dem Paket sudo und einer Änderung des Skripts /sbin/umount.crypt lässt sich das jedoch abändern. Zuerst installieren wir das Paket sudo:

root> emerge sudo

Jetzt erlauben wir dem Eigentümer der Festplatte (hier karsten) einige Befehle als root, ohne Passwort-Eingabe, auszuführen.

root> visudo

Folgende Zeile wird eingefügt.

karsten ALL = NOPASSWD: /bin/umount /home/karsten, /sbin/cryptsetup luksClose _dev_hdb

Das Skript /sbin/umount.crypt zum Aushängen der Festplatte wird um drei Funktionen ergänzt.

function print_error() {
        echo "ERROR: $1"
}
function prepare_umount() {
        while [[ $(fuser -m $1 | cut -d : -f 2 | wc -w) != 0 ]]; do
            fuser -km $1;
            sleep 1;
        done
}
function force_umount() {
        UMOUNT="$(which umount) $1"
        SUDO_UMOUNT="sudo $UMOUNT"
        CLOSE="$(which cryptsetup) luksClose $2"
        SUDO_CLOSE="sudo $CLOSE"
        echo "forcing umount of $1"
        if [[ $(sudo -l | grep "^ \+(root) NOPASSWD: $UMOUNT\$" | wc -l) == 1 ]]; then
                $SUDO_UMOUNT
                if [[ $? == 0 ]]; then
                        echo "closing $2"
                        if [[ $(sudo -l | grep "^ \+(root) NOPASSWD: $CLOSE\$" | wc -l) == 1 ]]; then
                                $SUDO_CLOSE
                                if [[ $? != 0 ]]; then print_error "\"$SUDO_CLOSE\" failed"; fi
                        else
                                print_error "you are not allowed to run \"$SUDO_CLOSE\" as root without password"
                        fi
                else
                        print_error "\"$SUDO_UMOUNT\" failed"
                fi
        else
                print_error "you are not allowed to run \"$SUDO_UMOUNT\" as root without password"
        fi
}

prepare_umount fordert alle Programme, die noch auf die Festplatte zugreifen, auf, sich zu beenden, denn sonst kann die Platte nicht ausgehängt werden. force_umount erzwingt das Aushängen der Platte, falls es zuvor, aufgrund fehlender Rechte, erfolglos war. print_error dient zur Ausgabe von Fehlern.

prepare_umount und force_umount werden wie folgt in das Skript eingefügt:

prepare_umount $1
umount "$1";
if [ $? -ne 0 ]; then
        echo "${0##*/}: error unmounting $1" >&2
        force_umount $1 $DMDEVICE
        exit 1
fi

Damit der Session-Zähler dekrementiert werden kann, muss der Festplatten-Eigentümer bzw. seine Gruppe (hier users) noch das Schreibrecht für das Verzeichnis /var/run/pam_umount erhalten.

root> chown root:users /var/run/pam_mount
root> chmod 775 /var/run/pam_mount

Das wars, nun funktioniert (wirklich) alles.

Leistungsbewertung

Das Entschlüsseln der Daten nach dem Lesen und das Verschlüsseln vor dem Schreiben kosten Zeit. Wie sich das auf den Durchsatz der Festplatte auswirkt, kann man mit dem Programm bonnie messen. Die Installation des Pakets ist ganz einfach.

root> emerge bonnie

Jetzt nehme ich die Identität des Nutzers karsten an.

root> su - karsten

Mit dem richtigen Passwort wird die Festplatte aufgeschlossen und eingehängt; anschließend wird in das Home-Verzeichnis von karsten gewechselt. Nun kann der Datendurchsatz der Platte gemessen werden.

karsten ~> bonnie -s 1024

Nach diesem Aufruf werden 1GB Daten (mehrmals) auf die Platte geschrieben und anschließend (mehrmals) wieder ausgelesen. Bei allen Operationen wird die Zeit gemessen. Ist bonnie fertig, werden die Mess-Ergebnisse ausgegeben.

              -------Sequential Output-------- ---Sequential Input-- --Random---
              -Per Char- --Block--- -Rewrite-- -Per Char- --Block--- --Seeks----
Machine    MB K/sec %CPU K/sec %CPU K/sec %CPU K/sec %CPU K/sec %CPU K/sec  %CPU
athlon   1024 24140 79.5 24356 15.4 13134  6.0 26305 77.7 31198  6.0 1618.2  5.2

Vorher habe ich bereits den Datendurchsatz der Festplatte ohne Verschlüsselung gemessen.

              -------Sequential Output-------- ---Sequential Input-- --Random---
              -Per Char- --Block--- -Rewrite-- -Per Char- --Block--- --Seeks----
Machine    MB K/sec %CPU K/sec %CPU K/sec %CPU K/sec %CPU K/sec %CPU K/sec  %CPU
athlon   1024 27278 88.4 36210 23.5 16648  7.7 27802 84.6 47837  8.5 1718.0  6.4

Was fällt auf? Das Schreiben und Lesen von Blöcken ist durch die Verschlüsselung ca. 30 Prozent langsamer geworden. Sonst ist der Durchsatz zwar auch geringer, liegt jedoch weit unter 30 Prozent.

Fazit: Ich denke, mit diesem Datendurchsatz kann man leben.

Abschließende Bemerkungen

Die hier vorgestellte Methode eignet sich für Rechner, an denen zu jedem Zeitpunkt maximal eine Person arbeitet. Denn ist die verschlüsselte Platte aufgeschlossen und eingehängt, kann jeder angemeldete Nutzer Dateien auf der Platte lesen, vorausgesetzt, er hat die entsprechenden Rechte.