dracut + systemd + LUKS + usbflash = automaatne avamine

Lugu algas ammu, kui Centos 7 (RHEL 7) välja tuli. Kui kasutasite diskide krüpteerimist Centos 6-s, siis ei olnud USB mälupulga vajalike võtmete ühendamisel automaatse avamisega mingeid probleeme. Kuid kui 7. versioon välja tuli, ei töötanud kõik nagu tavaliselt. Sel ajal sai lahenduse leidmiseks dracuti sysvinit-ile tagasi viia, kasutades lihtsat rida konfiguratsioonis: echo 'omit_dracutmodules+=" systemd "' > /etc/dracut.conf.d/luks-workaround.conf
See eemaldas meilt kiiresti ja paralleelselt süsteemiteenuseid käivitamise eelised, mis lühendasid oluliselt süsteemi käivitamise aega.
Asi on endiselt seal: 905683
Kuna ma ei oodanud lahendust, tegin selle enda jaoks ise ja nüüd jagan seda ka avalikkusega, kellele see huvi pakub, lugege edasi.
dracut + systemd + LUKS + usbflash = automaatne avamine

Sissejuhatus

Systemd, kui ma alles alustasin Centos 7 kasutamist, ei tekitanud see mingit emotsiooni, kuna peale väikese süntaktilise muudatuse teenuste haldamises ei tundnud ma alguses erilist erinevust. Hiljem hakkas mulle systemd meeldima, kuid esialgne mulje oli veidi rikkis, kuna dracuti arendajad ei pööranud erilist tähelepanu laadimisprotsessi toetamisele systemd abil koos ketaste krüpteerimisega. Üldiselt töötas see, kuid igal käivitamisel ketta parooli sisestamine ei olnud just meeldiv. serverile — ei ole just kõige huvitavam tegevus.
Olen proovinud hulga soovitusi ja lugenud käsiraamatut ning sain aru, et systemd režiimis on võimalik USB-seadmest konfiguratsioon, kuid ainult siis, kui iga ketas seostatakse käsitsi USB-seadmest võtmega, samas kui USB-seadet saab siduda ainult selle UUID järgi, LABEL ei töötanud. Sellise lahenduse hoidmine enda juures ei olnud just mugav, seega lõpuks ootasin ja, pärast peaaegu 7 aastat ootamist, sain aru, et keegi ei plaani probleemi lahendada.

Probleemid

Muidugi saab dracut'ile oma pistik programmi kirjutada peaaegu igaüks, kuid selle tööle panemine ei ole enam nii lihtne. Selgus, et tänu systemd'i paralleelsele käivitamise loomusele on keeruline oma koodi sisestamine ja käivitamisprotsessi muutmine. Dracut'i dokumentatsioon ei selgitanud kaugeltki kõike. Kuid pärast pikka katsetamist suutsin ma probleemi lahendada.

Kuidas see töötab

Põhjas on kolm ühikut:

  1. luks-auto-key.service — otsib LUKS-i võtmega salvestusseadmeid
  2. luks-auto.target — täidab systemd-cryptsetup'i sisseehitatud üksuste sõltuvuse rolli
  3. luks-auto-clean.service — puhastab luks-auto-key.service'i poolt loodud ajutised failid

Ja luks-auto-generator.sh — skript, mille käivitab systemd ja mis genereerib üksuseid tuumaparameetrite põhjal. Sarnased generaatorid loovad fstab'i jne üksusi.

luks-auto-generator.sh

Drop-in.conf'i abil muudetakse standardsete systemd-cryptsetup'i käitumist, lisades nende sõltuvusse luks-auto.target'i.

luks-auto-key.service ja luks-auto-key.sh

See üksus käivitab skripti luks-auto-key.sh, mis, tuginedes võtmetele rd.luks.*, leiab võtmetega kandjad ja kopeerib need ajutisse katalooge edasiseks kasutamiseks. Pärast protsessi lõppu kustutab luks-auto-clean.service ajutisest kataloogist võtmed.

Allikad:

/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 võtmeotsija
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 siht
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 võtme puhastaja
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 "

Installeerimine


mkdir -p /usr/lib/dracut/modules.d/99luks-auto/
# paigaldame siia peaaegu kõik failid
chmod +x /usr/lib/dracut/modules.d/99luks-auto/*.sh
# loome faili /etc/dracut.conf.d/luks-auto.conf
# ning genereerime uue initramfs
dracut -f

Kokkuvõte

Mugavuse huvides hoidsin ühilduvuse command line argumentide juures nii sysvinit režiimiga, mistõttu on see lihtsam vanades installatsioonides kasutamiseks.

Allikas: habr.com

Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid 🔥 Osta usaldusväärne veebihosting DDoS kaitsega, VPS VDS serverid | ProHoster