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 بالتزامن مع تشفير القرص. بشكل عام، نجح الأمر، لكن إدخال كلمة مرور القرص في كل مرة يبدأ فيها الخادم ليس هو الشيء الأكثر إثارة للاهتمام.
بعد تجربة مجموعة من التوصيات ودراسة الدليل، أدركت أنه في وضع النظام، من الممكن تكوين USB، ولكن فقط من خلال الارتباط اليدوي لكل قرص بمفتاح على قرص USB، ولا يمكن ربط قرص USB نفسه إلا من خلاله UUID، LABEL لم يعمل. لم يكن من المناسب جدًا الحفاظ على هذا في المنزل، لذلك في النهاية انغمست في الانتظار، وبعد الانتظار لمدة 7 سنوات تقريبًا، أدركت أنه لن يحل أحد المشكلة.

مشاكل

بالطبع، يمكن لأي شخص تقريبًا كتابة المكون الإضافي الخاص به لـ dracut، لكن جعله يعمل لم يعد بهذه السهولة. اتضح أنه نظرًا للطبيعة الموازية لبدء تشغيل systemd، فليس من السهل تضمين التعليمات البرمجية الخاصة بك وتغيير تقدم التحميل. وثائق dracut لم تشرح كل شيء. لكن بعد تجارب طويلة تمكنت من حل المشكلة.

كيف تعمل

ويرتكز على ثلاث وحدات:

  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 ويقوم بإنشاء وحدات بناءً على معلمات kernel. يتم إنشاء مولدات مماثلة بواسطة وحدات 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

اختتام

من أجل الراحة، لقد حافظت على التوافق مع خيارات سطر أوامر kernel كما هو الحال في وضع sysvinit، مما يسهل استخدامه في التثبيتات القديمة.

المصدر: www.habr.com

إضافة تعليق