90crypt: key on rem. device enhancements
RFC 1/1 90crypt: key on rem. device enhancements
Hi,
I'm doing some major modification of crypt module. Here's what I've done. If that's OK, I'll send cleaned up patch with docs update. Any remarks at this point are appreciated.
99base/dracut-lib.sh: new func.: do_mount, funiq, mkuniqdir
90crypt/cryptroot-ask.sh: new func.: search_key_for
The patch introduces support for labels and normal dev names for removable media storing key for LUKS. Previously only UUID was possible. rd_LUKS_KEYDEV_UUID changes to rd_LUKS_KEYDEV which takes dev names as in /etc/fstab (e.g.: rd_LUKS_KEYDEV=LABEL=boot).
It also fixes problem with devices not discovered on time by retrying (for default 3 times with incrementing sleep period) the scan.
probe_keydev informs which device it checks.
Moreover code (the rem. dev. part) is refactorized.
--- modules.d/90crypt/cryptroot-ask.sh | 98 ++++++++++++++++++---------- modules.d/90crypt/install | 1 + modules.d/99base/dracut-lib.sh | 123 ++++++++++++++++++++++++++++++++++-- 3 files changed, 182 insertions(+), 40 deletions(-)
modules.d/90crypt/cryptroot-ask.sh +63 -35
@@ -58,53 +58,82 @@ fi # # Try to mount device specified by UUID and probe for existence of any of -# the paths. On success return 0 and print "<uuid> <first-existing-path>", +# the paths. On success return 0 and print "<uuid>\t<first-existing-path>", # otherwise return 1. -# Function leaves mount point created. +# +# $1 = dev +# $2 = keypaths probe_keydev() { - local uuid="$1"; shift; local keypaths="$*" - local ret=1; local mount_point=/mnt/keydev + local dev="$1"; shift; local keypaths="$*" + local ret=1; local mount_point=$(mkuniqdir /mnt keydev) local path - [ -n "${uuid}" -a -n "${keypaths}" ] || return 1 - [ -d ${mount_point} ] || mkdir -p "${mount_point}" || return 1 + [ -n "${dev}" -a -n "${keypaths}" ] || return 1 + [ -d "${mount_point}" ] || die 'Mount point does not exist!' - if mount -r -U "${uuid}" "${mount_point}" 2>/dev/null >/dev/null; then + info "cryptroot-ask: Probing ${dev}..." + if do_mount -q "${dev}" "${mount_point}"; then for path in ${keypaths}; do if [ -f "${mount_point}/${path}" ]; then - echo "${uuid} ${path}" + echo "${dev} ${path}" ret=0 break fi done + + umount "${mount_point}" fi - umount "${mount_point}" 2>/dev/null >/dev/null + rmdir "${mount_point}" return ${ret} } -keypaths="$(getargs rd_LUKS_KEYPATH)" -unset keydev_uuid keypath - -if [ -n "$keypaths" ]; then - keydev_uuids="$(getargs rd_LUKS_KEYDEV_UUID)" - [ -n "$keydev_uuids" ] || { - warn 'No UUID of device storing LUKS key specified.' - warn 'It is recommended to set rd_LUKS_KEYDEV_UUID.' - warn 'Performing scan of *all* devices accessible by UUID...' - } - tmp=$(foreach_uuid_until "probe_keydev \$full_uuid $keypaths" \ - $keydev_uuids) && { - keydev_uuid="${tmp%% *}" - keypath="${tmp#* }" - } || { - warn "Key for $device not found." - } - unset tmp keydev_uuids -fi +# $1 = encrypted device +# $2 = tries (optional; default is 3) +# rd_LUKS_KEYPATH +# rd_LUKS_KEYDEV +# +# returns 0 if search succeeded +# returns 1 if search failed +# returns 2 if search skipped (when rd_LUKS_KEYPATH not specified) +search_key_for() { + local device="$1"; local tries="$2" + local keypaths="$(getargs rd_LUKS_KEYPATH)"; local keydevs + + if [ -n "$keypaths" ]; then + keydevs="$(getargs rd_LUKS_KEYDEV)" + [ -n "$keydevs" ] || { + warn 'No device storing LUKS key specified.' + warn 'It is recommended to set rd_LUKS_KEYDEV (best by UUID).' + warn 'Performing scan of *all* devices accessible by UUID...' + } + + { [ -z "$2" ] || [ $2 -lt 1 ]; } && tries=3 + local i=0 + while [ $i -lt 3 ]; do + sleep $i + # following outputs "$keydev\t$keypath" (on success only) + foreach_dev_until "probe_keydev \$___ $keypaths" $keydevs && \ + return 0 + warn "Key for $device not found. Trying again..." + i=$(($i+1)) + done + + return 1 + fi + + return 2 +} + +mkdir -p /mnt + +tmp=$(search_key_for $device) && { + keydev="${tmp%% *}" + keypath="${tmp#* }" +} +unset tmp -unset keypaths # # Open LUKS device
@@ -112,14 +141,13 @@ unset keypaths info "luksOpen $device $luksname" -if [ -n "$keydev_uuid" ]; then - mntp=/mnt/keydev - mkdir -p "$mntp" - mount -r -U "$keydev_uuid" "$mntp" +if [ -n "$keydev" ]; then + mntp=$(mkuniqdir /mnt keydev) + do_mount "$keydev" "$mntp" || die 'Mounting rem. dev. failed!' cryptsetup -d "$mntp/$keypath" luksOpen "$device" "$luksname" umount "$mntp" - rmdir -p "$mntp" 2>/dev/null - unset mntp keypath keydev_uuid + rmdir "$mntp" + unset mntp keypath keydev else # Prompt for password with plymouth, if installed. # Should we check if plymouthd is running?
modules.d/90crypt/install +1 -0
@@ -2,6 +2,7 @@ # -*- mode: shell-script; indent-tabs-mode: nil; sh-basic-offset: 4; -*- # ex: ts=8 sw=4 sts=4 et filetype=sh inst cryptsetup +inst rmdir inst "$moddir"/cryptroot-ask.sh /sbin/cryptroot-ask inst_hook cmdline 30 "$moddir/parse-crypt.sh" inst_hook pre-pivot 30 "$moddir/crypt-cleanup.sh"
modules.d/99base/dracut-lib.sh +118 -5
156 lines not on this page · raw message
Signature · 1 line
1.7.3
A message that is not in the archive (sent off-list or not yet received): 4c9e7c0d.d39ccc0a.676a.684a-ATjtLOhZ0NVl57MIdRCFDg@public.gmane.org
Quoted 19 lines · Amadeusz Żołnowski
modules.d/99base/dracut-lib.sh +19
-0+# Evaluate command for every given device. Every single device must be +# specified either by path, by label prefixed with 'LABEL=' or UUID prefixed +# with 'UUID='. UUIDs are processed by 'foreach_uuid_until'. List elements' +# order is preserved. +# +# foreach_dev_until command devices +# +# command - command to be evaluated +# devices - list of devices separated by space +# +# The function returns after *first successful evaluation* of the given command +# with status 0. If evaluation fails for every device, function returns with +# status 1. +# +# Example: +# foreach_dev_until "echo \$___; false" "/dev/sda1 LABEL=boot UUID=123a" +foreach_dev_until() { + local cmd="$1"; shift; local devs_list="$*" + local dev; local ___
modules.d/99base/dracut-lib.sh ip_to_var() { · line 357
Quoted 10 lines
modules.d/99base/dracut-lib.sh +10
-0+ + [ -n "${cmd}" ] || return 1 + + if [ -n "${devs_list}" ]; then + for dev in ${devs_list}; do + if strstarts "${dev}" 'UUID='; then + foreach_uuid_until -p 'UUID=' "${cmd}" "${dev#UUID=*}" && \ + return 0 + else + [ -e "${dev}" ] || [ -e "/dev/disk/by-label/${dev#LABEL=}" ] \
hmm.. how does it work with blank or non-ascii chars in LABELs?
...
Quoted 20 lines
modules.d/99base/dracut-lib.sh +20
-0+# It's a wrapper around 'mount' command. In addition to 'mount' you can specify +# device name like "UUID=01234567-89ab-cdef-0123-4567890abcde" and +# "LABEL=fun_label". 'do_mount' mounts device as read-only for default. To +# mount as writable pass '-w' option. 'mount_point' argument is required. +# +# do_mount [options] dev mount_point +# +# Extra options: +# -q Suppress any output. +# +# Example: +# do_mount -q -w LABEL=blah /mnt/disk +do_mount() { + local args; local quiet + + while [ $# -gt 2 ]; do + case $1 in + -q) quiet='2>/dev/null >/dev/null' ;; + -v) quiet=''; args="${args} $1" ;; + *) args="${args} $1" ;;
modules.d/99base/dracut-lib.sh ip_to_var() { · line 399
Quoted 10 lines
modules.d/99base/dracut-lib.sh +10
-0+ esac + shift + done + + local dev_name="$1"; local dev="${dev_name#*=}"; local mount_point="$2" + + case ${dev_name} in + -*) die 'do_mount - wrong usage!' ;; + UUID=*) args="${args} -U" ;; + LABEL=*) args="${args} -L" ;;
mount(8) supports LABEL= and UUID= tags, for example
mount LABEL=boot /mnt/test
so, you does not need to convert these tags to -L or -U options.
Karel
Signature · 2 lines
Karel Zak <[email protected]> http://karelzak.blogspot.com
A message that is not in the archive (sent off-list or not yet received): 20100930232719.GC6389-sHeGUpI7y9L/[email protected]
Excerpts from Karel Zak's message of Fri Oct 01 01:28:09 +0200 2010:
Quoted 24 lines · Amadeusz Żołnowski
Quoted 23 lines
modules.d/99base/dracut-lib.sh +23
-0+# Evaluate command for every given device. Every single device must be +# specified either by path, by label prefixed with 'LABEL=' or UUID prefixed +# with 'UUID='. UUIDs are processed by 'foreach_uuid_until'. List elements' +# order is preserved. +# +# foreach_dev_until command devices +# +# command - command to be evaluated +# devices - list of devices separated by space +# +# The function returns after *first successful evaluation* of the given command +# with status 0. If evaluation fails for every device, function returns with +# status 1. +# +# Example: +# foreach_dev_until "echo \$___; false" "/dev/sda1 LABEL=boot UUID=123a" +foreach_dev_until() { + local cmd="$1"; shift; local devs_list="$*" + local dev; local ___ + + [ -n "${cmd}" ] || return 1 + + if [ -n "${devs_list}" ]; then
Quoted 8 lines
modules.d/99base/dracut-lib.sh ip_to_var() { · line 357
Quoted 6 lines
modules.d/99base/dracut-lib.sh +6
-0+ for dev in ${devs_list}; do + if strstarts "${dev}" 'UUID='; then + foreach_uuid_until -p 'UUID=' "${cmd}" "${dev#UUID=*}" && \ + return 0 + else + [ -e "${dev}" ] || [ -e "/dev/disk/by-label/${dev#LABEL=}" ] \hmm.. how does it work with blank or non-ascii chars in LABELs?
Thank you for pointing this out!
It doesn't work with non-printable or spaces. Moreover 'getargs' doesn't expect spaces (even quoted with ""), so you cannot specify LABEL including those chars.
I've just fixed issues around 'foreach_dev_until'. (I'll send patch in a moment.) Label may contain spaces, tabs, greek letters or whatever now. I still left 'getopts' as is, so this new feature isn't easly available yet, but I'll fix that - don't worry. :-)
Quoted 5 lines
mount(8) supports LABEL= and UUID= tags, for example
mount LABEL=boot
/mnt/testso, you does not need to convert these tags to -L or -U options.
I'm amazed I've missed that feature. Thank you for highlighting it! I've removed the redundant 'do_mount'.
Cheers,
Signature · 3 lines
Amadeusz Żołnowski PGP key: 1024D/C284750D