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,187 +56,204 @@ _open_luks() {
return 1 return 1
fi fi
local exit_st= local real_dev=
if [ "${ltype}" = "ROOT" ]; then
while true; do real_dev="${REAL_ROOT}"
local gpg_cmd="" elif [ "${ltype}" = "SWAP" ]; then
exit_st=1 real_dev="${REAL_RESUME}"
fi
# do not force the link to /dev/mapper/root
# but rather use the value from root=, which is
# in ${REAL_ROOT}
local luks_dev_name=$(basename "${LUKS_DEVICE}")
local real_dev=
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 we use LVM + cryptsetup, we may have collisions between
# the two inside /dev/mapper. So, make up a way to avoid them.
LUKS_NAME="${LUKS_NAME}_${luks_dev_name}-$(basename ${real_dev})"
fi
# if crypt_silent=1 and some error occurs, bail out. local exit_st= luks_device=
local any_error= for luks_device in ${luks_devices}; do
[ "${dev_error}" = "1" ] && any_error=1
[ "${key_error}" = "1" ] && any_error=1
[ "${keydev_error}" = "1" ] && any_error=1
if [ "${CRYPT_SILENT}" = "1" ] && [ -n "${any_error}" ]; then
bad_msg "Failed to setup the LUKS device"
exit_st=1
break
fi
if [ "${dev_error}" = "1" ]; then good_msg "Working on device ${luks_device}..."
prompt_user "LUKS_DEVICE" "${LUKS_NAME}"
dev_error=0
continue
fi
if [ "${key_error}" = "1" ]; then while true; do
prompt_user "LUKS_KEY" "${LUKS_NAME} key"
key_error=0
continue
fi
if [ "${keydev_error}" = "1" ]; then local gpg_cmd=""
prompt_user "LUKS_KEYDEV" "${LUKS_NAME} key device" exit_st=1
keydev_error=0
continue
fi
local luks_dev=$(find_real_device "${LUKS_DEVICE}") # do not force the link to /dev/mapper/root
[ -n "${luks_dev}" ] && LUKS_DEVICE="${luks_dev}" # otherwise hope... # but rather use the value from root=, which is
# in ${REAL_ROOT}
local luks_dev_name=$(basename "${luks_device}")
local luks_name_prefix=
setup_md_device "${LUKS_DEVICE}" if echo "${real_dev}" | grep -q "^/dev/mapper/"; then
cryptsetup isLuks "${LUKS_DEVICE}" || { local real_dev_bn=$(basename "${real_dev}")
bad_msg "${LUKS_DEVICE} does not contain a LUKS header" # If we use LVM + cryptsetup, we may have collisions between
dev_error=1 # the two inside /dev/mapper. So, make up a way to avoid them.
continue; luks_dev_name="${luks_name}_${luks_dev_name}-${real_dev_bn}"
} fi
# Handle keys # if crypt_silent=1 and some error occurs, bail out.
if [ "${LUKS_TRIM}" = "yes" ]; then local any_error=
good_msg "Enabling TRIM support for ${LUKS_NAME}." [ "${dev_error}" = "1" ] && any_error=1
cryptsetup_opts="${cryptsetup_opts} --allow-discards" [ "${key_error}" = "1" ] && any_error=1
fi [ "${keydev_error}" = "1" ] && any_error=1
if [ "${CRYPT_SILENT}" = "1" ] && [ -n "${any_error}" ]; then
bad_msg "Failed to setup the LUKS device"
exit_st=1
break
fi
if [ "${dev_error}" = "1" ]; then
prompt_user "luks_device" "${luks_dev_name}"
dev_error=0
continue
fi
if [ -n "${LUKS_KEY}" ]; then if [ "${key_error}" = "1" ]; then
local real_luks_keydev="${LUKS_KEYDEV}" prompt_user "luks_key" "${luks_dev_name} key"
key_error=0
continue
fi
if [ ! -e "${mntkey}${LUKS_KEY}" ]; then if [ "${keydev_error}" = "1" ]; then
real_luks_keydev=$(find_real_device "${LUKS_KEYDEV}") prompt_user "luks_keydev" "${luks_dev_name} key device"
good_msg "Using key device ${real_luks_keydev}." keydev_error=0
continue
fi
if [ ! -b "${real_luks_keydev}" ]; then local luks_dev=$(find_real_device "${luks_device}")
bad_msg "Insert device ${LUKS_KEYDEV} for ${LUKS_NAME}" [ -n "${luks_dev}" ] && \
bad_msg "You have 10 seconds..." luks_device="${luks_dev}" # otherwise hope...
local count=10
while [ ${count} -gt 0 ]; do setup_md_device "${luks_device}"
count=$((count-1)) cryptsetup isLuks "${luks_device}" || {
sleep 1 bad_msg "${luks_device} does not contain a LUKS header"
dev_error=1
continue;
}
# Handle keys
if [ "${luks_trim}" = "yes" ]; then
good_msg "Enabling TRIM support for ${luks_dev_name}."
cryptsetup_opts="${cryptsetup_opts} --allow-discards"
fi
real_luks_keydev=$(find_real_device "${LUKS_KEYDEV}") if [ -n "${luks_key}" ]; then
[ ! -b "${real_luks_keydev}" ] || { local real_luks_keydev="${luks_keydev}"
good_msg "Device ${real_luks_keydev} detected."
break; if [ ! -e "${mntkey}${luks_key}" ]; then
} real_luks_keydev=$(find_real_device "${luks_keydev}")
done good_msg "Using key device ${real_luks_keydev}."
if [ ! -b "${real_luks_keydev}" ]; then if [ ! -b "${real_luks_keydev}" ]; then
eval CRYPT_${ltype}_KEY=${LUKS_KEY} bad_msg "Insert device ${luks_keydev} for ${luks_dev_name}"
_bootstrap_key ${ltype} bad_msg "You have 10 seconds..."
eval LUKS_KEYDEV='"${CRYPT_'${ltype}'_KEYDEV}"' local count=10
while [ ${count} -gt 0 ]; do
count=$((count-1))
sleep 1
real_luks_keydev=$(find_real_device "${luks_keydev}")
[ ! -b "${real_luks_keydev}" ] || {
good_msg "Device ${real_luks_keydev} detected."
break;
}
done
real_luks_keydev=$(find_real_device "${LUKS_KEYDEV}")
if [ ! -b "${real_luks_keydev}" ]; then if [ ! -b "${real_luks_keydev}" ]; then
keydev_error=1 eval CRYPT_${ltype}_KEY=${luks_key}
bad_msg "Removable device ${LUKS_KEYDEV} not found." _bootstrap_key ${ltype}
eval luks_keydev='"${CRYPT_'${ltype}'_KEYDEV}"'
real_luks_keydev=$(find_real_device "${luks_keydev}")
if [ ! -b "${real_luks_keydev}" ]; then
keydev_error=1
bad_msg "Device ${luks_keydev} not found."
continue
fi
# continue otherwise will mount keydev which is
# mounted by bootstrap
continue continue
fi fi
fi
# continue otherwise will mount keydev which is # At this point a device was recognized, now let's see
# mounted by bootstrap # if the key is there
mkdir -p "${mntkey}" # ignore
mount -n -o ro "${real_luks_keydev}" \
"${mntkey}" || {
keydev_error=1
bad_msg "Mounting of device ${real_luks_keydev} failed."
continue;
}
good_msg "Removable device ${real_luks_keydev} mounted."
if [ ! -e "${mntkey}${luks_key}" ]; then
umount -n "${mntkey}"
key_error=1
keydev_error=1
bad_msg "{luks_key} on ${real_luks_keydev} not found."
continue continue
fi fi
fi fi
# At this point a device was recognized, now let's see # At this point a candidate key exists
# if the key is there # (either mounted before or not)
mkdir -p "${mntkey}" # ignore good_msg "${luks_key} on device ${real_luks_keydev} found"
if [ "$(echo ${luks_key} | grep -o '.gpg$')" = ".gpg" ] && \
mount -n -o ro "${real_luks_keydev}" \ [ -e /usr/bin/gpg ]; then
"${mntkey}" || {
keydev_error=1 # TODO(lxnay): WTF is this?
bad_msg "Mounting of device ${real_luks_keydev} failed." [ -e /dev/tty ] && mv /dev/tty /dev/tty.org
continue; mknod /dev/tty c 5 1
}
cryptsetup_opts="${cryptsetup_opts} -d -"
good_msg "Removable device ${real_luks_keydev} mounted." gpg_cmd="/usr/bin/gpg --logger-file /dev/null"
gpg_cmd="${gpg_cmd} --quiet --decrypt ${mntkey}${luks_key} | "
if [ ! -e "${mntkey}${LUKS_KEY}" ]; then else
umount -n "${mntkey}" cryptsetup_opts="${cryptsetup_opts} -d ${mntkey}${luks_key}"
key_error=1
keydev_error=1
bad_msg "{LUKS_KEY} on ${real_luks_keydev} not found."
continue
fi fi
fi fi
# At this point a candidate key exists # At this point, keyfile or not, we're ready!
# (either mounted before or not) local cmd="${gpg_cmd}/sbin/cryptsetup"
good_msg "${LUKS_KEY} on device ${real_luks_keydev} found" cmd="${cmd} ${cryptsetup_opts} open ${luks_device} ${luks_dev_name}"
if [ "$(echo ${LUKS_KEY} | grep -o '.gpg$')" = ".gpg" ] && \ _crypt_exec "${luks_device}" "${cmd}"
[ -e /usr/bin/gpg ]; then local ret="${?}"
# TODO(lxnay): WTF is this? # TODO(lxnay): WTF is this?
[ -e /dev/tty ] && mv /dev/tty /dev/tty.org [ -e /dev/tty.org ] \
mknod /dev/tty c 5 1 && rm -f /dev/tty \
&& mv /dev/tty.org /dev/tty
cryptsetup_opts="${cryptsetup_opts} -d -"
gpg_cmd="/usr/bin/gpg --logger-file /dev/null" if [ "${ret}" = "0" ]; then
gpg_cmd="${gpg_cmd} --quiet --decrypt ${mntkey}${LUKS_KEY} | " exit_st=0
else good_msg "LUKS device ${luks_device} opened"
cryptsetup_opts="${cryptsetup_opts} -d ${mntkey}${LUKS_KEY}"
fi # This is fine if the crypt device is a physical device
fi # like /dev/sdaX, however, if we have cryptsetup inside
# LVM, we must tweak REAL_ROOT if there is no device node.
# At this point, keyfile or not, we're ready! start_volumes # this should create /dev/mapper links
local cmd="${gpg_cmd}/sbin/cryptsetup" if echo "${real_dev}" | grep -q "^/dev/mapper/"; then
cmd="${cmd} ${cryptsetup_opts} luksOpen ${LUKS_DEVICE} ${LUKS_NAME}" if [ ! -e "${real_dev}" ]; then
_crypt_exec "${cmd}" # WARN: while for ltype=SWAP this may not be a problem,
local ret="${?}" # for ltype=ROOT this may render the system unbootable
# because lvm can get angry to see a symlink where it's
# TODO(lxnay): WTF is this? # not supposed to be or we may fail to create the proper
[ -e /dev/tty.org ] \ # link (due to the if above), however, reordering the
&& rm -f /dev/tty \ # cmdline entries may solve this.
&& mv /dev/tty.org /dev/tty good_msg "Creating symlink ${luks_dev_name} -> ${real_dev}"
ln -s "${luks_dev_name}" "${real_dev}" || exit_st=1
if [ "${ret}" = "0" ]; then fi
exit_st=0
good_msg "LUKS device ${LUKS_DEVICE} opened"
# This is fine if the crypt device is a physical device
# like /dev/sdaX, however, if we have cryptsetup inside
# LVM, we must tweak REAL_ROOT if there is no device node.
start_volumes # this should create /dev/mapper links
if echo "${real_dev}" | grep -q "^/dev/mapper/"; then
if [ ! -e "${real_dev}" ]; then
good_msg "Creating symlink for ${LUKS_NAME} to ${real_dev}"
ln -s "${LUKS_NAME}" "${real_dev}" || exit_st=1
fi fi
break
fi fi
break bad_msg "Failed to open LUKS device ${luks_device}"
fi dev_error=1
key_error=1
keydev_error=1
done
bad_msg "Failed to open LUKS device ${LUKS_DEVICE}"
dev_error=1
key_error=1
keydev_error=1
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