Príbeh sa začal dávno, ešte pri vydaní Centos 7 (RHEL 7). Ak ste používali šifrovanie na diskoch s Centos 6, neboli žiadne problémy s automatickým odblokovaním diskov pri pripojení USB flash disku s potrebnými kľúčmi. Avšak, pri vydaní 7-ky zrazu všetko nefungovalo tak, ako ste boli zvyknutí. Vtedy sa dalo nájsť riešenie v návrate dracut k sysvinit pomocou jednoduchého riadku v konfigurácii: echo 'omit_dracutmodules+=" systemd "' > /etc/dracut.conf.d/luks-workaround.conf
Čo nás okamžite obralo o všetku krásu systemd — rýchle a paralelné spúšťanie systémových služieb, čo výrazne skracovalo čas spustenia systému.
A situácia sa nezmenila:
Nečakajúc na riešenie, urobil som ho pre seba sám, a teraz sa s ním delím aj s verejnosťou, kto má záujem, čítajte ďalej.

Úvod
Systemd, keď som začal pracovať s Centos 7, vo mne nepodnieklo žiadne emócie, pretože okrem nevýznamnej zmeny v syntaxi správy služieb som na začiatku necítil žiadny zásadný rozdiel. Neskôr sa mi systemd páčil, ale prvý dojem bol trochu narušený, pretože vývojári dracut nevenovali veľa času podpore procesu spúšťania pomocou systemd spolu so šifrovaním diskov. Celkovo to fungovalo, ale zadávanie hesla od disku pri každom spustení servera — nie je práve zaujímavé zamestnanie.
Vyskúšajúc množstvo odporúčaní a študujúc manuál som pochopil, že v režime systemd je možné nastavenie s USB, ale iba pri ručnej asociácii každého disku s kľúčom na USB disku, pričom samotný USB disk sa dá spojiť iba podľa jeho UUID, LABEL nefungoval. Udržiavať si takú konfiguráciu nebolo veľmi pohodlné, preto som sa nakoniec ponoril do očakávania a po takmer 7 rokoch som pochopil — nikto sa nechystá tento problém vyriešiť.
Problémy
Samozrejme, napísať svoj plugin k dracut môže takmer každý, ale spraviť ho funkčným už nie je také jednoduché. Ukázalo sa, že vďaka paralelnej prirodzenosti spúšťania systemd je ťažké zapojiť svoj kód a zmeniť priebeh spúšťania. Dokumentácia k dracut nevysvetľovala všetko. Avšak v dôsledku dlhých experimentov som dokázal problém vyriešiť.
Ako to funguje
Na základe sú tri jednotky:
- luks-auto-key.service — hľadá úložiská s kľúčmi pre LUKS
- luks-auto.target — plní úlohu závislosti pre zabudované jednotky systemd-cryptsetup
- luks-auto-clean.service — čistí dočasné súbory vytvorené lux-auto-key.service
luks-auto-generator.sh je skript, ktorý sa spúšťa cez systemd a generuje jednotky na základe parametrov jadra. Podobné generátory vytvárajú jednotky fstab a podobne.
luks-auto-generator.sh
Pomocou drop-in.conf sa mení správanie štandardných systemd-cryptsetup, pričom sa im pridáva závislosť na luks-auto.target.
luks-auto-key.service a luks-auto-key.sh
Táto jednotka spúšťa skript luks-auto-key.sh, ktorý na základe kľúčov rd.luks.* nachádza médium s kľúčmi a kopíruje ich do dočasného adresára na ďalšie použitie. Po ukončení procesu sú kľúče z dočasného adresára odstránené pomocou luks-auto-clean.service.
Zdrojové kódy:
/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 vyhľadávač kľúčov
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 cieľ
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 čistič kľúčov
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 "Inštalácia
mkdir -p \/usr\/lib\/dracut\/modules.d\/99luks-auto\/\n# uložte sem takmer všetky súbory\nchmod +x \/usr\/lib\/dracut\/modules.d\/99luks-auto\/*.sh\n# vytvorte súbor \/etc\/dracut.conf.d\/luks-auto.conf\n# a generujeme nový initramfs\ndracut -f
Záver
Pre pohodlie som zachoval kompatibilitu s parametrami príkazového riadku jadra pre režim sysvinit, čo uľahčuje použitie v starších inštaláciách.
Zdroj: habr.com
