Het verhaal begon lang geleden, nog bij de uitgave van CentOS 7 (RHEL 7). Als je schijfversleuteling op CentOS 6 gebruikte, waren er geen problemen met het automatisch ontgrendelen van schijven bij aansluiting van een USB-stick met de benodigde sleutels. Echter, bij de lancering van 7 werkte alles plotseling niet zoals je gewend was. Destijds kon je een oplossing vinden door dracut terug te brengen naar sysvinit met behulp van een eenvoudige regel in de configuratie: echo 'omit_dracutmodules+=" systemd "' > /etc/dracut.conf.d/luks-workaround.conf
Wat ons onmiddellijk beroofde van de charme van systemd — de snelle en parallelle opstart van systeemdiensten, wat de opstarttijd van het systeem aanzienlijk verkortte.
En nog steeds niet opgelost:
In afwachting van een oplossing heb ik het zelf opgelost, en nu deel ik het met het publiek, als je geïnteresseerd bent, lees dan verder.

Inleiding
Systemd wekte in het begin, toen ik net begon te werken met CentOS 7, geen bijzondere emoties, omdat ik, afgezien van een gering verschil in de syntaxis voor het beheren van diensten, in het begin geen bijzondere verschillen merkte. Uiteindelijk begon ik systemd te waarderen, maar de eerste indruk was enigszins verstoord, omdat de ontwikkelaars van dracut niet veel tijd investeerden in het ondersteunen van het opstartproces met behulp van systemd in combinatie met schijfversleuteling. Over het algemeen werkte het, maar het invoeren van het wachtwoord voor de schijf bij elke opstart was de server — niet bepaald een interessante bezigheid.
Na talloze aanbevelingen te hebben uitgeprobeerd en de handleiding te hebben bestudeerd, begreep ik dat in de systemd-modus configuratie met USB mogelijk was, maar alleen bij handmatige associatie van elke schijf met de sleutel op de USB-stick, waarbij de USB-stick alleen kon worden gekoppeld via zijn UUID; LABEL werkte niet. Het onderhouden van zoiets was niet erg handig, dus uiteindelijk dook ik in de afwachting en, na bijna 7 jaar gewacht te hebben, realiseerde ik me — niemand heeft de intentie om het probleem op te lossen.
Problemen
Natuurlijk kan bijna iedereen zijn eigen plugin voor dracut schrijven, maar het functioneel maken is al een stuk moeilijker. Het bleek dat door de parallelle natuur van het opstarten met systemd het inschakelen van je eigen code en het wijzigen van het opstartproces niet zo eenvoudig was. De documentatie voor dracut verklaarde lang niet alles. Echter, na langdurige experimenten slaagde ik erin om het probleem op te lossen.
Hoe het werkt
De basis bestaat uit drie eenheden:
- luks-auto-key.service — zoekt opslagmedia met sleutels voor LUKS
- luks-auto.target — fungeert als afhankelijkheid voor ingebouwde eenheden van systemd-cryptsetup
- luks-auto-clean.service — verwijdert tijdelijke bestanden die zijn aangemaakt door luks-auto-key.service
luks-auto-generator.sh is een script dat wordt uitgevoerd door systemd en zorgt voor de generatie van eenheden op basis van de kernelparameters. Vergelijkbare generatoren creëren eenheden zoals fstab, enzovoorts.
luks-auto-generator.sh
Met drop-in.conf wordt het gedrag van de standaard systemd-cryptsetup gewijzigd door hengsluks-auto.target als afhankelijkheid toe te voegen.
luks-auto-key.service en luks-auto-key.sh
Deze eenheid start het script luks-auto-key.sh, dat, gebaseerd op de sleutels rd.luks.*, de stations met sleutels vindt en deze naar een tijdelijke map kopieert voor verder gebruik. Na het einde van het proces verwijdert luks-auto-clean.service de sleutels uit de tijdelijke map.
Bronnen:
/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 sleutelzoeker
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 doel
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 sleutelreiniger
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 "Installatie
mkdir -p \/usr\/lib\/dracut\/modules.d\/99luks-auto\/
# hier bijna alle bestanden plaatsen
chmod +x \/usr\/lib\/dracut\/modules.d\/99luks-auto\/*.sh
# maak het bestand \/etc\/dracut.conf.d\/luks-auto.conf
# en genereer een nieuwe initramfs
dracut -f
Conclusie
Voor het gemak heb ik de compatibiliteit met de opdrachtregelparameters van de kernel behouden zoals bij de sysvinit-modus, wat het gebruik in oudere installaties vergemakkelijkt.
Bron: habr.com
