セキュリティ · 5 min read · Jan 10, 2026

PAM と EncFS を使用した安全なディレクトリの作成

PAM と EncFS を使用した安全なディレクトリの作成

目次

. はじめに
. encfs のインストール
. pam_script のインストール
. PAM 設定の調整
. 結果

はじめに

私は、資格情報を必要とするプログラムで多くの作業を行っています。
そのようなプログラムの例は次のとおりです:
. mount.cifs
. fusesmb (詳細はこちらを参照)

さて、私のネットワーク(および他のネットワーク)では、ログイン時に提供される資格情報は、これらのプログラムによって使用される可能性があり(そして使用されるべきです)、これらの資格情報をどのように取得し、十分なセキュリティを提供することができるでしょうか?
PAM モジュール pam_script を使用すると、パスワードをファイルに保存でき、そのファイルは fusembmount.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.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 を 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 は存在する必要があります。

Share: X/Twitter LinkedIn

新しい投稿を受信箱で受け取る

スパムはありません。いつでも購読を解除できます。