A história começou há muito tempo, quando o Centos 7 (RHEL 7) foi lançado. Se você usou criptografia em unidades com Centos 6, não houve problemas com o desbloqueio automático de unidades ao conectar uma unidade flash USB com as chaves necessárias. Porém, quando o 7 foi lançado, de repente nem tudo funcionou como você estava acostumado. Então foi possível encontrar uma solução para retornar o dracut ao sysvinit usando uma linha simples na configuração: echo 'omit_dracutmodules+=" systemd "' > /etc/dracut.conf.d/luks-workaround.conf
O que nos privou imediatamente de toda a beleza do systemd - lançamento rápido e paralelo de serviços do sistema, o que reduziu significativamente o tempo de inicialização do sistema.
As coisas ainda estão lá:
Sem esperar solução, eu mesmo fiz e agora estou compartilhando com o público, quem tiver interesse, continue lendo.
Introdução
O Systemd, quando comecei a trabalhar com o Centos 7, não causou nenhuma emoção, pois além de uma pequena mudança na sintaxe de gerenciamento de serviços, não senti muita diferença no início. Posteriormente, gostei do systemd, mas a primeira impressão foi um pouco estragada, já que os desenvolvedores do dracut não gastaram muito tempo apoiando o processo de inicialização usando o systemd em conjunto com a criptografia de disco. No geral funcionou, mas digitar a senha do disco toda vez que o servidor inicializa não é o mais interessante.
Depois de tentar várias recomendações e estudar o manual, percebi que no modo systemd a configuração com USB é possível, mas apenas com a associação manual de cada disco a uma chave em um disco USB, e o próprio disco USB só pode ser associado por seu UUID, LABEL não funcionou. Não era muito conveniente manter isso em casa, então acabei mergulhando na espera e, depois de esperar quase 7 anos, percebi que ninguém iria resolver o problema.
Problemas
Claro, quase qualquer um pode escrever seu próprio plugin para o dracut, mas fazê-lo funcionar não é mais tão fácil. Acontece que devido à natureza paralela da inicialização do systemd, não é tão fácil incluir seu código e alterar o progresso do carregamento. A documentação do dracut não explicava tudo. No entanto, após longas experiências, consegui resolver o problema.
Como funciona
É baseado em três unidades:
- luks-auto-key.service - procura unidades com chaves para LUKS
- luks-auto.target - atua como uma dependência para unidades integradas do systemd-cryptsetup
- luks-auto-clean.service - limpa arquivos temporários criados por luks-auto-key.service
E luks-auto-generator.sh é um script iniciado pelo systemd e gera unidades com base nos parâmetros do kernel. Geradores semelhantes são criados por unidades fstab, etc.
luks-auto-generator.sh
Usando drop-in.conf, o comportamento do systemd-cryptsetup padrão é alterado adicionando luks-auto.target à sua dependência.
luks-auto-key.service e luks-auto-key.sh
Esta unidade executa o script luks-auto-key.sh, que, com base nas chaves rd.luks.*, encontra mídia com as chaves e as copia para um diretório temporário para uso posterior. Após a conclusão do processo, as chaves são excluídas do diretório temporário por luks-auto-clean.service.
Fontes:
/usr/lib/dracut/modules.d/99luks-auto/module-setup.sh
#!/bin/bash
check () {
if ! dracut_module_included "systemd"; then
"luks-auto needs systemd in the initramfs"
return 1
fi
return 255
}
depends () {
echo "systemd"
return 0
}
install () {
inst "$systemdutildir/systemd-cryptsetup"
inst_script "$moddir/luks-auto-generator.sh" "$systemdutildir/system-generators/luks-auto-generator.sh"
inst_script "$moddir/luks-auto-key.sh" "/etc/systemd/system/luks-auto-key.sh"
inst_script "$moddir/luks-auto.sh" "/etc/systemd/system/luks-auto.sh"
inst "$moddir/luks-auto.target" "${systemdsystemunitdir}/luks-auto.target"
inst "$moddir/luks-auto-key.service" "${systemdsystemunitdir}/luks-auto-key.service"
inst "$moddir/luks-auto-clean.service" "${systemdsystemunitdir}/luks-auto-clean.service"
ln_r "${systemdsystemunitdir}/luks-auto.target" "${systemdsystemunitdir}/initrd.target.wants/luks-auto.target"
ln_r "${systemdsystemunitdir}/luks-auto-key.service" "${systemdsystemunitdir}/initrd.target.wants/luks-auto-key.service"
ln_r "${systemdsystemunitdir}/luks-auto-clean.service" "${systemdsystemunitdir}/initrd.target.wants/luks-auto-clean.service"
}
/usr/lib/dracut/modules.d/99luks-auto/luks-auto-generator.sh
#!/bin/sh
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
. /lib/dracut-lib.sh
SYSTEMD_RUN='/run/systemd/system'
CRYPTSETUP='/usr/lib/systemd/systemd-cryptsetup'
TOUT=$(getargs rd.luks.key.tout)
if [ ! -z "$TOUT" ]; then
mkdir -p "${SYSTEMD_RUN}/luks-auto-key.service.d"
cat > "${SYSTEMD_RUN}/luks-auto-key.service.d/drop-in.conf" <<EOF
[Service]
Type=oneshot
ExecStartPre=/usr/bin/sleep $TOUT
EOF
fi
mkdir -p "$SYSTEMD_RUN/luks-auto.target.wants"
for argv in $(getargs rd.luks.uuid -d rd_LUKS_UUID); do
_UUID=${argv#luks-}
_UUID_ESC=$(systemd-escape -p $_UUID)
mkdir -p "${SYSTEMD_RUN}/systemd-cryptsetup@luksx2d${_UUID_ESC}.service.d"
cat > "${SYSTEMD_RUN}/systemd-cryptsetup@luksx2d${_UUID_ESC}.service.d/drop-in.conf" <<EOF
[Unit]
After=luks-auto.target
ConditionPathExists=!/dev/mapper/luks-${_UUID}
EOF
cat > "${SYSTEMD_RUN}/luks-auto@${_UUID_ESC}.service" <<EOF
[Unit]
Description=luks-auto Cryptography Setup for %I
DefaultDependencies=no
Conflicts=umount.target
IgnoreOnIsolate=true
Before=luks-auto.target
BindsTo=dev-disk-byx2duuid-${_UUID_ESC}.device
After=dev-disk-byx2duuid-${_UUID_ESC}.device luks-auto-key.service
Before=umount.target
[Service]
Type=oneshot
RemainAfterExit=yes
TimeoutSec=0
ExecStart=/etc/systemd/system/luks-auto.sh ${_UUID}
ExecStop=$CRYPTSETUP detach 'luks-${_UUID}'
Environment=DRACUT_SYSTEMD=1
StandardInput=null
StandardOutput=syslog
StandardError=syslog+console
EOF
ln -fs ${SYSTEMD_RUN}/luks-auto@${_UUID_ESC}.service $SYSTEMD_RUN/luks-auto.target.wants/luks-auto@${_UUID_ESC}.service
done
/usr/lib/dracut/modules.d/99luks-auto/luks-auto-key.service
[Unit]
Description=LUKS AUTO key searcher
After=cryptsetup-pre.target
Before=luks-auto.target
DefaultDependencies=no
[Service]
Environment=DRACUT_SYSTEMD=1
Type=oneshot
ExecStartPre=/usr/bin/sleep 1
ExecStart=/etc/systemd/system/luks-auto-key.sh
RemainAfterExit=true
StandardInput=null
StandardOutput=syslog
StandardError=syslog+console
/usr/lib/dracut/modules.d/99luks-auto/luks-auto-key.sh
#!/bin/sh
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
export DRACUT_SYSTEMD=1
. /lib/dracut-lib.sh
MNT_B="/tmp/luks-auto"
ARG=$(getargs rd.luks.key)
IFS=$':' _t=(${ARG})
KEY=${_t[0]}
F_FIELD=''
F_VALUE=''
if [ ! -z $KEY ] && [ ! -z ${_t[1]} ];then
IFS=$'=' _t=(${_t[1]})
F_FIELD=${_t[0]}
F_VALUE=${_t[1]}
F_VALUE="${F_VALUE%"}"
F_VALUE="${F_VALUE#"}"
fi
mkdir -p $MNT_B
finding_luks_keys(){
local _DEVNAME=''
local _UUID=''
local _TYPE=''
local _LABEL=''
local _MNT=''
local _KEY="$1"
local _F_FIELD="$2"
local _F_VALUE="$3"
local _RET=0
blkid -s TYPE -s UUID -s LABEL -u filesystem | grep -v -E -e "TYPE=".*_member"" -e "TYPE="crypto_.*"" -e "TYPE="swap"" | while IFS=$'' read -r _line; do
IFS=$':' _t=($_line);
_DEVNAME=${_t[0]}
_UUID=''
_TYPE=''
_LABEL=''
_MNT=''
IFS=$' ' _t=(${_t[1]});
for _a in "${_t[@]}"; do
IFS=$'=' _v=(${_a});
temp="${_v[1]%"}"
temp="${temp#"}"
case ${_v[0]} in
'UUID')
_UUID=$temp
;;
'TYPE')
_TYPE=$temp
;;
'LABEL')
_LABEL=$temp
;;
esac
done
if [ ! -z "$_F_FIELD" ];then
case $_F_FIELD in
'UUID')
[ ! -z "$_F_VALUE" ] && [ "$_UUID" != "$_F_VALUE" ] && continue
;;
'LABEL')
[ ! -z "$_F_VALUE" ] && [ "$_LABEL" != "$_F_VALUE" ] && continue
;;
*)
[ "$_DEVNAME" != "$_F_FIELD" ] && continue
;;
esac
fi
_MNT=$(findmnt -n -o TARGET $_DEVNAME)
if [ -z "$_MNT" ]; then
_MNT=${MNT_B}/KEY-${_UUID}
mkdir -p "$_MNT" && mount -o ro "$_DEVNAME" "$_MNT"
_RET=$?
else
_RET=0
fi
if [ "${_RET}" -eq 0 ] && [ -f "${_MNT}/${_KEY}" ]; then
cp "${_MNT}/${_KEY}" "$MNT_B/${_UUID}.key"
info "Found ${_MNT}/${_KEY} on ${_UUID}"
fi
if [[ "${_MNT}" =~ "${MNT_B}" ]]; then
umount "$_MNT" && rm -rfd --one-file-system "$_MNT"
fi
done
return 0
}
finding_luks_keys $KEY $F_FIELD $F_VALUE
/usr/lib/dracut/modules.d/99luks-auto/luks-auto.target
[Unit]
Description=LUKS AUTO target
After=systemd-readahead-collect.service systemd-readahead-replay.service
After=cryptsetup-pre.target luks-auto-key.service
Before=cryptsetup.target
/usr/lib/dracut/modules.d/99luks-auto/luks-auto.sh
#!/bin/sh
# -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*-
# ex: ts=8 sw=4 sts=4 et filetype=sh
export DRACUT_SYSTEMD=1
. /lib/dracut-lib.sh
MNT_B="/tmp/luks-auto"
CRYPTSETUP='/usr/lib/systemd/systemd-cryptsetup'
for i in $(ls -p $MNT_B | grep -v /);do
info "Trying $i on $1..."
$CRYPTSETUP attach "luks-$1" "/dev/disk/by-uuid/$1" $MNT_B/$i 'tries=1'
if [ "$?" -eq "0" ]; then
info "Found $i for $1"
exit 0
fi
done
warn "No key found for $1. Fallback to passphrase mode."
/usr/lib/dracut/modules.d/99luks-auto/luks-auto-clean.service
[Unit]
Description=LUKS AUTO key cleaner
After=cryptsetup.target
DefaultDependencies=no
[Service]
Type=oneshot
ExecStart=/usr/bin/rm -rfd --one-file-system /tmp/luks-auto
/etc/dracut.conf.d/luks-auto.conf
add_dracutmodules+=" luks-auto "
Instalação
mkdir -p /usr/lib/dracut/modules.d/99luks-auto/
# размещаем тут почти все файлы
chmod +x /usr/lib/dracut/modules.d/99luks-auto/*.sh
# создаем файл /etc/dracut.conf.d/luks-auto.conf
# И генерируем новый initramfs
dracut -f
Conclusão
Por conveniência, mantive a compatibilidade com as opções de linha de comando do kernel como no modo sysvinit, o que facilita o uso em instalações mais antigas.
Fonte: habr.com