Segurança de Dados · 11 min read · Jan 10, 2026

Criando Um Diretório Seguro Com PAM E EncFS

Criando Um Diretório Seguro Com PAM E EncFS

Conteúdos

. Introdução
. Instalação do encfs
. Instalação do pam_script
. Ajustando a configuração do PAM
. O resultado

Introdução

Eu trabalho muito com programas que requerem credenciais
Exemplos desses programas são:
. mount.cifs
. fusesmb (veja os detalhes aqui)

Agora, na minha rede (e em outras) as credenciais fornecidas no login poderiam (e deveriam) ser usadas por esses programas. Como você pode recuperar essas credenciais, fornecendo segurança suficiente?
Com o módulo PAM pam_script é possível armazenar a senha em um arquivo, que será usado pelo fusemb e mount.cifs para ler a senha.

Para alcançar segurança, pode-se tornar o usuário que está fazendo login o proprietário e negar leitura/escrita para qualquer outra pessoa. Remova este arquivo quando o usuário terminar sua sessão.
Isso é suficiente, para o tempo de execução. Mas eu estava me perguntando, e se o sistema travar, e o arquivo com as credenciais permanecer no disco rígido? Qualquer um que conseguir montar esse disco rígido com, por exemplo, um lifecd, pode ler esse arquivo!

É por isso que eu estava procurando uma maneira de criptografar esse arquivo.

Com o encfs isso é muito possível! Em tempo de execução, ele fornece uma interface para arquivos e diretórios criptografados, que só existem em tempo de execução! Quando o sistema não está em execução, existem apenas arquivos criptografados, inúteis quando você não conhece a chave para isso. E essa chave é exatamente a senha (criptografada)! É por isso que eu escolhi uma combinação de PAM e Encfs.

Veja o site do Encfs: http://freshmeat.net/projects/encfs

Essa construção tem como objetivo fornecer segurança suficiente para o tempo de execução e inatividade (após uma falha) para armazenar informações sensíveis, não para criar um diretório seguro permanente em seu disco rígido para armazenar documentos.

Instalação do encfs

Claro que o FUSE deve estar instalado.
A instalação é muito simples:
(veja o site para rlog que é necessário pelo EncFs)

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

Após a instalação, você pode testar se funciona:

mkdir -p ~/test/encrypted  
mkdir -p ~/test/decrypted  
  
encfs ~/test/encrypted ~/test/decrypted  
  
mount (deve mostrar o mount recém-criado)  
  
echo "Isso é muito secreto." > ~/test/decrypted/testfile

O diretório encrypted contém os arquivos criptografados e permanece no disco rígido após desmontar e/ou desligar. O diretório decrypted é a interface para ele e desaparece quando desmontado e/ou desligado.

O arquivo testfile deve aparecer no diretório decrypted e em forma criptografada no diretório encrypted. O nome e o conteúdo do arquivo estão criptografados.

Eu escolhi um mapa separado na máquina local onde para cada usuário que faz login será armazenado um criptografado (e uma interface para ele):

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

Note as permissões: todos devem ser capazes de criar um diretório aqui. Mais adiante neste documento é explicado o porquê.

Instalação do pam_script

A instalação é muito simples. Após um comando make, mova a biblioteca para o diretório apropriado, /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 usa alguns parâmetros. Todos eles estão descritos no README no diretório de origem. Importantes são:

parâmetros comuns

  • runas=#user#: faz o script chamado ser executado como usuário #user#

parâmetros apenas na parte de autenticação

  • onauth=/path/to/onauth/script: caminho para o script que é executado quando na parte de autenticação
    o padrão é /etc/security/onauth

apenas na parte da sessão

  • onsessionopen=/path/to/onsessionopen/script: caminho para o script que é executado quando uma sessão (válida) é iniciada
    o padrão é /etc/security/onsessionopen

  • onsessionclose=/path/to/onsessionclose/script: caminho para o script que é executado quando uma sessão é encerrada
    o padrão é /etc/security/onsessionclose

Após a instalação, a parte mais complexa é configurar o sistema para fazer uso deste módulo.

Ajustando a configuração do PAM

Eu usei pam_script na parte de autenticação e na parte da sessão do arquivo de serviço pam (login,kde).
Primeiro, descrevo como ajustar a parte de autenticação, onde pam_script é usado mais de uma vez.

