ZFS on LUKS
Autor: * Coffnix
"Home Network Defense"
Utilize criptografia pra tudo
Motivo
Pelo visto você é um bom menino (ou menine kekekeke), que criptografou seu disco certinho, e seguiu as boas práticas recomendadas pelo área31 hackerspace lendo os artigos HackForge e aprendeu até a criar seu próprio storage criptografado em roteador residencial. Aprendeu inclusive a desbloquear seu LUKS durante o boot usando um device USB. Logo, agora vc quer um espelho de discos semelhante ao RAID-1, só que usando ZFS on Linux. Então se divirta :D
É fundamental reconhecer que, embora indesejado, o acesso não autorizado aos seus arquivos é uma possibilidade real. Muitas pessoas acreditam na inviolabilidade de seus dados, confiando nas promessas de segurança feitas por empresas, ou na suposta eficácia de seus sistemas operacionais e softwares de proteção. No entanto, é um equívoco comum subestimar que, com a motivação correta e acesso físico, qualquer zé mané da esquina com conhecimentos básicos, ou em um cenário mais extremo, entidades governamentais (sim, agentes do ESTADO), podem acessar ou comprometer seus dados. Portanto, a implementação de criptografia em nível de sistema de arquivos em seus sistemas não é apenas recomendada, mas essencial. A mensagem aqui é clara: não negligencie a segurança dos seus dados. Priorize a criptografia abrangente para garantir sua proteção, ou seja, CRIPTOGRAFE TUDO, estúpido!
Requisitos
Aqui utilizamos OpenSUSE + ZFS on Linux, mas você tem liberdade de utilizar em qualquer distro, adapte somente os comandos e arquivos caso utilize outra distro.
Procedimento
Instale o ZFS
Configure o repositório "Filesystem Tools" criando o arquivo abaixo:
/etc/zypp/repos.d/repo-filesystems.repo
[filesystems]
name=Filesystem tools and FUSE-related packages (openSUSE_Tumbleweed)
enabled=1
autorefresh=1
baseurl=https://download.opensuse.org/repositories/filesystems/openSUSE_Tumbleweed/
type=rpm-md
gpgcheck=1
gpgkey=https://download.opensuse.org/repositories/filesystems/openSUSE_Tumbleweed/repodata/repomd.xml.key
Agora atualize o cache e instale os pacotes necessários:
root # zypper refresh root # zypper in zfs-kmp-default zfs-ueficert zfs
Adicione o ZFS ao boot:
root # systemctl enable zfs-import-cache root # systemctl enable zfs-mount
Configure o LUKS
Agora configure crie o luks em cada disco. Lembre-se de alterar os ID dos devices de "ata-ST8000NE001-2M7101_WSD0SC28" e "ata-ST8000NE001-2M7101_WSD0SC30" para os seus:
Há um bug em aberto no ZFS, eu caí nele inclusive, logo por enquanto use --type luks1 ao invés de luks2. Mais infos: https://github.com/openzfs/zfs/issues/14533
root # mkdir -p /etc/area31/headerstore root # cryptsetup luksFormat --force-password --type luks1 --cipher aes-xts-plain64 --hash sha512 --key-size 512 /dev/disk/by-id/ata-ST8000NE001-2M7101_WSD0SC28 --align-payload 8192 --header /etc/area31/headerstore/ata-ST8000NE001-2M7101_WSD0SC28.img root # cryptsetup luksFormat --force-password --type luks1 --cipher aes-xts-plain64 --hash sha512 --key-size 512 /dev/disk/by-id/ata-ST8000NE001-2M7101_WSD0SC30 --align-payload 8192 --header /etc/area31/headerstore/ata-ST8000NE001-2M7101_WSD0SC30.img
Crie uma chave para desbloqueio do LUKS:
root # mkdir -p /opt/area31/keystore/keyfile ; dd if=/dev/urandom of=/opt/area31/keystore/keyfile bs=4096 count=1 root # chmod 400 /opt/area31/keystore/keyfile
Inclua a chave em ambos os discos:
root # cryptsetup luksAddKey /dev/disk/by-id/ata-ST8000NE001-2M7101_WSD0SC30 --header /etc/area31/headerstore/ata-ST8000NE001-2M7101_WSD0SC30.img /opt/area31/keystore/keyfile root # cryptsetup luksAddKey /dev/disk/by-id/ata-ST8000NE001-2M7101_WSD0SC28 --header /etc/area31/headerstore/ata-ST8000NE001-2M7101_WSD0SC28.img /opt/area31/keystore/keyfile
Monte usando a chave:
root # cryptsetup luksOpen /dev/disk/by-id/ata -ST8000NE001-2M7101_WSD0SC30 --header /etc/area31/headerstore/ata-ST8000NE001-2M7101_WSD0SC30.img --key-file=/opt/area31/keystore/keyfile storage_disk1_luks root # cryptsetup luksOpen /dev/disk/by-id/ata-ST8000NE001-2M7101_WSD0SC28 --header /etc/area31/headerstore/ata-ST8000NE001-2M7101_WSD0SC28.img --key-file=/opt/area31/keystore/keyfile storage_disk2_luks
Configure o mirror ZFS
Crie um novo pool ZFS como mirror com 2 dispositivos LUKS montados previamente, com nome vdisk0 montado em /storage:
root # mkdir -p /storage ; chattr +i /storage root # zpool create -f -o ashift=12 -m /storage vdisk0 mirror /dev/mapper/storage_disk1_luks /dev/mapper/storage_disk2_luks
Inicie o serviço ZFS para montar o pool sem precisar reiniciar:
root # systemctl start zfs-mount root # systemctl start zfs-import-cache
Para desmontar /storage (se necessário):
root # zpool export vdisk0
Para remontar o /storage (se necessário):
root # zpool import vdisk0
Habilite o 'relatime' no pool ZFS para otimizar o acesso a tempos de leitura:
root # zfs set relatime=on vdisk0
Ativar a compressão lz4 para economizar espaço no pool ZFS:
root # zfs set compression=lz4 vdisk0
Ative a deduplication (IMPORTANTE!!!):
root # zfs set dedup=on vdisk0
Verifique se dedup está ativo:
root # zfs get dedup vdisk0
Para checar o status do ZFS:
root # zfs get all vdisk0 root # zpool status
Liste todos os datasets ZFS:
root # zfs list -o name,mountpoint,used,avail,refer
Ative cache em SSD ou NVME
Adicione um NVME ou SSD como cache ao pool ZFS. Basta criar uma partição comum do tipo 8e (Linux) entre 50GB e 100GB e depois adiciona-lo. Ex:
root # zpool add vdisk0 cache /dev/nvme0n1p7
Montagem automática do ZFS
Crie o config para o script:
root # mkdir -p /opt/area31/conf ; touch /opt/area31/conf/mount-luks-zfs.conf
Com o seguinte conteúdo:
/opt/area31/bin/mount-luks-zfs.sh
DISK_ID_01="ata-ST8000NE001-2M7101_WSD0SC30"
DISK_ID_02="ata-ST8000NE001-2M7101_WSD0SC28"
LUKS_DISK_01="storage_disk1_luks"
LUKS_DISK_02="storage_disk2_luks"
LUKS_KEYFILE="/etc/area31/keystore/keyfile"
DIR_LUKS_HEADER="/etc/area31/headerstore"
DIR_ZFS="/storage"
ZFS_VDISK_01="vdisk0"
Agora crie o script para montagem automática do ZFS:
root # mkdir -p /opt/area31/bin ; touch /opt/area31/bin/mount-luks-zfs.sh ; chmod +x /opt/area31/bin/mount-luks-zfs.sh
Com o seguinte conteúdo:
/opt/area31/bin/mount-luks-zfs.sh
(bash source code) #!/bin/bash
###########################################################################
# Verifica se o script já está em execução
SCRIPT_NAME="${0##*/}"
if [ "$(pgrep -cx "${SCRIPT_NAME}")" -gt 1 ]; then
echo "O script já está em execução, aguarde 10 segundos para tentar novamente."
sleep 10
if [ "$(pgrep -cx "${SCRIPT_NAME}")" -gt 1 ]; then
echo "O script já está em execução e não será executado novamente."
exit 1
fi
fi
###########################################################################
# Iniciando script
CNF="/opt/area31/conf/mount-luks-zfs.conf"
# Checa se existe o arquivo de CONF, caso não exista ele sai
if [ -f "${CNF}" ] && [ ! -z "${CNF}" ]; then
source ${CNF}
else
echo "${CNF} não encontrado."
exit 1
fi
ERROR="0"
# Lista de variáveis para verificar
variables=("DIR_ZFS" "ZFS_VDISK_01" "DIR_LUKS_HEADER" "LUKS_KEYFILE" "LUKS_DISK_02" "LUKS_DISK_01" "DISK_ID_02" "DISK_ID_01")
# Função para verificar variável
check_variable() {
local var_name="$1"
# Usando indireção para pegar o valor da variável
local actual_value="${!var_name}"
# Verificar se a variável está definida e não está vazia
if [[ -z "$actual_value" ]]; then
echo "Erro: $var_name não está definido ou está vazio!"
return 1
fi
}
# Verificar todas as variáveis do CONF antes de iniciar o script
for var in "${variables[@]}"; do
check_variable "$var" | | exit 1
done
###########################################################################################################
check_pool_zfs_active(){
CHECK_MOUNTED="$(mount | grep zfs | grep "${DIR_ZFS}" | wc -l)"
if [ "${CHECK_MOUNTED}" -ne 0 ]; then
CHECK_NONE_ZFS="$(zpool status | grep 'no pools available' | wc -l 2> /dev/null)"
if [ "${CHECK_NONE_ZFS}" -ne 0 ]; then
ZFS_UMOUNTED="1"
else
ZFS_UMOUNTED="0"
fi
else
echo -e "/storage desmontado e ZFS inativo"
exit 0
fi
}
umount_luks(){
CHECK_LUKS_DISK_01="$(lsblk -o NAME,TYPE,MOUNTPOINT | grep -w 'crypt' | grep ${LUKS_DISK_01} | wc -l)"
CHECK_LUKS_DISK_02="$(lsblk -o NAME,TYPE,MOUNTPOINT | grep -w 'crypt' | grep ${LUKS_DISK_02} | wc -l)"
if [ "${CHECK_LUKS_DISK_01}" -ne 0 ]; then
cryptsetup luksClose "${LUKS_DISK_01}"
if [ "$?" -ne 0 ]; then
echo -e "Erro ao desmontar o LUKS \"${LUKS_DISK_01}\""
ERROR="1"
else
echo -e "Sucesso ao desmontar o LUKS \"${LUKS_DISK_01}\""
fi
else
echo "LUKS \"${LUKS_DISK_01}\" desmontado."
fi
if [ "${CHECK_LUKS_DISK_02}" -ne 0 ]; then
cryptsetup luksClose "${LUKS_DISK_02}"
if [ "$?" -ne 0 ]; then
echo -e "Erro ao tentar desmontar o LUKS \"${LUKS_DISK_02}\""
ERROR="1"
else
echo -e "Sucesso ao desmontar o LUKS \"${LUKS_DISK_02}\""
fi
else
echo "LUKS \"${LUKS_DISK_02}\" desmontado."
fi
}
mount_zfs(){
# Checa se o disco está montado
CHECK_LUKS_DISK_01="$(lsblk -o NAME,TYPE,MOUNTPOINT | grep -w 'crypt' | grep ${LUKS_DISK_01} | wc -l)"
CHECK_LUKS_DISK_02="$(lsblk -o NAME,TYPE,MOUNTPOINT | grep -w 'crypt' | grep ${LUKS_DISK_02} | wc -l)"
# monte usando a chave do pendrive
if [ "${CHECK_LUKS_DISK_01}" -eq 0 ]; then
echo -e "Tentando montar o LUKS \"${LUKS_DISK_01}\" do dispositivo \"${DISK_ID_01}\"..."
cryptsetup luksOpen "/dev/disk/by-id/${DISK_ID_01}" --header "${DIR_LUKS_HEADER}/${DISK_ID_01}.img" --key-file="${LUKS_KEYFILE}" "${LUKS_DISK_01}"
if [ "$?" -ne 0 ]; then
ERROR="1"
exit 1
else
echo -e "Sucesso ao montar os dispositivos LUKS do dispositivo \"${DISK_ID_01}\"."
fi
fi
if [ "${CHECK_LUKS_DISK_02}" -eq 0 ]; then
echo -e "Tentando montar o LUKS \"${LUKS_DISK_02}\" do dispositivo \"${DISK_ID_02}\"..."
cryptsetup luksOpen "/dev/disk/by-id/${DISK_ID_02}" --header "${DIR_LUKS_HEADER}/${DISK_ID_02}.img" --key-file="${LUKS_KEYFILE}" "${LUKS_DISK_02}"
if [ "$?" -ne 0 ]; then
ERROR="1"
exit 1
else
echo -e "Sucesso ao montar os dispositivos LUKS do dispositivo \"${DISK_ID_02}\"."
fi
fi
zfs get all "${ZFS_VDISK_01}" &> /dev/null
if [ "$?" -eq 0 ]; then
echo -e "ZFS inicializado. Caso deseje utilize $0 check ou $0 umount"
else
if [ "${ERROR}" -eq 0 ]; then
echo -e "LUKS montado. Tentando inicializar o ZFS..."
zpool import "${ZFS_VDISK_01}"
if [ "$?" -ne 0 ]; then
ERROR="1"
exit 1
else
echo -e "Sucesso ao inicializar o ZFS.\n$(df -hT "${DIR_ZFS}")"
fi
fi
fi
}
case "$1" in
check)
zfs get all "${ZFS_VDISK_01}"
zpool status "${ZFS_VDISK_01}"
;;
umount)
CHECK_MOUNTED="$(mount | grep zfs | grep "${DIR_ZFS}" | wc -l)"
if [ "${CHECK_MOUNTED}" -eq 0 ]; then
echo -e "ZFS desmontado. Tentando desmontar o LUKS..."
umount_luks
else
zpool export "${ZFS_VDISK_01}"
if [ "$?" -ne 0 ]; then
echo -e "Erro ao tentar desmontar o ZFS \"${ZFS_VDISK_01}\""
else
umount_luks
fi
fi
;;
*)
mount_zfs
;;
esac
Configure o systemd
Caso queira que o ZFS seja montado automaticamente no processo de boot, crie o seguinte arquivo:
/etc/systemd/system/mount-luks-zfs.service
[Unit]
Description=mount-luks-zfs
Wants=networking.service
After=networking.service
[Service]
Type=simple
RemainAfterExit=yes
ExecStart=/opt/area31/bin/mount-luks-zfs.sh
ExecStop=/opt/area31/bin/mount-luks-zfs.sh umount
ExecReload=/opt/area31/bin/mount-luks-zfs.sh
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
Ative e inicie:
root # systemctl enable /etc/systemd/system/mount-luks-zfs.service root # systemctl start mount-luks-zfs.service
Seja feliz! :D