dracut + systemd + LUKS + usbflash = automatisk oppgradering

Historien begynte for lenge siden, da CentOS 7 (RHEL 7) ble lansert. Hvis du brukte disk-kryptering pĂ„ CentOS 6, var det ingen problemer med automatisk opplĂ„sning av disker nĂ„r du koblet til en USB-minnepinne med de nĂždvendige nĂžklene. Men ved lanseringen av 7, sluttet plutselig alt Ă„ fungere som du var vant til. Da kunne du finne en lĂžsning ved Ă„ gĂ„ tilbake til dracut med sysvinit ved hjelp av en enkel linje i konfigurasjonen: echo ‘omit_dracutmodules+=" systemd "’ > /etc/dracut.conf.d/luks-workaround.conf
Dette fratrekkte oss all gleden av systemd — den raske og parallelle oppstarten av systemtjenester, noe som betydelig reduserte oppstartstiden.
Situasjonen er fortsatt den samme: 905683
Uten Ä vente pÄ en lÞsning, laget jeg en for meg selv, og nÄ deler jeg den med publikum; hvis du er interessert, les videre.
dracut + systemd + LUKS + usbflash = automatisk oppgradering

Innledning

Systemd ga meg ingen fĂžlelser da jeg fĂžrst begynte Ă„ jobbe med CentOS 7, ettersom jeg i starten ikke merket noen betydelig forskjell bortsett fra mindre endringer i syntaksen for styring av tjenester. Senere ble jeg glad i systemd, men det fĂžrste inntrykket var litt Ăždelagt fordi utviklerne av dracut ikke brukte mye tid pĂ„ Ă„ stĂžtte oppstartsprosessen ved hjelp av systemd sammen med disk-kryptering. Generelt fungerte det, men Ă„ mĂ„tte skrive inn passordet for disken ved hver oppstart var serveren — ikke akkurat spennende.
Etter Ă„ ha prĂžvd mange anbefalinger og lest manualen, forsto jeg at det var mulig Ă„ konfigurere USB i systemd-modus, men kun ved manuell assosiasjon av hver disk med nĂžkkelen pĂ„ USB-minnepinnen, og selve USB-minnepinnen kunne bare knyttes til dens UUID; LABEL fungerte ikke. Å opprettholde noe slikt var ikke veldig praktisk for meg, sĂ„ til slutt ga jeg meg til Ă„ vente, og etter Ă„ ha ventet i nesten 7 Ă„r, forsto jeg — ingen hadde til hensikt Ă„ lĂžse problemet.

Problemer

SelvfÞlgelig kan nesten alle skrive et eget plugin til dracut, men Ä fÄ det til Ä fungere er ikke sÄ enkelt. Det viste seg at pÄ grunn av den parallelle naturen til systemd, var det ikke lett Ä inkludere sin egen kode og endre oppstartsprosessen. Dokumentasjonen til dracut forklarte ikke alt. Men etter langvarige eksperimenter klarte jeg Ä lÞse oppgaven.

Hvordan det fungerer

Kjernen bestÄr av tre enheter:

  1. luks-auto-key.service — sþker etter lagringsenheter med nþkler for LUKS
  2. luks-auto.target — fungerer som en avhengighet for de innebygde systemd-cryptsetup enhetene
  3. luks-auto-clean.service — fjerner midlertidige filer opprettet av luks-auto-key.service

luks-auto-generator.sh er et skript som kjÞres av systemd og genererer enheter basert pÄ kjerneparametere. Tilsvarende generatorer lager enheter for fstab osv.

luks-auto-generator.sh

Med drop-in.conf endres oppfĂžrselen til standard systemd-cryptsetup ved Ă„ legge luks-auto.target som en avhengighet.

luks-auto-key.service og luks-auto-key.sh

Denne enheten kjÞrer skriptet luks-auto-key.sh, som, basert pÄ nÞklene rd.luks.*, finner lagringsenheter med nÞkler og kopierer dem til en midlertidig katalog for videre bruk. Etter at prosessen er fullfÞrt, fjerner luks-auto-clean.service nÞklene fra den midlertidige katalogen.

Kildekode:

/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 nĂžkkelsĂžker
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 mÄl
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 nĂžkkelrenser
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 "

Installasjon


mkdir -p \/usr\/lib\/dracut\/modules.d\/99luks-auto\/
# plasserer nesten alle filer her
chmod +x \/usr\/lib\/dracut\/modules.d\/99luks-auto\/*.sh
# oppretter filen \/etc\/dracut.conf.d\/luks-auto.conf
# og genererer ny initramfs
dracut -f

Konklusjon

For enkelhets skyld har jeg bevart kompatibiliteten med kjerneparameterne for kommandolinjen lik den for sysvinit-modus, noe som gjĂžr det enklere Ă„ bruke i eldre installasjoner.

Kilde: habr.com

KjĂžp pĂ„litelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere đŸ”„ KjĂžp pĂ„litelig hosting for nettsteder med DDoS-beskyttelse, VPS VDS-servere | ProHoster