initramfs: add support for multiple crypted root and swap devices

It is now possible to pass multiple crypt_roots= and crypt_swaps=
parameters (mind the "s") and have multiple devices concurring to
building the final root device and swap areas activated.
The change is backward compatible and crypt_root=, crypt_swap= is
still fully supported.

This change makes possible to support multiple device mapper block
devices grouped together into aggregated software raid devices.
For instance, two individual luks devices can be now used to build
a single raid0 or raid1 md device.

Moreover, genkernel-next initramfs now supports multiple encrypted
swap devices.
master
Fabio Erculiani 11 years ago
parent cda54cd13b
commit b4307cc484

@ -14,15 +14,17 @@ _bootstrap_key() {
} }
_crypt_exec() { _crypt_exec() {
local luks_dev="${1}"
local cmd="${2}"
# TODO(lxnay): this fugly crypt_silent should really go away # TODO(lxnay): this fugly crypt_silent should really go away
if [ "${CRYPT_SILENT}" = "1" ]; then if [ "${CRYPT_SILENT}" = "1" ]; then
eval ${1} >/dev/null 2>/dev/null eval ${cmd} >/dev/null 2>/dev/null
else else
ask_for_password --ply-tries 5 \ ask_for_password --ply-tries 5 \
--ply-cmd "${1}" \ --ply-cmd "${cmd}" \
--ply-prompt "Encryption password (${LUKS_DEVICE}): " \ --ply-prompt "Encryption password (${luks_dev}): " \
--tty-tries 5 \ --tty-tries 5 \
--tty-cmd "${1}" || return 1 --tty-cmd "${cmd}" || return 1
return 0 return 0
fi fi
} }
@ -30,19 +32,22 @@ _crypt_exec() {
_open_luks() { _open_luks() {
case ${1} in case ${1} in
root) root)
local ltypes=ROOTS
local ltype=ROOT local ltype=ROOT
;; ;;
swap) swap)
local ltypes=SWAPS
local ltype=SWAP local ltype=SWAP
;; ;;
esac esac
eval local LUKS_DEVICE='"${CRYPT_'${ltype}'}"' eval local luks_devices='"${CRYPT_'${ltypes}'}"'
eval local LUKS_KEY='"${CRYPT_'${ltype}'_KEY}"' eval local luks_key='"${CRYPT_'${ltype}'_KEY}"'
eval local LUKS_KEYDEV='"${CRYPT_'${ltype}'_KEYDEV}"' eval local luks_keydev='"${CRYPT_'${ltype}'_KEYDEV}"'
eval local LUKS_TRIM='"${CRYPT_'${ltype}'_TRIM}"' eval local luks_trim='"${CRYPT_'${ltype}'_TRIM}"'
local luks_name="${1}"
local LUKS_NAME="${1}"
local dev_error=0 key_error=0 keydev_error=0 local dev_error=0 key_error=0 keydev_error=0
local mntkey="/mnt/key/" cryptsetup_opts="" local mntkey="/mnt/key/" cryptsetup_opts=""
@ -51,27 +56,34 @@ _open_luks() {
return 1 return 1
fi fi
local exit_st= local real_dev=
if [ "${ltype}" = "ROOT" ]; then
real_dev="${REAL_ROOT}"
elif [ "${ltype}" = "SWAP" ]; then
real_dev="${REAL_RESUME}"
fi
local exit_st= luks_device=
for luks_device in ${luks_devices}; do
good_msg "Working on device ${luks_device}..."
while true; do while true; do
local gpg_cmd="" local gpg_cmd=""
exit_st=1 exit_st=1
# do not force the link to /dev/mapper/root # do not force the link to /dev/mapper/root
# but rather use the value from root=, which is # but rather use the value from root=, which is
# in ${REAL_ROOT} # in ${REAL_ROOT}
local luks_dev_name=$(basename "${LUKS_DEVICE}") local luks_dev_name=$(basename "${luks_device}")
local real_dev=
local luks_name_prefix= local luks_name_prefix=
if [ "${ltype}" = "ROOT" ]; then
real_dev="${REAL_ROOT}"
elif [ "${ltype}" = "SWAP" ]; then
real_dev="${REAL_RESUME}"
fi
if echo "${real_dev}" | grep -q "^/dev/mapper/"; then if echo "${real_dev}" | grep -q "^/dev/mapper/"; then
local real_dev_bn=$(basename "${real_dev}")
# If we use LVM + cryptsetup, we may have collisions between # If we use LVM + cryptsetup, we may have collisions between
# the two inside /dev/mapper. So, make up a way to avoid them. # the two inside /dev/mapper. So, make up a way to avoid them.
LUKS_NAME="${LUKS_NAME}_${luks_dev_name}-$(basename ${real_dev})" luks_dev_name="${luks_name}_${luks_dev_name}-${real_dev_bn}"
fi fi
# if crypt_silent=1 and some error occurs, bail out. # if crypt_silent=1 and some error occurs, bail out.
@ -86,55 +98,56 @@ _open_luks() {
fi fi
if [ "${dev_error}" = "1" ]; then if [ "${dev_error}" = "1" ]; then
prompt_user "LUKS_DEVICE" "${LUKS_NAME}" prompt_user "luks_device" "${luks_dev_name}"
dev_error=0 dev_error=0
continue continue
fi fi
if [ "${key_error}" = "1" ]; then if [ "${key_error}" = "1" ]; then
prompt_user "LUKS_KEY" "${LUKS_NAME} key" prompt_user "luks_key" "${luks_dev_name} key"
key_error=0 key_error=0
continue continue
fi fi
if [ "${keydev_error}" = "1" ]; then if [ "${keydev_error}" = "1" ]; then
prompt_user "LUKS_KEYDEV" "${LUKS_NAME} key device" prompt_user "luks_keydev" "${luks_dev_name} key device"
keydev_error=0 keydev_error=0
continue continue
fi fi
local luks_dev=$(find_real_device "${LUKS_DEVICE}") local luks_dev=$(find_real_device "${luks_device}")
[ -n "${luks_dev}" ] && LUKS_DEVICE="${luks_dev}" # otherwise hope... [ -n "${luks_dev}" ] && \
luks_device="${luks_dev}" # otherwise hope...
setup_md_device "${LUKS_DEVICE}" setup_md_device "${luks_device}"
cryptsetup isLuks "${LUKS_DEVICE}" || { cryptsetup isLuks "${luks_device}" || {
bad_msg "${LUKS_DEVICE} does not contain a LUKS header" bad_msg "${luks_device} does not contain a LUKS header"
dev_error=1 dev_error=1
continue; continue;
} }
# Handle keys # Handle keys
if [ "${LUKS_TRIM}" = "yes" ]; then if [ "${luks_trim}" = "yes" ]; then
good_msg "Enabling TRIM support for ${LUKS_NAME}." good_msg "Enabling TRIM support for ${luks_dev_name}."
cryptsetup_opts="${cryptsetup_opts} --allow-discards" cryptsetup_opts="${cryptsetup_opts} --allow-discards"
fi fi
if [ -n "${LUKS_KEY}" ]; then if [ -n "${luks_key}" ]; then
local real_luks_keydev="${LUKS_KEYDEV}" local real_luks_keydev="${luks_keydev}"
if [ ! -e "${mntkey}${LUKS_KEY}" ]; then if [ ! -e "${mntkey}${luks_key}" ]; then
real_luks_keydev=$(find_real_device "${LUKS_KEYDEV}") real_luks_keydev=$(find_real_device "${luks_keydev}")
good_msg "Using key device ${real_luks_keydev}." good_msg "Using key device ${real_luks_keydev}."
if [ ! -b "${real_luks_keydev}" ]; then if [ ! -b "${real_luks_keydev}" ]; then
bad_msg "Insert device ${LUKS_KEYDEV} for ${LUKS_NAME}" bad_msg "Insert device ${luks_keydev} for ${luks_dev_name}"
bad_msg "You have 10 seconds..." bad_msg "You have 10 seconds..."
local count=10 local count=10
while [ ${count} -gt 0 ]; do while [ ${count} -gt 0 ]; do
count=$((count-1)) count=$((count-1))
sleep 1 sleep 1
real_luks_keydev=$(find_real_device "${LUKS_KEYDEV}") real_luks_keydev=$(find_real_device "${luks_keydev}")
[ ! -b "${real_luks_keydev}" ] || { [ ! -b "${real_luks_keydev}" ] || {
good_msg "Device ${real_luks_keydev} detected." good_msg "Device ${real_luks_keydev} detected."
break; break;
@ -142,14 +155,14 @@ _open_luks() {
done done
if [ ! -b "${real_luks_keydev}" ]; then if [ ! -b "${real_luks_keydev}" ]; then
eval CRYPT_${ltype}_KEY=${LUKS_KEY} eval CRYPT_${ltype}_KEY=${luks_key}
_bootstrap_key ${ltype} _bootstrap_key ${ltype}
eval LUKS_KEYDEV='"${CRYPT_'${ltype}'_KEYDEV}"' eval luks_keydev='"${CRYPT_'${ltype}'_KEYDEV}"'
real_luks_keydev=$(find_real_device "${LUKS_KEYDEV}") real_luks_keydev=$(find_real_device "${luks_keydev}")
if [ ! -b "${real_luks_keydev}" ]; then if [ ! -b "${real_luks_keydev}" ]; then
keydev_error=1 keydev_error=1
bad_msg "Removable device ${LUKS_KEYDEV} not found." bad_msg "Device ${luks_keydev} not found."
continue continue
fi fi
@ -172,19 +185,19 @@ _open_luks() {
good_msg "Removable device ${real_luks_keydev} mounted." good_msg "Removable device ${real_luks_keydev} mounted."
if [ ! -e "${mntkey}${LUKS_KEY}" ]; then if [ ! -e "${mntkey}${luks_key}" ]; then
umount -n "${mntkey}" umount -n "${mntkey}"
key_error=1 key_error=1
keydev_error=1 keydev_error=1
bad_msg "{LUKS_KEY} on ${real_luks_keydev} not found." bad_msg "{luks_key} on ${real_luks_keydev} not found."
continue continue
fi fi
fi fi
# At this point a candidate key exists # At this point a candidate key exists
# (either mounted before or not) # (either mounted before or not)
good_msg "${LUKS_KEY} on device ${real_luks_keydev} found" good_msg "${luks_key} on device ${real_luks_keydev} found"
if [ "$(echo ${LUKS_KEY} | grep -o '.gpg$')" = ".gpg" ] && \ if [ "$(echo ${luks_key} | grep -o '.gpg$')" = ".gpg" ] && \
[ -e /usr/bin/gpg ]; then [ -e /usr/bin/gpg ]; then
# TODO(lxnay): WTF is this? # TODO(lxnay): WTF is this?
@ -193,16 +206,16 @@ _open_luks() {
cryptsetup_opts="${cryptsetup_opts} -d -" cryptsetup_opts="${cryptsetup_opts} -d -"
gpg_cmd="/usr/bin/gpg --logger-file /dev/null" gpg_cmd="/usr/bin/gpg --logger-file /dev/null"
gpg_cmd="${gpg_cmd} --quiet --decrypt ${mntkey}${LUKS_KEY} | " gpg_cmd="${gpg_cmd} --quiet --decrypt ${mntkey}${luks_key} | "
else else
cryptsetup_opts="${cryptsetup_opts} -d ${mntkey}${LUKS_KEY}" cryptsetup_opts="${cryptsetup_opts} -d ${mntkey}${luks_key}"
fi fi
fi fi
# At this point, keyfile or not, we're ready! # At this point, keyfile or not, we're ready!
local cmd="${gpg_cmd}/sbin/cryptsetup" local cmd="${gpg_cmd}/sbin/cryptsetup"
cmd="${cmd} ${cryptsetup_opts} luksOpen ${LUKS_DEVICE} ${LUKS_NAME}" cmd="${cmd} ${cryptsetup_opts} open ${luks_device} ${luks_dev_name}"
_crypt_exec "${cmd}" _crypt_exec "${luks_device}" "${cmd}"
local ret="${?}" local ret="${?}"
# TODO(lxnay): WTF is this? # TODO(lxnay): WTF is this?
@ -212,7 +225,7 @@ _open_luks() {
if [ "${ret}" = "0" ]; then if [ "${ret}" = "0" ]; then
exit_st=0 exit_st=0
good_msg "LUKS device ${LUKS_DEVICE} opened" good_msg "LUKS device ${luks_device} opened"
# This is fine if the crypt device is a physical device # This is fine if the crypt device is a physical device
# like /dev/sdaX, however, if we have cryptsetup inside # like /dev/sdaX, however, if we have cryptsetup inside
@ -220,18 +233,27 @@ _open_luks() {
start_volumes # this should create /dev/mapper links start_volumes # this should create /dev/mapper links
if echo "${real_dev}" | grep -q "^/dev/mapper/"; then if echo "${real_dev}" | grep -q "^/dev/mapper/"; then
if [ ! -e "${real_dev}" ]; then if [ ! -e "${real_dev}" ]; then
good_msg "Creating symlink for ${LUKS_NAME} to ${real_dev}" # WARN: while for ltype=SWAP this may not be a problem,
ln -s "${LUKS_NAME}" "${real_dev}" || exit_st=1 # for ltype=ROOT this may render the system unbootable
# because lvm can get angry to see a symlink where it's
# not supposed to be or we may fail to create the proper
# link (due to the if above), however, reordering the
# cmdline entries may solve this.
good_msg "Creating symlink ${luks_dev_name} -> ${real_dev}"
ln -s "${luks_dev_name}" "${real_dev}" || exit_st=1
fi fi
fi fi
break break
fi fi
bad_msg "Failed to open LUKS device ${LUKS_DEVICE}" bad_msg "Failed to open LUKS device ${luks_device}"
dev_error=1 dev_error=1
key_error=1 key_error=1
keydev_error=1 keydev_error=1
done
done done
umount -l "${mntkey}" 2>/dev/null >/dev/null umount -l "${mntkey}" 2>/dev/null >/dev/null
@ -246,7 +268,7 @@ start_luks() {
[ -n "${CRYPT_ROOT_KEY}" ] && [ -z "${CRYPT_ROOT_KEYDEV}" ] \ [ -n "${CRYPT_ROOT_KEY}" ] && [ -z "${CRYPT_ROOT_KEYDEV}" ] \
&& sleep 6 && _bootstrap_key "ROOT" && sleep 6 && _bootstrap_key "ROOT"
if [ -n "${CRYPT_ROOT}" ]; then if [ -n "${CRYPT_ROOTS}" ]; then
if _open_luks "root"; then if _open_luks "root"; then
# force REAL_ROOT= to some value if not set # force REAL_ROOT= to some value if not set
# this is mainly for backward compatibility, # this is mainly for backward compatibility,
@ -259,9 +281,9 @@ start_luks() {
# TODO(lxnay): this sleep 6 thing is hurting my eyes sooooo much. # TODO(lxnay): this sleep 6 thing is hurting my eyes sooooo much.
# same for swap, but no need to sleep if root was unencrypted # same for swap, but no need to sleep if root was unencrypted
[ -n "${CRYPT_SWAP_KEY}" ] && [ -z "${CRYPT_SWAP_KEYDEV}" ] \ [ -n "${CRYPT_SWAP_KEY}" ] && [ -z "${CRYPT_SWAP_KEYDEV}" ] \
&& { [ -z "${CRYPT_ROOT}" ] && sleep 6; _bootstrap_key "SWAP"; } && { [ -z "${CRYPT_ROOTS}" ] && sleep 6; _bootstrap_key "SWAP"; }
if [ -n "${CRYPT_SWAP}" ]; then if [ -n "${CRYPT_SWAPS}" ]; then
if _open_luks "swap"; then if _open_luks "swap"; then
# force REAL_RESUME= to some value if not set # force REAL_RESUME= to some value if not set
[ -z "${REAL_RESUME}" ] && REAL_RESUME="/dev/mapper/swap" [ -z "${REAL_RESUME}" ] && REAL_RESUME="/dev/mapper/swap"

@ -360,12 +360,12 @@ livecd_mount() {
_cache_cd_contents _cache_cd_contents
# If encrypted, find key and mount, otherwise mount as usual # If encrypted, find key and mount, otherwise mount as usual
if [ -n "${CRYPT_ROOT}" ]; then if [ -n "${CRYPT_ROOTS}" ]; then
CRYPT_ROOT_KEY=$(head -n 1 "${CDROOT_PATH}/${CDROOT_MARKER}") CRYPT_ROOT_KEY=$(head -n 1 "${CDROOT_PATH}/${CDROOT_MARKER}")
CRYPT_ROOT="$(losetup -f)" CRYPT_ROOTS="$(losetup -f)" # support only one value for livecd
good_msg "You booted an encrypted livecd" good_msg "You booted an encrypted livecd"
losetup "${CRYPT_ROOT}" "${CDROOT_PATH}/${LOOPEXT}${LOOP}" losetup "${CRYPT_ROOTS}" "${CDROOT_PATH}/${LOOPEXT}${LOOP}"
test_success "Preparing loop filesystem" test_success "Preparing loop filesystem"
start_luks start_luks

@ -86,7 +86,7 @@ zfs_start_volumes() {
if [ "${pools}" = "${ZFS_POOL}" ]; then if [ "${pools}" = "${ZFS_POOL}" ]; then
good_msg "ZFS pool ${ZFS_POOL} already imported." good_msg "ZFS pool ${ZFS_POOL} already imported."
if [ -n "${CRYPT_ROOT}" ] || [ -n "${CRYPT_SWAP}" ]; then if [ -n "${CRYPT_ROOTS}" ] || [ -n "${CRYPT_SWAPS}" ]; then
good_msg "LUKS detected. Reimporting ${ZFS_POOL}" good_msg "LUKS detected. Reimporting ${ZFS_POOL}"
zpool export -f "${ZFS_POOL}" zpool export -f "${ZFS_POOL}"
zpool import -N ${ZPOOL_FORCE} "${ZFS_POOL}" zpool import -N ${ZPOOL_FORCE} "${ZFS_POOL}"

@ -195,11 +195,26 @@ for x in ${CMDLINE}; do
;; ;;
crypt_root=*) crypt_root=*)
CRYPT_ROOT=${x#*=} # kept for backward compatibility
CRYPT_ROOTS=${x#*=}
;; ;;
crypt_swap=*) crypt_swap=*)
CRYPT_SWAP=${x#*=} # kept for backward compatibility
CRYPT_SWAPS=${x#*=}
;; ;;
crypt_roots=*)
# The first entry will be the one that
# is going to be mapped to ${REAL_ROOT}.
# Multiple "roots" devices are needed
# in order to support multiple dmcrypts
# aggregated through software raid arrays.
CRYPT_ROOTS="${CRYPT_ROOTS} ${x#*=}"
;;
crypt_swaps=*)
CRYPT_SWAPS="${CRYPT_SWAPS} ${x#*=}"
;;
root_key=*) root_key=*)
CRYPT_ROOT_KEY=${x#*=} CRYPT_ROOT_KEY=${x#*=}
;; ;;

@ -398,10 +398,33 @@ which the ramdisk scripts would recognize.
*crypt_root*=<...>:: *crypt_root*=<...>::
This specifies the device encrypted by Luks, which contains the This specifies the device encrypted by Luks, which contains the
root filesystem to mount. root filesystem to mount. If you intend to use multiple devices
cooperating in the building of a single root device node (like
for instance in the case of dmcrypts -> software raid), please
use crypt_roots= instead.
*crypt_roots*=<...>::
This specifies the devices encrypted by Luks, which concur in
the availability of a device node used for the root filesystem.
For instance, you could have two separately encrypted partitions
on separate disks that are part of a software raid setup. You just
need to declare multiple crypt_roots= statements each one containing
one device path. Please keep in mind that the entries will be processed
in the order they are declared. If you use crypt_roots=, remember to
remove any reference to crypt_root=. They all share the same root_key=
and root_keydev=.
*crypt_swap*=<...>:: *crypt_swap*=<...>::
This specifies the swap device encrypted by Luks. This specifies the swap device encrypted by Luks. If you have more than
one encrypted swap, please use crypt_swaps= instead.
*crypt_swaps*=<...>::
This specifies the swap devices encrypted by Luks. You just
need to declare multiple crypt_swaps= statements each one containing
one device path. Please keep in mind that the entries will be processed
in the order they are declared. If you use crypt_swaps=, remember to
remove any reference to crypt_swap=. They all share the same root_key=
and root_keydev=.
*root_key*=<...>:: *root_key*=<...>::
In case your root is encrypted with a key, you can use a device In case your root is encrypted with a key, you can use a device

Loading…
Cancel
Save