Безопасность · 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.soPam_script.so использует некоторые параметры. Все они описаны в README в исходном каталоге. Важные параметры:
Общие параметры
- runas=#user# : заставляет скрипт выполняться от имени пользователя #user#
Параметры только в части аутентификации
- onauth=/path/to/onauth/script : путь к скрипту, который выполняется в части аутентификации
по умолчанию это /etc/security/onauth
Только в части сессии
onsessionopen=/path/to/onsessionopen/script : путь к скрипту, который выполняется, когда (действительная) сессия начинается
по умолчанию это /etc/security/onsessionopenonsessionclose=/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 должен существовать.*
Get new posts in your inbox
No spam. Unsubscribe anytime.