Reputation: 1521
With this post I am sharing a solution with the community.
I have a Gentoo system installed on a ZFS pool consisting of multiple encrypted devices. It is normally decrypted at boot as described in this post. In the file /etc/default/grub
I add to the kernel line:
GRUB_CMDLINE_LINUX="dozfs crypt_roots=UUID=aaaaaaaa crypt_roots=UUID=bbbbbbbb ..."
where aaaaaaaa
and bbbbbbbb
stand for the UUID-s of the encrypted volumes as listed in /dev/disk/by-uuid
, but there is an inconvenience: A password must be entered once for each volume. I use full disk encryption and 8 encrypted volumes, which would require 9 password entries on each boot, even though I use the same password for all the volumes. Quite a hassle!
Is it possible to decrypt all volumes with a single password entry?
My initramfs is created via sys-kernel/genkernel-next-69
.
Upvotes: 1
Views: 1735
Reputation: 1521
I have come up with a patch for genkernel that allows for decrypting multiple volumes with a single password (save the file as /tmp/genkernel_multicrypt.patch
):
diff --git a/defaults/initrd.d/00-crypt-multicrypt.sh b/defaults/initrd.d/00-crypt-multicrypt.sh
new file mode 100755
index 0000000..848f06a
--- /dev/null
+++ b/defaults/initrd.d/00-crypt-multicrypt.sh
@@ -0,0 +1,88 @@
+#!/bin/sh
+
+MULTICRYPT_MAX_TRIES=4
+
+multicrypt_all_volumes_mapped()
+{
+ local k=0
+
+ while true; do
+ eval local voltype='"${MULTICRYPT_type'${k}'}"'
+ k=$(( k+1 ))
+
+ [ -n "$voltype" ] || return 0
+
+ local m=0
+ while true; do
+ eval local vol='"${MULTICRYPT_'${voltype}${m}'}"'
+ [ -n "$vol" ] || break
+ [ -e "/dev/mapper/${voltype}${m}" ] || return 1
+ m=$(( m+1 ))
+ done
+ done
+
+ return 0
+}
+
+multicrypt_open_volumes()
+{
+ local pass="$1"; shift
+
+ local k=0
+
+ while true; do
+ eval local voltype='"${MULTICRYPT_type'${k}'}"'
+ k=$(( k+1 ))
+
+ [ -n "$voltype" ] || return 0
+
+ local m=0
+ while true; do
+ local volname="${voltype}${m}"
+
+ eval local vol='"${MULTICRYPT_'${volname}'}"'
+ [ -n "$vol" ] || break
+
+ echo Decrypting vol ${volname} ${vol} ...
+ echo -n "$pass" | ${CRYPTSETUP_BIN} luksOpen /dev/disk/by-uuid/${vol} "${volname}" -d - || return 1
+
+ m=$(( m+1 ))
+ done
+ done
+}
+
+multicrypt_execute() {
+
+ [ -z "${MULTICRYPT_type0}" ] && return 1 # multicrypt not requested
+
+ local root_or_swap=
+ if [ -n "${CRYPT_ROOTS}" ] || [ -n "${CRYPT_SWAPS}" ]; then
+ root_or_swap=1
+ fi
+
+ if ! multicrypt_all_volumes_mapped; then
+ echo "Opening multiple encrypted partitions..."
+ fi
+
+ local try=0
+ while ! multicrypt_all_volumes_mapped && [ "$try" -lt "$MULTICRYPT_MAX_TRIES" ]; do
+ try=$(( try+1 ))
+
+ echo -n "Password (try $try of $MULTICRYPT_MAX_TRIES): "
+ read -s pass
+ echo ""
+
+ multicrypt_open_volumes "$pass"
+
+ if [ $? -ne 0 ]; then
+ echo "ERROR: Decrypting one or more volumes failed."
+ fi
+ done
+
+ if [ -n "${root_or_swap}" ]; then
+ # We postponed the initialization of raid devices
+ # in order to avoid to assemble possibly degraded
+ # arrays.
+ start_volumes
+ fi
+}
diff --git a/defaults/initrd.d/00-crypt.sh b/defaults/initrd.d/00-crypt.sh
index 0e7c863..0515446 100755
--- a/defaults/initrd.d/00-crypt.sh
+++ b/defaults/initrd.d/00-crypt.sh
@@ -4,6 +4,7 @@
. /etc/initrd.d/00-devmgr.sh
. /etc/initrd.d/00-splash.sh
. /etc/initrd.d/00-fsdev.sh
+. /etc/initrd.d/00-crypt-multicrypt.sh
CRYPTSETUP_BIN="/sbin/cryptsetup"
KEY_MNT="/mnt/key"
@@ -285,6 +286,8 @@ _open_luks() {
start_luks() {
+ multicrypt_execute && return 0
+
local root_or_swap=
if [ -n "${CRYPT_ROOTS}" ] || [ -n "${CRYPT_SWAPS}" ]; then
root_or_swap=1
The patch must be applied to genkernel as root in directory /usr/share/genkernel
:
su
# Make a backup of genkernel if something goes wrong.
cp -r /usr/share/genkernel /root/genkernel-backup
cd /usr/share/genkernel
patch -p1 < /tmp/genkernel_multicrypt.patch
If everything goes right, the patch will be applied and you will not see any error messages.
Usage
First make sure your disks can be decrypted using the same password!!!
The patch modifies genkernel's initial ramdisks to support the following additional input parameters:
MULTICRYPT_type0=boot
MULTICRYPT_type1=root
MULTICRYPT_type2=tank
MULTICRYPT_boot0=aaaaaaaaa
MULTICRYPT_root0=bbbbbbbbb
MULTICRYPT_root1=ccccccccc
MULTICRYPT_tank0=ddddddddd
MULTICRYPT_tank1=eeeeeeeee
...
In the above example you can freely choose how many types you want to have, I used 3: boot
, root
, tank
. For each type you can have one or more volume UUIDs as shown above.
Make sure the UUIDs are present in /dev/disk/by-uuid
.
Make sure the UUIDs are exact! Typos can lead to an unbootable system!
The devices will be added to the device mapper as boot0
, root0
, root1
, etc.
Now, the kernel line must be modified as follows (simply append all parameters mentioned above to the kernel line along with your regular ones, for example dolvm
, dozfs
, etc.).
Do not delete your original kernel line yet, simply comment it out in case you want to revert.
GRUB_CMDLINE_LINUX="MULTICRYPT_type0=boot MULTICRYPT_type1=root MULTICRYPT_type2=tank MULTICRYPT_boot0=aaaaaaaaa MULTICRYPT_root0=bbbbbbbbb MULTICRYPT_root1=ccccccccc MULTICRYPT_tank0=ddddddddd MULTICRYPT_tank1=eeeeeeeee ..."
# As per standard grub configuration, we need to load the necessary modules and enable encryption:
GRUB_PRELOAD_MODULES="part_gpt search_fs_uuid cryptodisk luks linux" # zfs lvm etc. if necessary
GRUB_ENABLE_CRYPTODISK="y"
When this chore is done, we can call genkernel do generate the new initramfs. Make sure you make a backup copy of your old one to enable easy recovery if something goes wrong. Compiling kernel and modules is not necessary.
Also, write down your original kernel line on a piece of paper, so that you can type it in Grub in case the system can't boot properly.
su
cp /boot/<YOUR-INITRAMFS> /boot/<YOUR-INITRAMFS>--copy
# This uses 8 threads for compilation, adjust accordingly.
# Add "--zfs" if your root is on ZFS.
genkernel --makeopts=-j8 --udev --luks --busybox --bootloader=grub2 --menuconfig --oldconfig --install initramfs
Your shiny new initramfs is installed in /boot
. Now for the final touch - GRUB's configuration file must be generated to activate the new parameters:
grub-mkconfig -o /boot/grub/grub.cfg
At the time of writing grub-mkconfig generates an incorrect file that needs to be patched manually if your root is on ZFS. In the file /boot/grub/grub.cfg
my root dataset is incorrectly specified as /ROOT/gentoo
instead of zroot/ROOT/gentoo
so I need to insert zroot
in multiple locations. This should not be necessary once that particular bug is fixed.
Done! After rebooting the following prompt will be shown:
Opening multiple encrypted partitions...
Password (try 1 of 4):
Enter your password and (fingers crossed) all volumes should decrypt.
Troubleshooting
If your system becomes unbootable, you can use your old initramfs that we saved. When the Grub menu is shown, hit E
and retype your original kernel line, appending "--saved" to your initramfs filename. This should boot your system as it was before.
If you are not satisfied with the patch, restore your original genkernel from /root/genkernel-backup
to /usr/share/genkernel
, then restore your original kernel line in /etc/default/grub
and call genkernel and grub-mkconfig again:
genkernel --makeopts=-j8 --udev --luks --busybox --bootloader=grub2 --menuconfig --oldconfig --install initramfs
grub-mkconfig -o /boot/grub/grub.cfg
Caveat
For I use a full disk encryption, I still need to enter my password twice at boot. First, to decrypt /boot
and load the kernel, then a second time to decrypt all other volumes. I am not aware of any solution for this.
State 17.02.2022
Upvotes: 1