dracut + systemd + LUKS + usbflash = autoodblokowanie

Historia zaczęła się dawno, jeszcze przy wydaniu CentOS 7 (RHEL 7). Jeśli używałeś szyfrowania dysków w CentOS 6, nie było żadnych problemów z automatycznym odblokowywaniem dysków po podłączeniu pamięci USB z odpowiednimi kluczami. Jednak po wydaniu siódemki nagle wszystko przestało działać tak, jak przywykłeś. Można było znaleźć rozwiązanie w powrocie dracuta do sysvinit za pomocą prostej linii w konfiguracji: echo ‘omit_dracutmodules+=" systemd "’ > /etc/dracut.conf.d/luks-workaround.conf
Co natychmiast pozbawiało nas całej przyjemności systemd — szybkie i równoległe uruchamianie usług systemowych, co znacznie skracało czas uruchamiania systemu.
I tak to trwa: 905683
Nie czekając na rozwiązanie, sam stworzyłem je dla siebie, a teraz dzielę się nim z publicznością, kto jest zainteresowany, niech czyta dalej.
dracut + systemd + LUKS + usbflash = autoodblokowanie

Wprowadzenie

Systemd, kiedy tylko zacząłem pracować z CentOS 7, nie wzbudzał żadnych emocji, ponieważ poza niewielką zmianą w składni zarządzania usługami, na początku nie odczułem szczególnej różnicy. Z biegiem czasu systemd mi się spodobał, ale pierwsze wrażenie było nieco zepsute, ponieważ deweloperzy dracuta nie poświęcali zbyt wiele czasu na wsparcie procesu uruchamiania z systemd w połączeniu z szyfrowaniem dysków. W zasadzie działało, ale wpisywanie hasła do dysku przy każdym uruchomieniu serwera — nie jest najciekawszą rzeczą.
Wypróbowałem mnóstwo rekomendacji i studiując podręcznik, zrozumiałem, że w trybie systemd można skonfigurować USB, ale tylko przy ręcznym powiązaniu każdego dysku z kluczem na dysku USB, przy czym sam dysk USB można powiązać tylko po jego UUID, LABEL nie działał. Utrzymywanie tego u siebie nie było zbyt wygodne, dlatego ostatecznie zanurzyłem się w oczekiwanie i, czekając prawie 7 lat, zrozumiałem — nikt nie ma zamiaru rozwiązać problemu.

Problemy

Oczywiście, napisać swój plugin do dracut może prawie każdy, ale sprawić, by działał, to już nie takie proste. Okazało się, że ze względu na równoległą naturę uruchamiania systemd włączenie własnego kodu i zmiana przebiegu uruchamiania nie jest takie łatwe. Dokumentacja do dracuta nie wyjaśniała wszystkiego. Jednak w wyniku długotrwałych eksperymentów udało mi się rozwiązać problem.

Jak to działa

U podstaw leżą trzy jednostki:

  1. luks-auto-key.service — szuka nośników z kluczami dla LUKS
  2. luks-auto.target — pełni rolę zależności dla wbudowanych jednostek systemd-cryptsetup
  3. luks-auto-clean.service — usuwa tymczasowe pliki utworzone przez luks-auto-key.service

luks-auto-generator.sh to skrypt, który jest uruchamiany przez systemd i generuje jednostki na podstawie parametrów jądra. Podobne generatory tworzą jednostki fstab itd.

luks-auto-generator.sh

Dzięki drop-in.conf zmienia się zachowanie standardowego systemd-cryptsetup, dodając im zależność luks-auto.target.

luks-auto-key.service i luks-auto-key.sh

Ten jednostka uruchamia skrypt luks-auto-key.sh, który, opierając się na kluczach rd.luks.*, znajduje nośniki z kluczami i kopiuje je do tymczasowego katalogu do późniejszego użycia. Po zakończeniu procesu klucze z tymczasowego katalogu są usuwane przez luks-auto-clean.service.

Źródła:

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

Instalacja


mkdir -p \/usr\/lib\/dracut\/modules.d\/99luks-auto\/
# umieszczamy tutaj prawie wszystkie pliki
chmod +x \/usr\/lib\/dracut\/modules.d\/99luks-auto\/*.sh
# tworzymy plik \/etc\/dracut.conf.d\/luks-auto.conf
# I generujemy nowy initramfs
dracut -f

Podsumowanie

Dla wygody zachowałem zgodność z parametrami wiersza poleceń rdzenia zarówno dla trybu sysvinit, co ułatwia użycie w starszych instalacjach.

Źródło: habr.com

Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS 🔥 Kup solidny hosting stron z ochroną przed DDoS, serwery VPS VDS | ProHoster