Безопасность · 10 min read · Jan 10, 2026

Создание безопасного каталога с PAM и EncFS

Создание безопасного каталога с PAM и EncFS

Содержание

. Введение
. Установка encfs
. Установка pam_script
. Настройка конфигурации PAM
. Результат

Введение

Я много работаю с программами, которые требуют учетные данные.
Примеры таких программ:
. mount.cifs
. fusesmb (подробности здесь)

Теперь в моей сети (и других) учетные данные, предоставленные при входе, могут (и должны) использоваться этими программами. Как можно получить эти учетные данные, обеспечив достаточную безопасность?
С помощью модуля PAM pam_script можно сохранить пароль в файл, который будет использоваться fusesmb и mount.cifs для чтения пароля.

Чтобы обеспечить безопасность, можно сделать пользователя, входящего в систему, владельцем и запретить чтение/запись для всех остальных. Удалите этот файл, когда пользователь завершит свою сессию.
Это достаточно для времени выполнения. Но мне было интересно, что будет, если система выйдет из строя, и файл с учетными данными останется на жестком диске? Любой, кто сможет смонтировать этот жесткий диск, например, с помощью lifecd, сможет прочитать этот файл!

Вот почему я искал способ зашифровать этот файл.

С помощью encfs это очень возможно! В режиме выполнения он предоставляет интерфейс для зашифрованных файлов и каталогов, которые существуют только во время выполнения! Когда система не работает, существуют только зашифрованные файлы, бесполезные, если вы не знаете ключ к ним. А этот ключ — это именно (зашифрованный) пароль! Вот почему я выбрал комбинацию PAM и Encfs.

Посмотрите на сайт Encfs: http://freshmeat.net/projects/encfs

Эта конструкция предназначена для обеспечения достаточной безопасности как во время работы, так и после сбоя (для хранения конфиденциальной информации), а не для создания постоянного безопасного каталога на вашем жестком диске для хранения документов.

Установка encfs

Конечно, FUSE должен быть установлен.
Установка очень проста:
(посмотрите на сайте rlog, который требуется для EncFs)

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

После установки вы можете проверить, работает ли это:

mkdir -p ~/test/encrypted  
mkdir -p ~/test/decrypted  
  
encfs ~/test/encrypted ~/test/decrypted  
  
mount (должен показать только что созданный монтирование)  
  
echo "Это очень секретно." > ~/test/decrypted/testfile

Каталог encrypted содержит зашифрованные файлы и остается на жестком диске после размонтирования и/или завершения работы. Каталог decrypted является интерфейсом к нему и исчезает при размонтировании и/или завершении работы.

Файл testfile должен появиться в расшифрованном каталоге и в зашифрованном виде в зашифрованном каталоге. Имя и содержимое файла зашифрованы.

Я выбрал отдельную папку на локальной машине, где для каждого пользователя, входящего в систему, будет храниться зашифрованный (и интерфейс к нему):

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

Обратите внимание на разрешения: каждый должен иметь возможность создать каталог здесь. Позже в этом документе объясняется, почему.

Установка pam_script

Установка очень проста. После команды make переместите библиотеку в правильный каталог, /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 использует некоторые параметры. Все они описаны в README в исходном каталоге. Важные параметры:

Общие параметры

  • runas=#user# : заставляет скрипт выполняться от имени пользователя #user#

Параметры только в части аутентификации

  • onauth=/path/to/onauth/script : путь к скрипту, который выполняется в части аутентификации
    по умолчанию это /etc/security/onauth

Только в части сессии

  • onsessionopen=/path/to/onsessionopen/script : путь к скрипту, который выполняется, когда (действительная) сессия начинается
    по умолчанию это /etc/security/onsessionopen

  • onsessionclose=/path/to/onsessionclose/script : путь к скрипту, который выполняется, когда сессия закрывается
    по умолчанию это /etc/security/onsessionclose

После установки более сложная задача — настроить систему для использования этого модуля.

Настройка конфигурации PAM

