dracut + systemd + LUKS + usbflash = قفل خودکار

داستان خیلی وقت پیش شروع شد، زمانی که Centos 7 (RHEL 7) منتشر شد. اگر از رمزگذاری در درایوهای دارای Centos 6 استفاده می کردید، پس از اتصال یک درایو فلش USB با کلیدهای لازم، هیچ مشکلی با باز کردن خودکار درایوها وجود نداشت. با این حال، زمانی که 7 منتشر شد، ناگهان همه چیز آنطور که شما عادت داشتید کار نکرد. سپس می‌توان با استفاده از یک خط ساده در پیکربندی، راه‌حلی برای برگرداندن dracut به sysvinit پیدا کرد: echo 'omit_dracutmodules+=" systemd "' > /etc/dracut.conf.d/luks-workaround.conf
که بلافاصله ما را از تمام جذابیت های systemd محروم کرد - راه اندازی سریع و موازی خدمات سیستم، که به طور قابل توجهی زمان راه اندازی سیستم را کاهش داد.
چیزهایی که هنوز وجود دارد: 905683
بدون اینکه منتظر راه حلی باشم، آن را برای خودم ساختم، و اکنون آن را با عموم به اشتراک می گذارم، کسانی که علاقه مند هستند، ادامه مطلب را بخوانید.
dracut + systemd + LUKS + usbflash = قفل خودکار

معرفی

Systemd، زمانی که برای اولین بار با Centos 7 شروع به کار کردم، هیچ احساسی ایجاد نکرد، زیرا جدا از یک تغییر جزئی در نحو مدیریت سرویس، در ابتدا تفاوت زیادی احساس نکردم. متعاقباً من systemd را دوست داشتم ، اما اولین برداشت کمی خراب شد ، زیرا توسعه دهندگان dracut زمان زیادی را برای پشتیبانی از فرآیند بوت با استفاده از systemd در ارتباط با رمزگذاری دیسک صرف نکردند. به طور کلی، کار می کرد، اما وارد کردن رمز عبور دیسک هر بار که سرور راه اندازی می شود، جالب ترین چیز نیست.
پس از آزمایش تعدادی توصیه و مطالعه کتابچه راهنمای کاربر، متوجه شدم که در حالت systemd پیکربندی با USB امکان پذیر است، اما فقط با ارتباط دستی هر دیسک با یک کلید روی یک دیسک USB، و خود دیسک USB فقط می تواند توسط آن مرتبط شود. UUID، LABEL کار نکرد. نگهداری از آن در خانه خیلی راحت نبود، بنابراین در نهایت در انتظار فرو رفتم و پس از تقریباً 7 سال انتظار، متوجه شدم که هیچ کس قرار نیست مشکل را حل کند.

مشکلات

البته تقریباً هر کسی می‌تواند افزونه خود را برای dracut بنویسد، اما کارکردن آن دیگر چندان آسان نیست. مشخص شد که به دلیل ماهیت موازی راه‌اندازی systemd، گنجاندن کد شما و تغییر روند بارگذاری چندان آسان نیست. اسناد دراکات همه چیز را توضیح نداد. با این حال، پس از آزمایش های طولانی، من توانستم مشکل را حل کنم.

چگونه کار می کند

این بر اساس سه واحد است:

  1. luks-auto-key.service - درایوهای دارای کلید LUKS را جستجو می کند
  2. luks-auto.target - به عنوان یک وابستگی برای واحدهای systemd-cryptsetup داخلی عمل می کند
  3. luks-auto-clean.service - فایل های موقت ایجاد شده توسط luks-auto-key.service را پاک می کند

و luks-auto-generator.sh اسکریپتی است که توسط systemd راه اندازی می شود و واحدهایی را بر اساس پارامترهای هسته تولید می کند. ژنراتورهای مشابه توسط واحدهای fstab و غیره ایجاد می شوند.

luks-auto-generator.sh

با استفاده از drop-in.conf، رفتار systemd-cryptsetup استاندارد با افزودن luks-auto.target به وابستگی آنها تغییر می کند.

luks-auto-key.service و luks-auto-key.sh

این واحد اسکریپت luks-auto-key.sh را اجرا می کند که بر اساس کلیدهای rd.luks.*، رسانه را با کلیدها پیدا می کند و آنها را برای استفاده بیشتر در یک فهرست موقت کپی می کند. پس از تکمیل فرآیند، کلیدها توسط luks-auto-clean.service از فهرست موقت حذف می شوند.

منابع:

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

نصب


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

نتیجه

برای راحتی، من سازگاری با گزینه های خط فرمان هسته مانند حالت sysvinit را حفظ کرده ام، که استفاده از آن را در نصب های قدیمی آسان تر می کند.

منبع: www.habr.com

اضافه کردن نظر