dracut + systemd + LUKS + usbflash = sblocco automatico

La storia è iniziata molto tempo fa, quando è stato rilasciato Centos 7 (RHEL 7). Se hai utilizzato la crittografia sulle unità con Centos 6, non si sono verificati problemi con lo sblocco automatico delle unità quando hai collegato un'unità flash USB con le chiavi necessarie. Tuttavia, quando è stato rilasciato 7, improvvisamente tutto non ha funzionato come eri abituato. Quindi è stato possibile trovare una soluzione restituendo dracut a sysvinit utilizzando una semplice riga nella configurazione: echo 'omit_dracutmodules+=" systemd "' > /etc/dracut.conf.d/luks-workaround.conf
Ciò ci ha immediatamente privato di tutta la bellezza di systemd: avvio rapido e parallelo dei servizi di sistema, che ha ridotto significativamente i tempi di avvio del sistema.
Le cose sono ancora lì: 905683
Senza aspettare una soluzione, l’ho realizzato da solo, e ora lo condivido con il pubblico, che è interessato, continua a leggere.
dracut + systemd + LUKS + usbflash = sblocco automatico

Introduzione

Systemd, quando ho iniziato a lavorare con Centos 7, non ha suscitato alcuna emozione, poiché a parte un piccolo cambiamento nella sintassi di gestione del servizio, all'inizio non ho sentito molta differenza. Successivamente mi è piaciuto systemd, ma la prima impressione è stata un po' rovinata, dal momento che gli sviluppatori di dracut non hanno dedicato molto tempo al supporto del processo di avvio utilizzando systemd insieme alla crittografia del disco. In generale ha funzionato, ma inserire la password del disco ogni volta che si avvia il server non è la cosa più interessante.
Dopo aver provato una serie di consigli e studiato il manuale, mi sono reso conto che in modalità systemd la configurazione con USB è possibile, ma solo associando manualmente ciascun disco con una chiave su un disco USB, e il disco USB stesso può essere associato solo tramite il suo UUID, LABEL non ha funzionato. Non era molto conveniente mantenerlo a casa, quindi alla fine mi sono buttato nell'attesa e, dopo aver aspettato quasi 7 anni, ho capito che nessuno avrebbe risolto il problema.

Problematica

Naturalmente, quasi chiunque può scrivere il proprio plugin per dracut, ma farlo funzionare non è più così semplice. Si è scoperto che, a causa della natura parallela dell'avvio di systemd, non è così semplice includere il codice e modificare l'avanzamento del caricamento. La documentazione di dracut non spiegava tutto. Tuttavia, dopo lunghi esperimenti, sono riuscito a risolvere il problema.

Come funziona

Si basa su tre unità:

  1. luks-auto-key.service - cerca unità con chiavi per LUKS
  2. luks-auto.target - funge da dipendenza per le unità systemd-cryptsetup integrate
  3. luks-auto-clean.service: pulisce i file temporanei creati da luks-auto-key.service

E luks-auto-generator.sh è uno script che viene lanciato da systemd e genera unità in base ai parametri del kernel. Generatori simili sono creati da unità fstab, ecc.

luks-auto-generator.sh

Utilizzando drop-in.conf, il comportamento di systemd-cryptsetup standard viene modificato aggiungendo luks-auto.target alla loro dipendenza.

luks-auto-key.service e luks-auto-key.sh

Questa unità esegue lo script luks-auto-key.sh che, in base alle chiavi rd.luks.*, trova i supporti con le chiavi e li copia in una directory temporanea per un ulteriore utilizzo. Una volta completato il processo, le chiavi vengono eliminate dalla directory temporanea da luks-auto-clean.service.

Fonti:

/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 "

Installazione


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

conclusione

Per comodità, ho mantenuto la compatibilità con le opzioni della riga di comando del kernel come per la modalità sysvinit, che ne facilita l'utilizzo nelle installazioni meno recenti.

Fonte: habr.com

Aggiungi un commento