dracut + systemd + LUKS + usbflash = desbloqueo automático

La historia comenzó hace mucho tiempo, con el lanzamiento de Centos 7 (RHEL 7). Si utilizó cifrado en discos con Centos 6, no hubo problemas con el desbloqueo automático de discos al conectar una unidad flash USB con las claves necesarias. Sin embargo, con el lanzamiento de 7-ki de repente todo no funcionó como solía hacerlo. Entonces fue posible encontrar una solución al devolver dracut a sysvinit utilizando una línea sin complicaciones en la configuración: echo 'omit_dracutmodules + = "systemd"'> /etc/dracut.conf.d/luks-workaround.conf

Lo que nos privó de inmediato de todo el encanto de systemd - rápido y lanzamiento paralelo de servicios del sistema, lo que redujo significativamente el tiempo de inicio del sistema. Las cosas siguen

ahí: 905683

Sin esperar una solución, lo hice por mí mismo, y ahora lo comparto con el público, que está interesado, sigue leyendo.



Introducción


Systemd, cuando comencé a trabajar con Centos 7, no causó ninguna emoción, ya que además de un pequeño cambio en la sintaxis de la administración del servicio, al principio no sentí mucha diferencia. Posteriormente, me gustó systemd, pero la primera impresión fue un poco estropeada, ya que los desarrolladores de dracut no dedicaron particularmente tiempo a apoyar el proceso de arranque usando systemd junto con el cifrado de disco. En general, funcionó, pero ingresar la contraseña del disco en cada inicio del servidor no es la actividad más interesante.

Después de probar un montón de recomendaciones y tomar en cuenta el manual, me di cuenta de que en el modo systemd es posible una configuración con USB, pero solo con la asociación manual de cada unidad con una llave en una unidad USB, y la unidad USB solo puede conectarse por su UUID, LABEL no funcionó. Mantener esto en casa no era muy conveniente, así que al final caí en la expectativa y, después de esperar durante casi 7 años, me di cuenta de que nadie iba a resolver el problema.

Problemas


Por supuesto, casi todos pueden escribir su propio complemento para dracut, pero hacerlo funcionar no es tan simple. Resultó que debido a la naturaleza paralela de ejecutar systemd, no es tan fácil incluir su código y cambiar el progreso de la descarga. La documentación para dracut no explicaba todo. Sin embargo, debido a los largos experimentos, pude resolver el problema.

Como funciona


Se basa en tres unidades:

  1. luks-auto-key.service - busca unidades clave para LUKS
  2. luks-auto.target: actúa como una dependencia para las unidades integradas de systemd-cryptsetup
  3. luks-auto-clean.service - limpia archivos temporales creados por luks-auto-key.service

Y luks-auto-generator.sh es un script que es ejecutado por systemd y genera unidades basadas en los parámetros del kernel. Generadores similares crean unidades fstab, etc.

luks-auto-generator.sh


Usando drop-in.conf, el comportamiento de systemd-cryptsetups estándar se cambia al agregarles luks-auto.target.

luks-auto-key.service y luks-auto-key.sh


Esta unidad ejecuta el script luks-auto-key.sh, que, basado en rd.luks. * Keys, busca medios con claves y los copia en un directorio temporal para su uso futuro. Una vez que se completa el proceso, luks-auto-clean.service elimina las claves del directorio temporal.

Fuentes:


/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@luks\x2d${_UUID_ESC}.service.d"
	cat > "${SYSTEMD_RUN}/systemd-cryptsetup@luks\x2d${_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-by\x2duuid-${_UUID_ESC}.device
After=dev-disk-by\x2duuid-${_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 "


Instalación

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


Conclusión


Por conveniencia, conservé la compatibilidad con los parámetros de la línea de comandos del kernel en cuanto al modo sysvinit, que facilita su uso en instalaciones más antiguas.

All Articles