A parte de autenticação

Pam_script tem a capacidade (a partir da versão 0.1.5) de obter a senha fornecida no login e torná-la disponível para scripts via uma variável de ambiente PAM_AUTHTOK.

O objetivo aqui é criar um diretório seguro onde informações confidenciais (como credenciais) são armazenadas.

O módulo é empilhado na parte de autenticação:

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

Como você pode ver, eu uso pam_scripts.so várias vezes:

  • A primeira vez para executar um script que cria um diretório criptografado (com encfs) e para escrever a senha secreta em um arquivo neste diretório para uso por programas sensíveis a credenciais como fusesmb e mount.cifs. Isso é feito logo antes do primeiro módulo de autenticação seguinte, pam_unix.

  • A última vez para executar um script quando a autenticação não é bem-sucedida. Quando os módulos de autenticação anteriores falham (pam_unix e pam_ldap) (e somente então) este módulo é alcançado. É necessário desmontar o diretório criptografado e remover arquivos temporários.

  • Note que o último módulo de todos é pam_deny, que é realmente necessário. Sem ele, qualquer um pode fazer login. Isso ocorre porque pam_script sempre retorna “PAM_SUCCESS”, não importa qual seja o valor de retorno dos scripts.

Não se esqueça de adicionar a flag “use_first_pass” ao módulo existente pam_unix.so.

Os scripts

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 não encontrado: algo errado 
    # 
 
    echo "Usuário não encontrado." 
    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 
 
    # criar um seguro 
 
    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; 
 
    #
    # teste se o diretório criptografado não está montado
    #
 
    if [ -z "$(mount | grep -w /var/lib/encfs/$userid/unencrypted )" ]; then 
 
        # 
        # criar um programa que fornece a senha 
        # 
 
        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; 
 
    # 
    # isso é o que importa: armazenar as credenciais em um arquivo 
    # neste caso, a senha 
    #  
 
    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; 

Algumas observações:

Este script cria um diretório criptografado (se já não existir) com encfs. Note a opção -S no encfs: a senha para este diretório criptografado é lida da entrada padrão e não solicitada.
Essa senha é a mesma usada no login.
O diretório criptografado está em /var/lib/encfs/$userid.

A senha é escrita em um arquivo, password.tmp. Este é um arquivo temporário: a senha não precisa ser a correta. Mais adiante no processo, dependendo do sucesso da validação, este arquivo é renomeado (para password) ou removido.

Muito importante notar que o script é executado como o usuário que está fazendo login, não como root! E porque programas como mount.cifs precisam ter acesso a este diretório para ler o arquivo de senha, a opção comum do fuse allow_root é adicionada.

#!/bin/bash
userid=$1
service=$2
userproperties=$(getent passwd | grep -m 1 -E "^$userid")
if [ -z "$userproperties" ]; then
    #
    # userproperties não encontrado: algo errado
    #
    echo "Usuário não encontrado."
    exit
fi;
homedir=$(echo $userproperties | cut -d ":" -f 6);
gidnr=$(echo $userproperties | cut -d ":" -f 4);
uidnr=$(echo $userproperties | cut -d ":" -f 3);
#
# este script é executado quando a autenticação falhou
# um seguro criptografado ainda é criado
# então é importante remover este seguro novamente
# 
if [ -d /var/lib/encfs ]; then
    if [ -d /var/lib/encfs/$userid/unencrypted ]; then
        #
        # teste se o diretório criptografado já está montado
        #
        if [ -n "$(mount | grep -w /var/lib/encfs/$userid/unencrypted )" ]; then
            if [ $(w -h $userid | wc -l) -eq 0 ]; then
                #
                # este usuário não está logado em mais tty's
                # apenas remova tudo e desmonte o diretório criptografado
                #
                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
                #
                # este usuário ainda está logado
                #
                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;

Algumas observações:

Este script é executado quando as credenciais fornecidas não são válidas. O diretório criptografado, que acabou de ser configurado, será removido (desmontado) novamente e limpo. Isso é claro, somente quando este usuário não está logado em nenhum outro tty.

A parte da sessão