Я использовал pam_script в части аутентификации и в части сессии файла службы pam (login,kde).
Сначала я опишу, как настроить часть аутентификации, где pam_script используется более одного раза.

Часть аутентификации

Pam_script имеет возможность (с версии 0.1.5) получать пароль, предоставленный при входе, и делать его доступным для скриптов через переменную окружения PAM_AUTHTOK.

Цель здесь — создать безопасный каталог, в котором хранятся конфиденциальные данные (такие как учетные данные).

Модуль установлен в части аутентификации:

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

Как видите, я использую pam_scripts.so несколько раз:

  • В первый раз, чтобы запустить скрипт, который создает зашифрованный каталог (с помощью encfs) и записать секретный пароль в файл в этом каталоге для использования такими программами, чувствительными к учетным данным, как fusesmb и mount.cifs. Это делается непосредственно перед первым модулем аутентификации, следующим за pam_unix.

  • В последний раз, чтобы запустить скрипт, когда аутентификация не удалась. Когда предыдущие модули аутентификации не удались (pam_unix и pam_ldap) (и только тогда) этот модуль будет достигнут. Необходимо размонтировать зашифрованный каталог и удалить временные файлы.

  • Обратите внимание, что последний модуль — это pam_deny, он действительно необходим. Без него любой сможет войти в систему. Это связано с тем, что pam_script всегда возвращает “PAM_SUCCESS”, независимо от значения возврата скриптов.

Не забудьте добавить флаг “use_first_pass” к существующему модулю pam_unix.so.

Скрипты

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 
 
    # 
    # userproperties не найдены: что-то не так 
    # 
 
    echo "Пользователь не найден." 
    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 
 
    # создать безопасный 
 
    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; 
 
    #
    # проверьте, что зашифрованный каталог еще не смонтирован
    #
 
    if [ -z "$(mount | grep -w /var/lib/encfs/$userid/unencrypted )" ]; then 
 
        # 
        # создать программу для предоставления пароля 
        # 
 
        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; 
 
    # 
    # это то, о чем все: хранение учетных данных в файле 
    # в данном случае пароля 
    #  
 
    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; 

Некоторые замечания:

Этот скрипт создает зашифрованный каталог (если он еще не существует) с помощью encfs. Обратите внимание на опцию -S в encfs: пароль для этого зашифрованного каталога читается из stdin и не запрашивается.
Этот пароль такой же, как используется при входе.
Зашифрованный каталог находится по адресу /var/lib/encfs/$userid.

Пароль записывается в файл password.tmp. Это временный файл: пароль не обязательно должен быть правильным. Позже в процессе, в зависимости от успеха проверки, этот файл переименовывается (в password) или удаляется.

Очень важно отметить, что скрипт выполняется от имени пользователя, входящего в систему, а не от имени root! И поскольку такие программы, как mount.cifs, позже должны иметь доступ к этому каталогу для чтения файла пароля, добавляется общая опция fuse allow_root.

#!/bin/bash
userid=$1
service=$2
userproperties=$(getent passwd | grep -m 1 -E "^$userid")
if [ -z "$userproperties" ]; then
    #
    # userproperties не найдены: что-то не так
    #
    echo "Пользователь не найден."
    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
    if [ -d /var/lib/encfs/$userid/unencrypted ]; then
        #
        # проверьте, что зашифрованный каталог уже смонтирован
        #
        if [ -n "$(mount | grep -w /var/lib/encfs/$userid/unencrypted )" ]; then
            if [ $(w -h $userid | wc -l) -eq 0 ]; then
                #
                # этот пользователь не вошел в систему на других tty
                # просто удалите все и размонтируйте зашифрованный каталог
                #
                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
                #
                # этот пользователь все еще вошел в систему
                #
                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;

Некоторые замечания:

Этот скрипт выполняется, когда предоставленные учетные данные недействительны. Зашифрованный каталог, который только что был настроен, будет удален (размонтирован) и очищен. Это, конечно, только в том случае, если этот пользователь не вошел в систему на любом другом tty.

