La storia è iniziata tanto tempo fa, ancora con il rilascio di Centos 7 (RHEL 7). Se hai utilizzato la crittografia sui dischi con Centos 6, non c'erano problemi con lo sblocco automatico dei dischi collegando una chiavetta USB con le chiavi necessarie. Tuttavia, con il rilascio della versione 7, all'improvviso tutto non funzionava come eri abituato. Allora era possibile trovare una soluzione tornando a dracut utilizzando sysvinit con una semplice riga di codice nel file di configurazione: echo ‘omit_dracutmodules+=" systemd "’ > /etc/dracut.conf.d/luks-workaround.conf
Ciò ci privava immediatamente di tutto il fascino di systemd: l'avvio rapido e parallelo dei servizi di sistema, che riduceva notevolmente il tempo di avvio del sistema.
E così siamo rimasti:
Non aspettando una soluzione, l'ho creata da solo e ora la condivido con il pubblico, chi è interessato legga oltre.

Introduzione
Systemd, quando ho iniziato a lavorare con Centos 7, non suscitò alcuna emozione, poiché a parte una lieve modifica della sintassi per la gestione dei servizi, non sentii inizialmente differenze significative. In seguito mi piacque systemd, ma la prima impressione fu un po' offuscata, poiché gli sviluppatori di dracut non dedicavano molto tempo al supporto del processo di avvio con systemd insieme alla crittografia dei dischi. In generale funzionava, ma dover inserire la password del disco ad ogni avvio server non è esattamente un'attività interessante.
Dopo aver provato una miriade di raccomandazioni e studiato il manuale, ho capito che in modalità systemd era possibile configurare l'USB, ma solo associando manualmente ogni disco alla chiave nella chiavetta USB, e la chiavetta USB poteva essere collegata solo tramite il suo UUID, l'LABEL non funzionava. Mantenere una configurazione del genere non era molto comodo, quindi alla fine ho vissuto in attesa e, dopo quasi 7 anni, ho capito che nessuno intendeva risolvere il problema.
Problemi
Certo, scrivere il proprio plugin per dracut può farlo quasi chiunque, ma renderlo funzionante non è affatto semplice. Si è rivelato che a causa della natura parallela dell'avvio di systemd, integrare il proprio codice e modificare il processo di avvio non è affatto facile. La documentazione di dracut non chiariva tutto. Tuttavia, a seguito di lunghi esperimenti, sono riuscito a risolvere il problema.
Come funziona
Al centro ci sono tre unità:
- luks-auto-key.service — ricerca dispositivi con chiavi per LUKS
- luks-auto.target — funge da dipendenza per le unità integrate systemd-cryptsetup
- luks-auto-clean.service — pulisce i file temporanei creati da luks-auto-key.service
Il luks-auto-generator.sh è uno script che viene eseguito da systemd e genera unità basate sui parametri del kernel. Generatori analoghi creano unità fstab e simili.
luks-auto-generator.sh
Con drop-in.conf si modifica il comportamento degli standard systemd-cryptsetup, aggiungendo come dipendenza luks-auto.target.
luks-auto-key.service e luks-auto-key.sh
Questa unità esegue lo script luks-auto-key.sh, che, basandosi sulle chiavi rd.luks.*, trova i supporti con le chiavi e li copia in una directory temporanea per un utilizzo successivo. Al termine del processo, le chiavi vengono rimosse dalla directory temporanea da luks-auto-clean.service.
Sorgenti:
/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=Ricercatore di chiavi LUKS AUTO
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=Obiettivo LUKS AUTO
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=Pulisci chiavi LUKS AUTO
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 "Installazione
mkdir -p \/usr\/lib\/dracut\/modules.d\/99luks-auto\/\n# posizioniamo qui quasi tutti i file
chmod +x \/usr\/lib\/dracut\/modules.d\/99luks-auto\/*.sh\n# creiamo il file \/etc\/dracut.conf.d\/luks-auto.conf\n# E generiamo un nuovo initramfs
dracut -f
Conclusione
Per comodità ho mantenuto la compatibilità con i parametri della riga di comando del kernel per la modalità sysvinit, facilitando l'uso nelle installazioni più vecchie.
Fonte: habr.com
