Quando o diretório pessoal do usuário estiver criptografado com ecryptfs
sshd
não poderá ler o arquivo authorized_keys
do diretório pessoal do usuário antes que o diretório pessoal seja montado.
Durante o login sshd
usará pam
para autenticar o usuário e pam
usará a senha digitada pelo usuário para montar o diretório pessoal criptografado.
Isso é problemático se você quiser restringir sshd
para permitir somente a autenticação de chave pública.
No entanto, você também pode colocar um arquivo authorized_keys
não criptografado no servidor. Isso permitirá que o usuário faça o login usando uma chave, mas como isso não invoca pam
, o diretório inicial não será montado e a montagem do diretório inicial sem conhecer a senha também não funcionará.
Como o diretório inicial não criptografado fica oculto pelo diretório inicial criptografado, colocar o arquivo authorized_keys
não criptografado em primeiro lugar pode ser um pouco complicado. Uma montagem de ligação do sistema de arquivos subjacente pode ajudar com isso.
Se, por exemplo, /home
for apenas um diretório no sistema de arquivos raiz, você poderá fazer o seguinte:
mkdir /mnt/rootfs
mount --bind / /mnt/rootfs
E então você pode criar /mnt/rootfs/home/$USER/.ssh/authorized_keys
Há mais coisas que você pode fazer. Como a versão criptografada e não criptografada de authorized_keys
são dois arquivos diferentes, você pode colocar diferentes conteúdos neles. Por exemplo, a versão não criptografada pode invocar um script para montar o diretório inicial criptografado:
command="/usr/local/bin/ecryptfs-mount-from-ssh" ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDM1Ot12ThbTcPOGpfh7AiRqp3P4BMm3DNo4mDg7gDFPwCmM9rKRHTH0fBVSqkSGlXm84q29bckDukg7vfqkbTpbkP3e2YmTkP6p1J2SoX2QMUnBRRgL9It/ZiAfA2I4QzUrcywVvokO1F2DqcRLy5e5wKTUFfvIm6D2QfBmGbnW2Kkpn16hQyLT1ClXjFC1qXUhazePv0cAtWUCUGjRcLr/ipOphS7eOB46cGhYqtbMkKx0t93ZG4f6jM0o32cYy3RqprpZpTmCeG1gDyG+IlSLBYXYggr72iwTKsTZ9pMDTCBQ8Pb7l317TPOcJzTtDxnpgpGE3x4Vu/Ww+zhsIeT kasperd 2014 May 24
A parte importante é o command
especificado antes da chave. Isso é invocado em vez do shell. Mas isso acontece somente quando essa chave pública específica é usada e somente se o diretório pessoal do usuário não estiver montado.
Se o diretório pessoal do usuário já estiver montado, esse arquivo authorized_keys
ficará oculto e a versão criptografada será usada. A versão criptografada de authorized_keys
não tem o command
, portanto, o script para montar o diretório inicial não é executado.
Então, o que acontece no script. Aqui está a minha versão:
#!/bin/bash -e
if [ $# = 1 ]
then
PUBKEY="$(
grep "$1" "$HOME/.ssh/authorized_keys" |
sed -e 's/.* ssh-rsa //;s/ .*//')"
/usr/local/bin/ssh-agent-ecryptfs-decryption.py "$PUBKEY" "$1" |
ecryptfs-unwrap-passphrase "$HOME/.ecryptfs-ssh-wrapped/$1" - |
ecryptfs-add-passphrase --fnek
fi
ecryptfs-mount-private
cd "$HOME"
if [ "$SSH_ORIGINAL_COMMAND" != "" ]
then
exec /bin/bash -c "$SSH_ORIGINAL_COMMAND"
fi
exec /bin/bash -l
No exemplo acima, o arquivo authorized_keys
é invocado sem argumentos, portanto, o primeiro bloco if
é ignorado. O comando ecryptfs-mount-private
solicitará a senha do usuário. Mas isso não exige que sshd
tenha a autenticação por senha ativada e, portanto, funcionará somente no sshd
com autenticação de chave pública.
O próximo comando será alterado para o diretório inicial criptografado do usuário (até que o script seja executado dentro do diretório inicial não criptografado).
A última parte do script executará o comando fornecido como argumento para o comando ssh
, se houver, ou o shell de login do usuário, se nenhum comando tiver sido fornecido.
Uma ressalva é que isso não funciona com o encaminhamento do X11, porque o diretório inicial ainda não está disponível, quando o cookie seria armazenado. Mas qualquer outra sessão aberta enquanto o diretório home já estiver montado, será capaz de lidar com o encaminhamento do X11.
Usar o ~/.ssh/rc
poderia resolver o problema de encaminhamento do X11. Isso é algo que eu não olhei ainda.
O primeiro bloco if
é um hack, que eu criei para permitir que o diretório pessoal do usuário seja montado sem precisar de senha. Em vez disso, ele usa um ssh-agent
encaminhado para montar o diretório pessoal do usuário. Essa parte vem com isenções de responsabilidade sobre não ter tido nenhuma revisão por pares, então confiar na criptografia no ssh-agent-ecryptfs-decryption.py
é inteiramente por sua conta e risco.
O script python é assim:
#!/usr/bin/env python
from sys import argv
from os import environ
import socket
s = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
s.connect(environ['SSH_AUTH_SOCK'])
def encode_int(v):
return ('%08x' % v).decode('hex')
def encode_string(s):
return encode_int(len(s)) + s
def encode_mpint(v):
h = '%x' % v
if len(h) & 1: h = '0' + h
return ('%04x%s' % (len(h) * 4, h)).decode('hex')
key_blob = argv[1].decode('base64')
msg = 'ecryptfs-decrypt ' + argv[2]
s.send(encode_string(chr(13) +
encode_string(key_blob) +
encode_string(msg) +
encode_int(0)))
response = s.recv(1024)
assert response == encode_string(chr(14) + response[5:]), argv[1]
passphrase = response[-48:].encode('base64').replace('\n', '')
print passphrase
Então, como funciona a descriptografia? Primeiro de tudo, o argumento para o script como fornecido por authorized_keys
é qualquer valor aleatório. Um uuid gerado com uuidgen
poderia funcionar. O script de shell usa o grep para encontrar a linha relevante no arquivo authorized_keys
para extrair a chave pública.
A chave pública codificada em base64, bem como o uuid, são fornecidos ao script python. A chave pública usada é exatamente aquela com a qual o usuário se autenticou. O script python solicita ao agente encaminhado uma assinatura em uma mensagem específica usando a chave pública em questão (porque as mensagens de assinatura são exatamente o que o ssh-agent
pode fazer). Parte da assinatura é então codificada com base64 para produzir uma senha.
Esta senha é usada para descriptografar um arquivo de senha ecryptfs
wrapped, mas o arquivo principal é criptografado usando a senha de login do usuário. Este é criptografado com uma senha gerada a partir da chave ssh.