Часть сессии

Когда достигается часть сессии, это означает, что предоставленные учетные данные (пароль) верны. Это означает, что пароль — в фазе аутентификации, хранящийся во временном файле — верен. Поэтому одна из задач в фазе сессии — переместить содержимое password.tmp в постоянный файл, password.
Вторая задача — запустить скрипты, которые нуждаются в этих учетных данных для собственного использования, такие как монтирование CIFS-дисков или fusesmb.
Логично, что любая конфиденциальная информация остается внутри безопасного каталога. Вот о чем все это изначально!!

Обратите внимание, что этот модуль по умолчанию запускает два скрипта:
. /etc/security/onsessionopen: когда сессия начинается/открывается;
. /etc/security/onsessionclose: когда сессия заканчивается/закрывается.

Я следую по умолчанию, нет причин делать иначе.

Мой файл /etc/pam.d/login (часть сессии) выглядит так:

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

Скрипты

При открытии сессии

cat /etc/security/onsessionopen
#!/bin/bash
retcode=0;
userid=$1
service=$2
userproperties=$(getent passwd | grep -E "^$userid")
if [ -z "$userproperties" ]; then
    #
    # userproperties не найдены: что-то не так
    #
    echo "Пользователь не найден."
    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
    #
    # проверьте, что зашифрованный каталог смонтирован
    #
    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
                #
                # старая файл пароля найден 
                #
                if [ -z "(diff /var/lib/encfs/$userid/unencrypted/password /var/lib/encfs/$userid/unencrypted/password.tmp)" ]; then
                    #
                    # новый пароль и старый совпадают
                    #
                    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
                #
                # пароль не найден: это первый вход.
                # просто переместите временный файл пароля в оставшийся
                #
                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;

При закрытии сессии

cat >> /etc/security/onsessionclose
#!/bin/bash
userid=$1
service=$2
userproperties=$(getent passwd | grep -E "^$userid")
if [ -z "$userproperties" ]; then
    #
    # userproperties не найдены: что-то не так
    #
    echo "Пользователь не найден."
    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
    if [ -d /var/lib/encfs/$userid/unencrypted ]; then
        #
        # проверьте, что зашифрованный каталог уже смонтирован
        #
        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;

Некоторые замечания:
. важно: ранние версии shadow (где программа входа является частью) не закрывали сессии по умолчанию (версии до 4.0.12). Вам нужно будет добавить:

    CLOSE_SESSIONS yes

в файл /etc/login.defs.
Эта опция не документирована и не присутствует в файле login.defs, установленном пакетом Shadow. Вам нужно будет добавить ее самостоятельно.
В более новых версиях эта опция удалена: сессия всегда закрывается.

Результат

Зашифрованный каталог теперь содержит учетные данные, которые доступны только владельцу и root. Создав файл mount.cifs.conf с этими значениями:

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

Теперь монтирование CIFS-диска возможно с помощью:

/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

где каталог /home/sbon/netshares/fileserver/public существует, и имеется общий доступ “public” на “fileserver”, smb/cifs сервер с IP-адресом 192.168.0.2.
Эта команда должна выполняться от имени root. Root должен иметь доступ к зашифрованному каталогу.

Другим примером является fusesmb, который может использовать учетные данные для просмотра сетевого окружения smb. Это не делается путем создания отдельного файла только для учетных данных, а в глобальном конфигурационном файле fusesmb для каждого пользователя в ~/.smb/fusesmb.conf:
(здесь я создаю простой конфигурационный файл только с учетными данными)

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  

Последняя ссылка необходима, потому что fusesmb (запущенный пользователем sbon) ожидает, что конфигурационный файл будет находиться там.
Теперь запустите его с:

fusesmb /home/sbon/network

когда я вошел в систему как сам (sbon). Каталог /home/sbon/network должен существовать.*

Share: X/Twitter LinkedIn

Get new posts in your inbox

No spam. Unsubscribe anytime.