セキュリティ · 5 min read · Jan 10, 2026
PAM と EncFS を使用した安全なディレクトリの作成
PAM と EncFS を使用した安全なディレクトリの作成
目次
. はじめに
. encfs のインストール
. pam_script のインストール
. PAM 設定の調整
. 結果
はじめに
私は、資格情報を必要とするプログラムで多くの作業を行っています。
そのようなプログラムの例は次のとおりです:
. mount.cifs
. fusesmb (詳細はこちらを参照)
さて、私のネットワーク(および他のネットワーク)では、ログイン時に提供される資格情報は、これらのプログラムによって使用される可能性があり(そして使用されるべきです)、これらの資格情報をどのように取得し、十分なセキュリティを提供することができるでしょうか?
PAM モジュール pam_script を使用すると、パスワードをファイルに保存でき、そのファイルは fusemb と mount.cifs によってパスワードを読み取るために使用されます。
セキュリティを確保するために、ログインするユーザーを所有者にし、他の誰にも読み取り/書き込みを拒否することができます。ユーザーがセッションを終了したときにこのファイルを削除します。
これは、実行時には十分です。しかし、システムがクラッシュした場合、資格情報を含むファイルがハードドライブに残るとどうなるでしょうか?例えば、lifecd でこのハードドライブをマウントできる誰でも、このファイルを読むことができます!
だからこそ、私はこのファイルを暗号化する方法を探していました。
encfs を使用すると、これが非常に可能です!実行時には、実行時にのみ存在する暗号化されたファイルとディレクトリへのインターフェースを提供します!システムが実行されていないとき、そこには暗号化されたファイルしか存在せず、キーを知らないと無用です。そして、このキーはまさに(暗号化された)パスワードです!だからこそ、私は PAM と Encfs の組み合わせを選びました。
Encfs のウェブサイトを参照してください: http://freshmeat.net/projects/encfs
この構造は、機密情報を保存するための実行時およびダウンタイム(クラッシュ後)に十分なセキュリティを提供することを目的としており、ハードドライブ上に文書を保存するための永続的な安全なディレクトリを作成するためのものではありません。
encfs のインストール
もちろん FUSE をインストールする必要があります。
インストールは非常に簡単です:
(EncFs に必要な rlog のウェブサイトを参照してください)
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 を auth および session 部分の pam (login,kde) サービスファイルで使用しました。
最初に、pam_script が複数回使用される認証部分の調整方法を説明します。
認証部分
Pam_script は、ログイン時に提供されたパスワードを取得し、これを環境変数 PAM_AUTHTOK を介してスクリプトに利用可能にする能力があります(バージョン 0.1.5 以降)。
ここでの目的は、機密情報(資格情報など)を保存する安全なディレクトリを作成することです。
モジュールは認証部分にスタックされています:
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 を使用)、このディレクトリ内のファイルに秘密のパスワードを書き込むスクリプトを実行するためです。これは、最初の認証モジュールである pam_unix の前に行われます。
最後は、認証が成功しなかったときにスクリプトを実行するためです。前の認証モジュール(pam_unix と pam_ldap)が失敗したとき(そのときだけ)このモジュールに到達します。暗号化されたディレクトリをアンマウントし、一時ファイルを削除する必要があります。
最後のモジュールである pam_deny は、本当に必要です。これがないと、誰でもログインできてしまいます。これは、pam_script が常に「PAM_SUCCESS」を返すためであり、スクリプトの戻り値に関係なくです。
既存のモジュール pam_unix.so に「use_first_pass」フラグを追加することを忘れないでください。
スクリプト
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
#
# ユーザーのプロパティが見つかりません:何かが間違っています
#
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 を使用して暗号化されたディレクトリを作成します(すでに存在しない場合)。encfs の -S オプションに注意してください:この暗号化されたディレクトリのパスワードは標準入力から読み取られ、プロンプトは表示されません。
このパスワードは、ログイン時に使用されるものと同じです。
暗号化されたディレクトリは /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
#
# ユーザーのプロパティが見つかりません:何かが間違っています
#
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 に移動することです。
2 番目のことは、CIFS シェアのマウントや fusesmb のように、これらの資格情報を必要とするスクリプトを実行することです。
機密情報はすべて安全なディレクトリ内に留まるべきです。これは最初からの目的でした!!
このモジュールはデフォルトで 2 つのスクリプトを実行します:
. /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
#
# ユーザーのプロパティが見つかりません:何かが間違っています
#
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
#
# ユーザーのプロパティが見つかりません:何かが間違っています
#
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 の初期バージョン(login プログラムがその一部である)では、デフォルトでセッションが閉じられませんでした(バージョン 4.0.12 より前)。次のように追加する必要があります:
CLOSE_SESSIONS yesを /etc/login.defs ファイルに追加します。このオプションは文書化されておらず、Shadow パッケージによってインストールされた login.defs ファイルには存在しません。自分で追加する必要があります。
新しいバージョンでは、このオプションは削除され、セッションは常に閉じられます。
結果
暗号化されたディレクトリには、所有者と 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 が存在し、”fileserver” で “public” という共有が利用可能で、IP 番号が 192.168.0.2 の smb/cifs サーバーです。
このコマンドは root として実行する必要があります。root は暗号化されたディレクトリにアクセスする必要があります。
別の例は fusesmb で、資格情報を使用して smb ネットワークの近隣をブラウズできます。これは、資格情報専用の別のファイルを作成するのではなく、~/.smb/fusesmb.conf の fusesmb のグローバルユーザー設定ファイルに行います:
(ここでは、資格情報のみを持つシンプルな設定ファイルを作成します)
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 は存在する必要があります。
新しい投稿を受信箱で受け取る
スパムはありません。いつでも購読を解除できます。