Sicherheit · 11 min read · Jan 10, 2026

Erstellen eines sicheren Verzeichnisses mit PAM und EncFS

Erstellen eines sicheren Verzeichnisses mit PAM und EncFS

Inhalte

. Einführung
. Installation von encfs
. Installation von pam_script
. Anpassen der PAM-Konfiguration
. Das Ergebnis

Einführung

Ich arbeite viel mit Programmen, die Anmeldeinformationen erfordern.
Beispiele für diese Programme sind:
. mount.cifs
. fusesmb (Details hier)

Jetzt könnten (und sollten) in meinem Netzwerk (und anderen) die bei der Anmeldung bereitgestellten Anmeldeinformationen von diesen Programmen verwendet werden. Wie kann man diese Anmeldeinformationen abrufen und dabei genügend Sicherheit bieten?
Mit dem PAM-Modul pam_script ist es möglich, das Passwort in einer Datei zu speichern, die von fusemb und mount.cifs zum Lesen des Passworts verwendet wird.

Um Sicherheit zu erreichen, könnte man den Benutzer, der sich anmeldet, zum Eigentümer machen und das Lesen/Schreiben für alle anderen verweigern. Entfernen Sie diese Datei, wenn der Benutzer seine Sitzung beendet.
Das ist genug für die Laufzeit. Aber ich habe mich gefragt, was passiert, wenn das System abstürzt und die Datei mit den Anmeldeinformationen auf der Festplatte bleibt? Jeder, der in der Lage ist, diese Festplatte mit beispielsweise einem Lifecd zu mounten, kann diese Datei lesen!

Deshalb habe ich nach einer Möglichkeit gesucht, diese Datei zu verschlüsseln.

Mit encfs ist das sehr gut möglich! Zur Laufzeit bietet es eine Schnittstelle zu verschlüsselten Dateien und Verzeichnissen, die nur zur Laufzeit existiert! Wenn das System nicht läuft, gibt es nur verschlüsselte Dateien, die nutzlos sind, wenn man den Schlüssel dafür nicht kennt. Und dieser Schlüssel ist genau das (verschlüsselte) Passwort! Deshalb habe ich mich für eine Kombination aus PAM und Encfs entschieden.

Schauen Sie sich die Website von Encfs an: http://freshmeat.net/projects/encfs

Diese Konstruktion soll genügend Sicherheit für die Lauf- und Ausfallzeit (nach einem Absturz) bieten, um sensible Informationen zu speichern, nicht um ein dauerhaft sicheres Verzeichnis auf Ihrer Festplatte zu erstellen, um Dokumente zu speichern.

Installation von encfs

Natürlich sollte FUSE installiert sein.
Die Installation ist sehr einfach:
(siehe die Website für rlog, das von EncFs benötigt wird)

tar -xzf encfs-*.tar.gz  
cd encfs-*  
configure --prefix=/usr --sysconfdir=/etc --libexecdir=/usr/sbin  
make  
make install

Nach der Installation können Sie testen, ob es funktioniert:

mkdir -p ~/test/encrypted  
mkdir -p ~/test/decrypted  
  
encfs ~/test/encrypted ~/test/decrypted  
  
mount (sollte das gerade erstellte Mount anzeigen)  
  
echo "Das ist sehr geheim." > ~/test/decrypted/testfile

Das Verzeichnis encrypted enthält die verschlüsselten Dateien und bleibt auf der Festplatte nach dem Unmounten und/oder Herunterfahren. Das Verzeichnis decrypted ist die Schnittstelle dazu und verschwindet beim Unmounten und/oder Herunterfahren.

Die Datei testfile sollte im entschlüsselten Verzeichnis erscheinen und in verschlüsselter Form im verschlüsselten Verzeichnis. Name und Inhalt der Datei sind verschlüsselt.

Ich habe mich für einen separaten Ordner auf der lokalen Maschine entschieden, in dem für jeden Benutzer, der sich anmeldet, ein verschlüsseltes (und eine Schnittstelle dazu) gespeichert wird:

install -m777 -o root -g root /var/lib/encfs

Beachten Sie die Berechtigungen: Jeder muss hier ein Verzeichnis erstellen können. Später in diesem Dokument wird erklärt, warum.

Installation von pam_script

Die Installation ist sehr einfach. Nach einem make-Befehl verschieben Sie die Bibliothek in das richtige Verzeichnis, /lib/security.

tar -xzf pam-script-*.tar.gz  
cd pam-script-*  
make  
mv pam_script.so /lib/security  
chown root:root /lib/security/pam_script.so  
chmod 755 /lib/security/pam_script.so

Pam_script.so verwendet einige Parameter. Alle sind im README im Quellverzeichnis beschrieben. Wichtig sind:

allgemeine Parameter

  • runas=#user#: lässt das aufgerufene Skript als Benutzer #user# ausgeführt werden

Parameter nur im Authentifizierungsteil

  • onauth=/path/to/onauth/script: Pfad zum Skript, das im Authentifizierungsteil ausgeführt wird
    Standard ist /etc/security/onauth

nur im Sitzungsteil

  • onsessionopen=/path/to/onsessionopen/script: Pfad zum Skript, das ausgeführt wird, wenn eine (gültige) Sitzung gestartet wird
    Standard ist /etc/security/onsessionopen

  • onsessionclose=/path/to/onsessionclose/script: Pfad zum Skript, das ausgeführt wird, wenn eine Sitzung geschlossen wird
    Standard ist /etc/security/onsessionclose

Nach der Installation ist das Komplexere, das System so zu konfigurieren, dass es dieses Modul nutzt.

Anpassen der PAM-Konfiguration

Ich habe pam_script im Authentifizierungs- und im Sitzungsteil der pam (login,kde) Dienstdatei verwendet.
Zuerst beschreibe ich, wie man den Authentifizierungsteil anpasst, wo pam_script mehr als einmal verwendet wird.

Der Authentifizierungsteil

Pam_script hat die Fähigkeit (ab Version 0.1.5), das beim Login bereitgestellte Passwort zu erhalten und dieses über eine Umgebungsvariable PAM_AUTHTOK für Skripte verfügbar zu machen.

Der Zweck hier ist, ein sicheres Verzeichnis zu erstellen, in dem vertrauliche Informationen (wie Anmeldeinformationen) gespeichert werden.

Das Modul wird im Authentifizierungsteil gestapelt:

cat /etc/pam.d/login
-- snip --
auth                required        pam_shells.so
auth                required        pam_script.so expose=1
auth                sufficient        pam_unix.so use_first_pass
auth                sufficient        pam_ldap.so use_first_pass
auth                required        pam_script.so onauth=/etc/security/onauth.failed
auth                required        pam_deny.so

Wie Sie sehen können, verwende ich pam_scripts.so mehrfach:

  • Das erste Mal, um ein Skript auszuführen, das ein verschlüsseltes Verzeichnis (mit encfs) erstellt und das geheime Passwort in einer Datei in diesem Verzeichnis für die Verwendung durch anmeldeinformationssensitive Programme wie fusesmb und mount.cifs schreibt. Dies geschieht direkt vor dem ersten Authentifizierungsmodul, das folgt, pam_unix.

  • Das letzte Mal, um ein Skript auszuführen, wenn die Authentifizierung nicht erfolgreich ist. Wenn die vorhergehenden Authentifizierungsmodule fehlschlagen (pam_unix und pam_ldap) (und nur dann) wird dieses Modul erreicht. Es ist notwendig, das verschlüsselte Verzeichnis zu unmounten und temporäre Dateien zu entfernen.

  • Beachten Sie, dass das letzte Modul von allen pam_deny ist, was wirklich notwendig ist. Ohne es könnte jeder sich anmelden. Das liegt daran, dass pam_script immer “PAM_SUCCESS” zurückgibt, egal was der Rückgabewert der Skripte ist.

Vergessen Sie nicht, das Flag “use_first_pass” zu dem bestehenden Modul pam_unix.so hinzuzufügen.

Die Skripte

