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() {
local luks_dev="${1}"
local cmd="${2}"
# TODO(lxnay): this fugly crypt_silent should really go away
if [ "${CRYPT_SILENT}" = "1" ]; then
eval ${1} >/dev/null 2>/dev/null
eval ${cmd} >/dev/null 2>/dev/null
else
ask_for_password --ply-tries 5 \
--ply-cmd "${1}" \
--ply-prompt "Encryption password (${LUKS_DEVICE}): " \
--ply-cmd "${cmd}" \
--ply-prompt "Encryption password (${luks_dev}): " \
--tty-tries 5 \
--tty-cmd "${1}" || return 1
--tty-cmd "${cmd}" || return 1
return 0
fi
}
@ -30,19 +32,22 @@ _crypt_exec() {
_open_luks() {
case ${1} in
root)
local ltypes=ROOTS
local ltype=ROOT
;;
swap)
local ltypes=SWAPS
local ltype=SWAP
;;
esac
eval local LUKS_DEVICE='"${CRYPT_'${ltype}'}"'
eval local LUKS_KEY='"${CRYPT_'${ltype}'_KEY}"'
eval local LUKS_KEYDEV='"${CRYPT_'${ltype}'_KEYDEV}"'
eval local LUKS_TRIM='"${CRYPT_'${ltype}'_TRIM}"'
eval local luks_devices='"${CRYPT_'${ltypes}'}"'
eval local luks_key='"${CRYPT_'${ltype}'_KEY}"'
eval local luks_keydev='"${CRYPT_'${ltype}'_KEYDEV}"'
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 mntkey="/mnt/key/" cryptsetup_opts=""
@ -51,187 +56,204 @@ _open_luks() {
return 1
fi
local exit_st=
while true; do
local gpg_cmd=""
exit_st=1
# 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
local real_dev=
if [ "${ltype}" = "ROOT" ]; then
real_dev="${REAL_ROOT}"
elif [ "${ltype}" = "SWAP" ]; then
real_dev="${REAL_RESUME}"
fi
# if crypt_silent=1 and some error occurs, bail out.
local any_error=
[ "${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
local exit_st= luks_device=
for luks_device in ${luks_devices}; do
if [ "${dev_error}" = "1" ]; then
prompt_user "LUKS_DEVICE" "${LUKS_NAME}"
dev_error=0
continue
fi
good_msg "Working on device ${luks_device}..."
if [ "${key_error}" = "1" ]; then
prompt_user "LUKS_KEY" "${LUKS_NAME} key"
key_error=0
continue
fi
while true; do
if [ "${keydev_error}" = "1" ]; then
prompt_user "LUKS_KEYDEV" "${LUKS_NAME} key device"
keydev_error=0
continue
fi
local gpg_cmd=""
exit_st=1
local luks_dev=$(find_real_device "${LUKS_DEVICE}")
[ -n "${luks_dev}" ] && LUKS_DEVICE="${luks_dev}" # otherwise hope...
# 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 luks_name_prefix=
setup_md_device "${LUKS_DEVICE}"
cryptsetup isLuks "${LUKS_DEVICE}" || {
bad_msg "${LUKS_DEVICE} does not contain a LUKS header"
dev_error=1
continue;
}
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
# the two inside /dev/mapper. So, make up a way to avoid them.
luks_dev_name="${luks_name}_${luks_dev_name}-${real_dev_bn}"
fi
# Handle keys
if [ "${LUKS_TRIM}" = "yes" ]; then
good_msg "Enabling TRIM support for ${LUKS_NAME}."
cryptsetup_opts="${cryptsetup_opts} --allow-discards"
fi
# if crypt_silent=1 and some error occurs, bail out.
local any_error=
[ "${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
prompt_user "luks_device" "${luks_dev_name}"
dev_error=0
continue
fi
if [ -n "${LUKS_KEY}" ]; then
local real_luks_keydev="${LUKS_KEYDEV}"
if [ "${key_error}" = "1" ]; then
prompt_user "luks_key" "${luks_dev_name} key"
key_error=0
continue
fi
if [ ! -e "${mntkey}${LUKS_KEY}" ]; then
real_luks_keydev=$(find_real_device "${LUKS_KEYDEV}")
good_msg "Using key device ${real_luks_keydev}."
if [ "${keydev_error}" = "1" ]; then
prompt_user "luks_keydev" "${luks_dev_name} key device"
keydev_error=0
continue
fi
if [ ! -b "${real_luks_keydev}" ]; then
bad_msg "Insert device ${LUKS_KEYDEV} for ${LUKS_NAME}"
bad_msg "You have 10 seconds..."
local count=10
while [ ${count} -gt 0 ]; do
count=$((count-1))
sleep 1
local luks_dev=$(find_real_device "${luks_device}")
[ -n "${luks_dev}" ] && \
luks_device="${luks_dev}" # otherwise hope...
setup_md_device "${luks_device}"
cryptsetup isLuks "${luks_device}" || {
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}")
[ ! -b "${real_luks_keydev}" ] || {
good_msg "Device ${real_luks_keydev} detected."
break;
}
done
if [ -n "${luks_key}" ]; then
local real_luks_keydev="${luks_keydev}"
if [ ! -e "${mntkey}${luks_key}" ]; then
real_luks_keydev=$(find_real_device "${luks_keydev}")
good_msg "Using key device ${real_luks_keydev}."
if [ ! -b "${real_luks_keydev}" ]; then
eval CRYPT_${ltype}_KEY=${LUKS_KEY}
_bootstrap_key ${ltype}
eval LUKS_KEYDEV='"${CRYPT_'${ltype}'_KEYDEV}"'
bad_msg "Insert device ${luks_keydev} for ${luks_dev_name}"
bad_msg "You have 10 seconds..."
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
keydev_error=1
bad_msg "Removable device ${LUKS_KEYDEV} not found."
eval CRYPT_${ltype}_KEY=${luks_key}
_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
fi
fi
# continue otherwise will mount keydev which is
# mounted by bootstrap
# At this point a device was recognized, now let's see
# 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
fi
fi
# At this point a device was recognized, now let's see
# 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
# At this point a candidate key exists
# (either mounted before or not)
good_msg "${luks_key} on device ${real_luks_keydev} found"
if [ "$(echo ${luks_key} | grep -o '.gpg$')" = ".gpg" ] && \
[ -e /usr/bin/gpg ]; then
# TODO(lxnay): WTF is this?
[ -e /dev/tty ] && mv /dev/tty /dev/tty.org
mknod /dev/tty c 5 1
cryptsetup_opts="${cryptsetup_opts} -d -"
gpg_cmd="/usr/bin/gpg --logger-file /dev/null"
gpg_cmd="${gpg_cmd} --quiet --decrypt ${mntkey}${luks_key} | "
else
cryptsetup_opts="${cryptsetup_opts} -d ${mntkey}${luks_key}"
fi
fi
# At this point a candidate key exists
# (either mounted before or not)
good_msg "${LUKS_KEY} on device ${real_luks_keydev} found"
if [ "$(echo ${LUKS_KEY} | grep -o '.gpg$')" = ".gpg" ] && \
[ -e /usr/bin/gpg ]; then
# TODO(lxnay): WTF is this?
[ -e /dev/tty ] && mv /dev/tty /dev/tty.org
mknod /dev/tty c 5 1
cryptsetup_opts="${cryptsetup_opts} -d -"
gpg_cmd="/usr/bin/gpg --logger-file /dev/null"
gpg_cmd="${gpg_cmd} --quiet --decrypt ${mntkey}${LUKS_KEY} | "
else
cryptsetup_opts="${cryptsetup_opts} -d ${mntkey}${LUKS_KEY}"
fi
fi
# At this point, keyfile or not, we're ready!
local cmd="${gpg_cmd}/sbin/cryptsetup"
cmd="${cmd} ${cryptsetup_opts} luksOpen ${LUKS_DEVICE} ${LUKS_NAME}"
_crypt_exec "${cmd}"
local ret="${?}"
# TODO(lxnay): WTF is this?
[ -e /dev/tty.org ] \
&& rm -f /dev/tty \
&& mv /dev/tty.org /dev/tty
if [ "${ret}" = "0" ]; then
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
# At this point, keyfile or not, we're ready!
local cmd="${gpg_cmd}/sbin/cryptsetup"
cmd="${cmd} ${cryptsetup_opts} open ${luks_device} ${luks_dev_name}"
_crypt_exec "${luks_device}" "${cmd}"
local ret="${?}"
# TODO(lxnay): WTF is this?
[ -e /dev/tty.org ] \
&& rm -f /dev/tty \
&& mv /dev/tty.org /dev/tty
if [ "${ret}" = "0" ]; then
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
# WARN: while for ltype=SWAP this may not be a problem,
# 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
break
fi
break
fi
bad_msg "Failed to open LUKS device ${luks_device}"
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
umount -l "${mntkey}" 2>/dev/null >/dev/null
@ -246,7 +268,7 @@ start_luks() {
[ -n "${CRYPT_ROOT_KEY}" ] && [ -z "${CRYPT_ROOT_KEYDEV}" ] \
&& sleep 6 && _bootstrap_key "ROOT"
if [ -n "${CRYPT_ROOT}" ]; then
if [ -n "${CRYPT_ROOTS}" ]; then
if _open_luks "root"; then
# force REAL_ROOT= to some value if not set
# this is mainly for backward compatibility,
@ -259,9 +281,9 @@ start_luks() {
# TODO(lxnay): this sleep 6 thing is hurting my eyes sooooo much.
# same for swap, but no need to sleep if root was unencrypted
[ -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
# force REAL_RESUME= to some value if not set
[ -z "${REAL_RESUME}" ] && REAL_RESUME="/dev/mapper/swap"

@ -360,12 +360,12 @@ livecd_mount() {
_cache_cd_contents
# 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="$(losetup -f)"
CRYPT_ROOTS="$(losetup -f)" # support only one value for 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"
start_luks

@ -86,7 +86,7 @@ zfs_start_volumes() {
if [ "${pools}" = "${ZFS_POOL}" ]; then
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}"
zpool export -f "${ZFS_POOL}"
zpool import -N ${ZPOOL_FORCE} "${ZFS_POOL}"

@ -195,11 +195,26 @@ for x in ${CMDLINE}; do
;;
crypt_root=*)
CRYPT_ROOT=${x#*=}
# kept for backward compatibility
CRYPT_ROOTS=${x#*=}
;;
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=*)
CRYPT_ROOT_KEY=${x#*=}
;;

@ -398,10 +398,33 @@ which the ramdisk scripts would recognize.
*crypt_root*=<...>::
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*=<...>::
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*=<...>::
In case your root is encrypted with a key, you can use a device

Loading…
Cancel
Save