dracut + systemd + LUKS + usbflash = automatische Entsperrung

Die Geschichte begann vor langer Zeit mit der Veröffentlichung von Centos 7 (RHEL 7). Wenn Sie Verschlüsselung auf Festplatten mit Centos 6 verwendet haben, gab es keine Probleme beim automatischen Entsperren von Festplatten, wenn Sie ein USB-Flash-Laufwerk mit den erforderlichen Schlüsseln anschließen. Mit der Veröffentlichung von 7-ki funktionierte jedoch plötzlich nicht mehr alles wie früher. Dann war es möglich, eine Lösung für die Rückgabe von dracut an sysvinit mithilfe einer unkomplizierten Zeile in der Konfiguration zu finden: echo 'omit_dracutmodules + = "systemd"'> /etc/dracut.conf.d/luks-workaround.conf

Was uns sofort den ganzen Charme von systemd beraubte - schnell und Paralleler Start von Systemdiensten, wodurch die Systemstartzeit erheblich verkürzt wurde. Die Dinge sind immer noch

da: 905683

Ohne auf eine Lösung zu warten, habe ich es für mich selbst gemacht und jetzt teile ich es mit der interessierten Öffentlichkeit, lesen Sie weiter.



Einführung


Systemd, als ich anfing, mit Centos 7 zu arbeiten, verursachte es keine Emotionen, da ich neben einer geringfügigen Änderung der Syntax des Service-Managements zunächst keinen großen Unterschied spürte. In der Folge mochte ich systemd, aber der erste Eindruck war etwas verwöhnt, da dracut-Entwickler nicht besonders viel Zeit darauf verwendet haben, den Startvorgang mit systemd zusammen mit der Festplattenverschlüsselung zu unterstützen. Im Allgemeinen hat es funktioniert, aber die Eingabe des Kennworts von der Festplatte bei jedem Start des Servers ist nicht die interessanteste Aktivität.

Nachdem ich eine Reihe von Empfehlungen getestet und das Handbuch berücksichtigt hatte, stellte ich fest, dass im systemd-Modus eine Konfiguration mit USB möglich ist, jedoch nur mit der manuellen Zuordnung jedes Laufwerks zu einem Schlüssel auf einem USB-Laufwerk, und das USB-Laufwerk selbst kann nur über seine UUID verbunden werden. LABEL funktionierte nicht. Dies zu Hause aufrechtzuerhalten war nicht sehr praktisch, so dass ich am Ende in Erwartung geriet und nach fast 7 Jahren Wartezeit realisierte, dass niemand das Problem lösen würde.

Probleme


Natürlich kann fast jeder sein eigenes Plug-In für dracut schreiben, aber es ist nicht so einfach, es zum Laufen zu bringen. Es stellte sich heraus, dass es aufgrund der Parallelität der Ausführung von systemd nicht so einfach ist, Ihren Code aufzunehmen und den Download-Fortschritt zu ändern. Die Dokumentation für dracut hat nicht alles erklärt. Aufgrund langwieriger Experimente konnte ich das Problem jedoch lösen.

Wie funktioniert es


Es basiert auf drei Einheiten:

  1. luks-auto-key.service - sucht nach Schlüssellaufwerken für LUKS
  2. luks-auto.target - fungiert als Abhängigkeit für die integrierten Einheiten von systemd-cryptsetup
  3. luks-auto-clean.service - Bereinigt temporäre Dateien, die von luks-auto-key.service erstellt wurden

Und luks-auto-generator.sh ist ein Skript, das von systemd ausgeführt wird und Einheiten basierend auf Kernelparametern generiert. Ähnliche Generatoren erstellen fstab-Einheiten usw.

luks-auto-generator.sh


Mit drop-in.conf wird das Verhalten von Standard-systemd-cryptsetups geändert, indem ihnen luks-auto.target hinzugefügt wird.

luks-auto-key.service und luks-auto-key.sh


Dieses Gerät führt das Skript luks-auto-key.sh aus, das basierend auf den rd.luks. * Keys Medien mit Schlüsseln findet und diese zur zukünftigen Verwendung in ein temporäres Verzeichnis kopiert. Nach Abschluss des Vorgangs löscht luks-auto-clean.service die Schlüssel aus dem temporären Verzeichnis.

Quellen:


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


Installation

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


Fazit


Der Einfachheit halber habe ich die Kompatibilität mit den Kernel-Befehlszeilenparametern wie im Sysvinit-Modus beibehalten, was die Verwendung in älteren Installationen erleichtert.

All Articles