dracut + systemd + LUKS + usbflash = αυτόματο ξεκλείδωμα

Η ιστορία ξεκίνησε πριν από πολύ καιρό, όταν κυκλοφόρησε το Centos 7 (RHEL 7). Εάν χρησιμοποιήσατε κρυπτογράφηση σε μονάδες με Centos 6, τότε δεν υπήρχαν προβλήματα με το αυτόματο ξεκλείδωμα των μονάδων δίσκου όταν συνδέσατε μια μονάδα flash USB με τα απαραίτητα κλειδιά. Ωστόσο, όταν κυκλοφόρησε το 7, ξαφνικά όλα δεν λειτουργούσαν όπως τα είχες συνηθίσει. Στη συνέχεια, ήταν δυνατό να βρεθεί μια λύση για την επιστροφή του dracut στο sysvinit χρησιμοποιώντας μια απλή γραμμή στο config: 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, αλλά η λειτουργία του δεν είναι πλέον τόσο εύκολη. Αποδείχθηκε ότι λόγω της παράλληλης φύσης της εκκίνησης του συστήματος, δεν είναι τόσο εύκολο να συμπεριλάβετε τον κωδικό σας και να αλλάξετε την πρόοδο φόρτωσης. Η τεκμηρίωση για το 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@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

Προσθέστε ένα σχόλιο