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
Не дождавшись решения, я сделал его для себя сам, а теперь делюсь и с общественностью, кому интересно, читайте дальше.

Введение

Systemd, когда я только начал работать с Centos 7 не вызвал никаких эмоций, так как кроме незначительного изменения в синтаксисе управления службами, я не почувствовал вначале особой разницы. Впоследствии systemd мне понравился, но первое впечатление было немного подпорчено, так как разработчики dracut не особо уделяли время на поддержку процесса загрузки с помощью systemd совместно с шифрованием дисков. В целом оно работало, но вводить пароль от диска при каждом зауске сервера — не самое интересное занятие.
Опробовав кучу рекомендаций и учитавшись мануалом я понял, что в режиме 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 и осуществляет генерацию юнитов на основе параметров ядра. Аналогичные генераторы создают юниты 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@luks\x2d${_UUID_ESC}.service.d" 	cat > "${SYSTEMD_RUN}/systemd-cryptsetup@luks\x2d${_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-by\x2duuid-${_UUID_ESC}.device After=dev-disk-by\x2duuid-${_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, что облегчает использование в старых инсталяция.

ссылка на оригинал статьи https://habr.com/ru/post/504800/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *