dracut + systemd + LUKS + usbflash = déverrouillage automatique

L'histoire a commencé il y a longtemps, lors de la sortie de Centos 7 (RHEL 7). Si vous avez utilisé le chiffrement des disques avec Centos 6, il n'y avait aucun problème de déverrouillage automatique des disques lors de la connexion d'une clé USB avec les clés nécessaires. Cependant, avec la sortie de la version 7, tout a soudainement cessé de fonctionner comme vous en aviez l'habitude. À ce moment-là, on pouvait trouver une solution en revenant de dracut à sysvinit grâce à une simple ligne dans le fichier de configuration : echo ‘omit_dracutmodules+=" systemd "’ > /etc/dracut.conf.d/luks-workaround.conf
Ce qui nous privait immédiatement de toute la beauté de systemd — le démarrage rapide et parallèle des services système, ce qui réduisait considérablement le temps de démarrage du système.
Et toujours rien : 905683
Ne sachant pas attendre une solution, je l'ai créée moi-même et maintenant je la partage avec le public, pour ceux que cela intéresse, lisez la suite.
dracut + systemd + LUKS + usbflash = déverrouillage automatique

Introduction

Systemd, quand j'ai commencé à travailler avec Centos 7, n'a suscité aucune émotion, car mis à part un changement mineur dans la syntaxe de gestion des services, je n'ai pas ressenti de différence particulière au début. Par la suite, j'ai aimé systemd, mais la première impression a été un peu entachée, car les développeurs de dracut ne consacraient pas beaucoup de temps au soutien du processus de démarrage avec systemd en conjonction avec le chiffrement des disques. En général, cela fonctionnait, mais taper le mot de passe du disque à chaque démarrage — ce n'est pas le faire le plus intéressant. de serveurs — ce n'est pas une occupation des plus passionnantes.
Après avoir essayé une multitude de recommandations et avoir étudié le manuel, j'ai compris qu'en mode systemd, il était possible de configurer avec USB, mais seulement en associant manuellement chaque disque à la clé sur la clé USB, et que la clé USB elle-même ne pouvait être liée que par son UUID, l'étiquette (LABEL) ne fonctionnant pas. Maintenir cela chez moi n'était pas très pratique, donc finalement je me suis plongé dans l'attente et, après avoir attendu près de 7 ans, j'ai compris — personne ne s'apprête à résoudre le problème.

Problèmes

Bien sûr, presque tout le monde peut écrire son propre plugin pour dracut, mais en faire un qui fonctionne, c'est déjà moins simple. Il s'est avéré qu'en raison de la nature parallèle du démarrage de systemd, inclure mon propre code et modifier le processus de démarrage n'est pas aussi facile. La documentation de dracut n'expliquait pas tout. Cependant, suite à de longues expériences, j'ai pu résoudre le problème.

Comment cela fonctionne

À la base, il y a trois unités :

  1. luks-auto-key.service — recherche des supports avec des clés pour LUKS
  2. luks-auto.target — joue le rôle de dépendance pour les unités intégrées de systemd-cryptsetup
  3. luks-auto-clean.service — nettoie les fichiers temporaires créés par luks-auto-key.service

luks-auto-generator.sh est un script qui s’exécute avec systemd et génère des unités en fonction des paramètres du noyau. Des générateurs similaires créent des unités fstab, etc.

luks-auto-generator.sh

Avec drop-in.conf, le comportement par défaut de systemd-cryptsetup est modifié en ajoutant la dépendance à luks-auto.target.

luks-auto-key.service et luks-auto-key.sh

Cette unité exécute le script luks-auto-key.sh qui, basé sur les clés rd.luks.*, détecte les dispositifs contenant les clés et les copie dans un répertoire temporaire pour une utilisation future. Après la fin du processus, les clés du répertoire temporaire sont supprimées par luks-auto-clean.service.

Sources :

/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=Rechercheur de clés AUTO LUKS
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=Cible AUTO LUKS
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=Nettoyeur de clés AUTO LUKS
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 "

Installation


mkdir -p /usr/lib/dracut/modules.d/99luks-auto/
# On place ici presque tous les fichiers
chmod +x /usr/lib/dracut/modules.d/99luks-auto/*.sh
# On crée le fichier /etc/dracut.conf.d/luks-auto.conf
# Et on génère un nouvel initramfs
dracut -f

Conclusion

Pour plus de commodité, j'ai conservé la compatibilité avec les paramètres de ligne de commande du noyau comme pour le mode sysvinit, ce qui facilite l'utilisation dans les anciennes installations.

Source : habr.com

Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS 🔥 Acheter un hébergement fiable pour les sites avec protection DDoS, serveurs VPS VDS | ProHoster