dracut + systemd + LUKS + usbflash = autodetectie

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: 905683
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.
dracut + systemd + LUKS + usbflash = autodetectie

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:

  1. luks-auto-key.service — zoekt opslagmedia met sleutels voor LUKS
  2. luks-auto.target — fungeert als afhankelijkheid voor ingebouwde eenheden van systemd-cryptsetup
  3. 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

Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers 🔥 Koop betrouwbare webhosting met bescherming tegen DDoS, VPS VDS servers | ProHoster