보안 · 9 min read · Jan 10, 2026

PAM과 EncFS로 안전한 디렉토리 만들기

PAM과 EncFS로 안전한 디렉토리 만들기

목차

. 소개
. encfs 설치
. pam_script 설치
. PAM 구성 조정
. 결과

소개

저는 자격 증명이 필요한 프로그램과 많은 작업을 합니다.
그러한 프로그램의 예는 다음과 같습니다:
. mount.cifs
. fusesmb (자세한 내용은 여기에서 확인)

이제 제 네트워크(및 다른 네트워크)에서 로그인 시 제공된 자격 증명은 이러한 프로그램에서 사용될 수 있습니다(그리고 사용되어야 합니다). 이러한 자격 증명을 어떻게 안전하게 검색할 수 있을까요?
PAM 모듈 pam_script를 사용하면 비밀번호를 파일에 저장할 수 있으며, 이 파일은 fusesmbmount.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 "This is very secret." > ~/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를 여러 번 사용합니다:

  • 첫 번째는 암호화된 디렉토리를 생성하고 자격 증명에 민감한 프로그램인 fusesmb 및 mount.cifs에서 사용할 비밀 비밀번호를 이 디렉토리의 파일에 쓰는 스크립트를 실행하기 위해 사용됩니다. 이는 첫 번째 인증 모듈인 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 옵션에 유의하세요: 이 암호화된 디렉토리의 비밀번호는 stdin에서 읽히며 프롬프트가 표시되지 않습니다.
이 비밀번호는 로그인 시 사용된 것과 동일합니다.
암호화된 디렉토리는 /var/lib/encfs/$userid에 있습니다.
비밀번호는 password.tmp라는 파일에 기록됩니다. 이는 임시 파일입니다: 비밀번호가 올바를 필요는 없습니다. 나중에 프로세스에서 유효성 검증의 성공 여부에 따라 이 파일은 이름이 변경되거나(비밀번호로) 제거됩니다.
매우 중요한 점은 이 스크립트가 로그인하는 사용자로 실행된다는 것입니다. 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로 이동하는 것입니다.
두 번째는 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
    #
    # 사용자 속성을 찾을 수 없음: 뭔가 잘못됨
    #
    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(로그인 프로그램이 포함된 버전)는 기본적으로 세션을 종료하지 않았습니다(버전 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” 공유가 있어야 합니다.
이 명령은 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

새 게시물을 받은 편지함에서 받기

스팸은 없습니다. 언제든지 구독 해지 가능합니다.