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 installApó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/testfileO 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/encfsNote 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.soPam_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/onsessionopenonsessionclose=/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.soComo 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.soOs 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 yesno 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.confAgora 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.2donde 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/networkquando estou logado como eu mesmo (sbon). O diretório /home/sbon/network deve existir.*
Receba novas postagens na sua caixa de entrada
Sem spam. Cancele a assinatura a qualquer momento.