cat /etc/security/onauth
#!/bin/bash 
 
retcode=0; 
 
userid=$1 
service=$2 
authtok=$3 
 
if [ -z "$authtok" ]; then 
 
    authtok=$PAM_AUTHTOK 
 
fi; 
 
userproperties=$(getent passwd | grep -m 1 -E "^$userid") 
 
if [ -z "$userproperties" ]; then 
 
    # 
    # Benutzer nicht gefunden: etwas ist falsch 
    # 
 
    echo "Benutzer nicht gefunden." 
    exit 
 
fi; 
 
homedir=$(echo $userproperties | cut -d ":" -f 6); 
gidnr=$(echo $userproperties | cut -d ":" -f 4); 
uidnr=$(echo $userproperties | cut -d ":" -f 3); 
 
if [ -d /var/lib/encfs ]; then 
 
    # erstelle ein sicheres Verzeichnis 
 
    if [ ! -d /var/lib/encfs/$userid ]; then 
 
        install -m700 -o $uidnr -g $gidnr -d /var/lib/encfs/$userid 
 
    fi; 
 
    if [ ! -d /var/lib/encfs/$userid/encrypted ]; then 
 
        install -m700 -o $uidnr -g $gidnr -d /var/lib/encfs/$userid/encrypted 
 
    fi; 
 
    if [ ! -d /var/lib/encfs/$userid/unencrypted ]; then 
 
        install -m700 -o $uidnr -g $gidnr -d /var/lib/encfs/$userid/unencrypted 
 
    fi; 
 
    if [ ! -d /var/lib/encfs/$userid/run ]; then 
 
        install -m700 -o $uidnr -g $gidnr -d /var/lib/encfs/$userid/run 
        
    fi; 
 
    # 
    # teste, ob das verschlüsselte Verzeichnis nicht bereits gemountet ist
    # 
 
    if [ -z "$(mount | grep -w /var/lib/encfs/$userid/unencrypted )" ]; then 
 
        # 
        # erstelle ein Passwort-bereitstellendes Programm 
        # 
 
        cd /var/lib/encfs/$userid 
 
        md5authsum=$(echo $authtok | md5sum | cut -d " " -f 1) 
 
        echo "$md5authsum" > run/tmp 
        echo "$md5authsum" >> run/tmp 
 
        chown $uidnr:$gidnr run/tmp 
 
        rm -rf encrypted/* 
        rm -f encrypted/.encfs* 
 
        cat run/tmp | encfs -S /var/lib/encfs/$userid/encrypted /var/lib/encfs/$userid/unencrypted -- -o allow_root 1>>/dev/null 
 
    fi; 
 
    # 
    # darum geht es: die Anmeldeinformationen in einer Datei zu speichern 
    # in diesem Fall das Passwort 
    #  
 
    if [ -n "$(mount | grep -w /var/lib/encfs/$userid/unencrypted )" ]; then 
 
        cd /var/lib/encfs/$userid/unencrypted/ 
 
        if [ -f password.tmp ]; then 
 
                rm password.tmp 
 
        fi; 
 
        echo $authtok > password.tmp 
 
    fi; 
 
fi; 

Einige Anmerkungen:

Dieses Skript erstellt ein verschlüsseltes Verzeichnis (falls es nicht bereits existiert) mit encfs. Beachten Sie die -S-Option bei encfs: Das Passwort für dieses verschlüsselte Verzeichnis wird von stdin gelesen und nicht abgefragt.
Dieses Passwort ist dasselbe wie das, das bei der Anmeldung verwendet wird.
Das verschlüsselte Verzeichnis befindet sich unter /var/lib/encfs/$userid.

Das Passwort wird in einer Datei, password.tmp, geschrieben. Dies ist eine temporäre Datei: Das Passwort muss nicht das richtige sein. Später im Prozess wird diese Datei je nach Erfolg der Validierung umbenannt (in password) oder entfernt.

Sehr wichtig zu beachten ist, dass das Skript als der sich anmeldende Benutzer und nicht als root ausgeführt wird! Und da Programme wie mount.cifs später Zugriff auf dieses Verzeichnis benötigen, um die Passwortdatei zu lesen, wird die allgemeine FUSE-Option allow_root hinzugefügt.

#!/bin/bash
userid=$1
service=$2
userproperties=$(getent passwd | grep -m 1 -E "^$userid")
if [ -z "$userproperties" ]; then
    #
    # Benutzer nicht gefunden: etwas ist falsch
    #
    echo "Benutzer nicht gefunden."
    exit
fi;
homedir=$(echo $userproperties | cut -d ":" -f 6);
gidnr=$(echo $userproperties | cut -d ":" -f 4);
uidnr=$(echo $userproperties | cut -d ":" -f 3);
#
# dieses Skript wird ausgeführt, wenn die Authentifizierung fehlgeschlagen ist
# ein sicheres verschlüsseltes Verzeichnis wird weiterhin erstellt
# daher ist es wichtig, dieses Verzeichnis wieder zu entfernen
# 
if [ -d /var/lib/encfs ]; then
    if [ -d /var/lib/encfs/$userid/unencrypted ]; then
        #
        # teste, ob das verschlüsselte Verzeichnis bereits gemountet ist
        #
        if [ -n "$(mount | grep -w /var/lib/encfs/$userid/unencrypted )" ]; then
            if [ $(w -h $userid | wc -l) -eq 0 ]; then
                #
                # dieser Benutzer ist nicht auf mehr tty's angemeldet
                # alles entfernen und das verschlüsselte Verzeichnis unmounten
                #
                rm -rf /var/lib/encfs/$userid/unencrypted/*
                fusermount -u /var/lib/encfs/$userid/unencrypted
                
                rm -rf /var/lib/encfs/$userid/encrypted/*
                rm -f /var/lib/encfs/$userid/encrypted/.encfs*
            else
                #
                # dieser Benutzer ist noch angemeldet
                #
                rm -f /var/lib/encfs/$userid/unencrypted/password.tmp
            fi;
        fi;
        if [ -z "$(mount | grep -w /var/lib/encfs/$userid/unencrypted )" ]; then
                
                rm -rf /var/lib/encfs/$userid/encrypted/*
                rm -f /var/lib/encfs/$userid/encrypted/.encfs*
                rm -rf /var/lib/encfs/$userid/unencrypted/*
        fi;
    fi;
fi;

Einige Anmerkungen:

Dieses Skript wird ausgeführt, wenn die bereitgestellten Anmeldeinformationen nicht gültig sind. Das verschlüsselte Verzeichnis, das gerade eingerichtet wurde, wird wieder entfernt (unmontiert) und geleert. Dies geschieht natürlich nur, wenn dieser Benutzer nicht auf einem anderen tty angemeldet ist.

Der Sitzungsteil

Wenn der Sitzungsteil erreicht wird, ist sicher, dass die bereitgestellten Anmeldeinformationen (Passwort) korrekt sind. Das bedeutet, dass das Passwort - im Authentifizierungsprozess in einer temporären Datei gespeichert - korrekt ist. Eine Sache, die im Sitzungsteil zu tun ist, besteht darin, den Inhalt von password.tmp in die permanente Datei password zu verschieben.
Eine zweite Sache ist das Ausführen von Skripten, die diese Anmeldeinformationen für eigene Zwecke benötigen, wie das Mounten von CIFS-Freigaben oder fusesmb.
Es ist logisch, dass alle vertraulichen Informationen im sicheren Verzeichnis bleiben. Darum ging es schließlich von Anfang an!!

Beachten Sie, dass dieses Modul standardmäßig zwei Skripte ausführt:
. /etc/security/onsessionopen: wenn eine Sitzung gestartet/öffnet;
. /etc/security/onsessionclose: wenn eine Sitzung endet/schließt.

Ich folge dem Standard, es gibt keinen Grund, es anders zu machen.

Meine /etc/pam.d/login-Datei (der Sitzungsteil) sieht so aus:

cat /etc/pam.d/login
-- snip --
session                required        pam_mkhomedir.so
session                required        pam_motd.so
session                 optional        pam_mail.so empty dir=/var/mail
session                optional        pam_lastlog.so
session                required        pam_env.so
session                required        pam_script.so
session          required               pam_unix.so
session                required        pam_ldap.so

Die Skripte

Bei Sitzungsöffnung

cat /etc/security/onsessionopen
#!/bin/bash
retcode=0;
userid=$1
service=$2
userproperties=$(getent passwd | grep -E "^$userid")
if [ -z "$userproperties" ]; then
    #
    # Benutzer nicht gefunden: etwas ist falsch
    #
    echo "Benutzer nicht gefunden."
    exit
fi;
homedir=$(echo $userproperties | cut -d ":" -f 6);
gidnr=$(echo $userproperties | cut -d ":" -f 4);
uidnr=$(echo $userproperties | cut -d ":" -f 3);
if [ -d /var/lib/encfs/$userid/encrypted ]; then
    #
    # teste, ob das verschlüsselte Verzeichnis gemountet ist
    #
    if [ -n "$(mount | grep -w /var/lib/encfs/$userid/unencrypted )" ]; then
        
        if [ -f /var/lib/encfs/$userid/unencrypted/password.tmp ]; then
            
            if [ -f /var/lib/encfs/$userid/unencrypted/password ]; then
                #
                # eine alte Passwortdatei gefunden 
                #
                if [ -z "(diff /var/lib/encfs/$userid/unencrypted/password /var/lib/encfs/$userid/unencrypted/password.tmp)" ]; then
                    #
                    # neues Passwort und altes sind gleich
                    #
                    rm /var/lib/encfs/$userid/unencrypted/password.tmp
                else
                    mv /var/lib/encfs/$userid/unencrypted/password.tmp /var/lib/encfs/$userid/unencrypted/password
                fi;
            else
                #
                # Passwort nicht gefunden: es ist die erste Anmeldung.
                # einfach die temporäre Passwortdatei in die verbleibende verschieben
                #
                mv /var/lib/encfs/$userid/unencrypted/password.tmp /var/lib/encfs/$userid/unencrypted/password
            fi;        
        fi;
        if [ -d /etc/session.d/pam/onsessionopen ]; then
            
            for script in /etc/session.d/pam/onsessionopen/*.sh; do
                
                if [ -x $script ]; then
                    
                    eval $script $userid $service
                fi
                
            done;
        fi;
    fi;
fi;

Bei Sitzungsabschluss

cat >> /etc/security/onsessionclose
#!/bin/bash
userid=$1
service=$2
userproperties=$(getent passwd | grep -E "^$userid")
if [ -z "$userproperties" ]; then
    #
    # Benutzer nicht gefunden: etwas ist falsch
    #
    echo "Benutzer nicht gefunden."
    exit
fi;
homedir=$(echo $userproperties | cut -d ":" -f 6);
gidnr=$(echo $userproperties | cut -d ":" -f 4);
uidnr=$(echo $userproperties | cut -d ":" -f 3);
#
# dieses Skript wird ausgeführt, wenn die Authentifizierung fehlgeschlagen ist
# ein sicheres verschlüsseltes Verzeichnis wird weiterhin erstellt
# daher ist es wichtig, dieses Verzeichnis wieder zu entfernen
# 
if [ -d /var/lib/encfs ]; then
    if [ -d /var/lib/encfs/$userid/unencrypted ]; then
        #
        # teste, ob das verschlüsselte Verzeichnis bereits gemountet ist
        #
        if [ -n "$(mount | grep -w /var/lib/encfs/$userid/unencrypted )" ]; then
            if [ $(w -h $userid | wc -l) -eq 0 ]; then
                
                rm -rf /var/lib/encfs/$service.$userid/unencrypted/*
                fusermount -u /var/lib/encfs/$userid/unencrypted
                
                rm -rf /var/lib/encfs/$userid/encrypted/*
                rm -f /var/lib/encfs/$userid/encrypted/.encfs*
                
            fi;
        else
            
            rm -rf /var/lib/encfs/$userid/encrypted/*
        fi;
        if [ -d /etc/session.d/pam/onsessionclose ]; then
            
            for script in /etc/session.d/pam/onsessionclose/*.sh; do
                if [ -x $script ]; then
                    eval $script $userid $service
                fi
                
            done;
        fi;
    fi;
fi;

Einige Anmerkungen:
. wichtig: Frühversionen von shadow (wo das Anmeldeprogramm ein Teil davon ist) schlossen standardmäßig keine Sitzungen (Versionen vor 4.0.12). Sie müssen hinzufügen:

    CLOSE_SESSIONS yes

zu der /etc/login.defs-Datei.
Diese Option ist nicht dokumentiert und nicht in der von dem Shadow-Paket installierten login.defs-Datei vorhanden. Sie müssen sie selbst hinzufügen.
In neueren Versionen wurde diese Option entfernt: Die Sitzung wird immer geschlossen.

Das Ergebnis

Das verschlüsselte Verzeichnis enthält jetzt die Anmeldeinformationen, die nur für den Eigentümer und root verfügbar sind. Durch Erstellen einer Datei mount.cifs.conf mit diesen Werten:

touch /var/lib/encfs/$userid/unencrypted/mount.cifs.conf  
chmod 600 /var/lib/encfs/$userid/unencrypted/mount.cifs.conf  
  
echo "username=$userid" > /var/lib/encfs/$userid/unencrypted/mount.cifs.conf  
echo -n "password=" >> /var/lib/encfs/$userid/unencrypted/mount.cifs.conf  
cat /var/lib/encfs/$userid/unencrypted/password >> /var/lib/encfs/$userid/unencrypted/mount.cifs.conf

Jetzt ist das Mounten einer CIFS-Freigabe möglich mit:

/sbin/mount.cifs //fileserver/public /home/sbon/netshares/fileserver/public -o credentails=/var/lib/encfs/sbon/unencrypted/mount.cifs.conf,ip=192.168.0.2

wo das Verzeichnis /home/sbon/netshares/fileserver/public existiert und eine Freigabe “public” auf “fileserver” verfügbar ist, ein smb/cifs-Server mit der IP-Nummer 192.168.0.2.
Dieser Befehl muss als root ausgeführt werden. Root muss Zugriff auf das verschlüsselte Verzeichnis haben.

Ein weiteres Beispiel ist fusesmb, das Anmeldeinformationen verwenden kann, um das smb-Netzwerk zu durchsuchen. Dies geschieht nicht, indem eine separate Datei nur für die Anmeldeinformationen erstellt wird, sondern in der globalen Benutzerkonfigurationsdatei von fusesmb in ~/.smb/fusesmb.conf:
(hier erstelle ich eine einfache Konfigurationsdatei nur mit Anmeldeinformationen)

touch /var/lib/encfs/sbon/unencrypted/fusesmb.conf  
chmod 600 /var/lib/encfs/sbon/unencrypted/fusesmb.conf  
  
echo "[global]" > /var/lib/encfs/sbon/unencrypted/fusesmb.conf  
echo "username = sbon" >> /var/lib/encfs/sbon/unencrypted/fusesmb.conf  
echo -n "password = " >> /var/lib/encfs/sbon/unencrypted/fusesmb.conf  
cat /var/lib/encfs/sbon/unencrypted/password >> /var/lib/encfs/sbon/unencrypted/fusesmb.conf  
  
ln -sf /var/lib/encfs/sbon/unencrypted/fusesmb.conf /home/sbon/.smb/fusesmb.conf  

Der letzte Link ist, weil fusesmb (gestart von sbon) die Konfigurationsdatei dort erwartet.
Jetzt starten Sie es mit:

fusesmb /home/sbon/network

wenn ich als ich selbst (sbon) angemeldet bin. Das Verzeichnis /home/sbon/network muss existieren.

Share: X/Twitter LinkedIn

Erhalte neue Beiträge in deinem Posteingang.

Kein Spam. Jederzeit abmelden.