Quando a parte da sessão é alcançada, é certo que as credenciais fornecidas (senha) estão corretas. Isso significa que a senha - na fase de autenticação armazenada em um arquivo temporário - é correta. Portanto, uma coisa a fazer na fase da sessão é mover o conteúdo de password.tmp para o permanente, password.

Uma segunda coisa é executar scripts que precisam dessas credenciais para seu próprio uso, como montar compartilhamentos CIFS ou fusesmb.
É lógico que qualquer informação confidencial permaneça dentro do diretório seguro. Isso era o que importava em primeiro lugar!!

Note que este módulo executa dois scripts por padrão:
. /etc/security/onsessionopen: quando uma sessão começa/abre;
. /etc/security/onsessionclose: quando uma sessão termina/fecha.

Eu sigo o padrão, não há razão para fazer diferente.

Meu arquivo /etc/pam.d/login (a parte da sessão) é assim:

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

Os scripts

Na abertura da sessão

cat /etc/security/onsessionopen
#!/bin/bash
retcode=0;
userid=$1
service=$2
userproperties=$(getent passwd | grep -E "^$userid")
if [ -z "$userproperties" ]; then
    #
    # userproperties não encontrado: algo errado
    #
    echo "Usuário não encontrado."
    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
    #
    # teste se o diretório criptografado está montado
    #
    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
                #
                # um arquivo de senha antigo encontrado 
                #
                if [ -z "(diff /var/lib/encfs/$userid/unencrypted/password /var/lib/encfs/$userid/unencrypted/password.tmp)" ]; then
                    #
                    # nova senha e antiga são as mesmas
                    #
                    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
                #
                # senha não encontrada: é o primeiro login.
                # apenas mova o arquivo de senha temporário para o restante
                #
                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;

Na fechamento da sessão

cat >> /etc/security/onsessionclose
#!/bin/bash
userid=$1
service=$2
userproperties=$(getent passwd | grep -E "^$userid")
if [ -z "$userproperties" ]; then
    #
    # userproperties não encontrado: algo errado
    #
    echo "Usuário não encontrado."
    exit
fi;
homedir=$(echo $userproperties | cut -d ":" -f 6);
gidnr=$(echo $userproperties | cut -d ":" -f 4);
uidnr=$(echo $userproperties | cut -d ":" -f 3);
#
# este script é executado quando a autenticação falhou
# um seguro criptografado ainda é criado
# então é importante remover este seguro novamente
# 
if [ -d /var/lib/encfs ]; then
    if [ -d /var/lib/encfs/$userid/unencrypted ]; then
        #
        # teste se o diretório criptografado já está montado
        #
        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;

Algumas observações:
. importante: versões iniciais do shadow (onde o programa de login é parte) não fechavam sessões por padrão (versões anteriores a 4.0.12). Você terá que adicionar:

    CLOSE_SESSIONS yes

no arquivo /etc/login.defs.
Esta opção não está documentada e não está presente no arquivo login.defs instalado pelo pacote Shadow. Você terá que adicioná-la você mesmo.
Nas versões mais novas, essa opção foi removida: a sessão é sempre fechada.

O resultado

O diretório criptografado agora contém as credenciais, que estão disponíveis apenas para o proprietário e root. Ao criar um arquivo mount.cifs.conf com esses valores:

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

Agora montar um compartilhamento cifs é possível com:

/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

donde o diretório /home/sbon/netshares/fileserver/public já existe, e há um compartilhamento “public” disponível no “fileserver”, um servidor smb/cifs com número de ip 192.168.0.2.
Este comando deve ser executado como root. O root precisa ter acesso ao diretório criptografado.

Um outro exemplo é fusesmb, que pode usar credenciais para navegar na rede smb. Isso não é feito criando um arquivo separado apenas para as credenciais, mas no arquivo de configuração global por usuário do fusesmb em ~/.smb/fusesmb.conf:
(aqui eu crio um arquivo de configuração simples apenas com credenciais)

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  

O último link é porque fusesmb (iniciado por sbon) espera o arquivo de configuração lá.
Agora inicie com:

fusesmb /home/sbon/network

quando estou logado como eu mesmo (sbon). O diretório /home/sbon/network deve existir.*

Share: X/Twitter LinkedIn

Receba novas postagens na sua caixa de entrada

Sem spam. Cancele a assinatura a qualquer momento.