mirror of
https://github.com/linuxboot/heads.git
synced 2024-12-18 20:47:55 +00:00
initrd/bin/root-hashes-gui.sh: Qubes support, faster hash creation
Don't spew the root hashes to the console when creating the hash file. This speeds up hash creation significantly. A basic Qubes install on a cheap (slow) SATA SSD reduced from about 1.5 minutes to just under 1 minute, and a PureOS install on a fast NVMe disk reduced from 2.5 minutes to 1 minute. Support opening LVM volume groups to find the root disk. If an LVM PV is found, its group is opened and the 'root' volume is used. There is no way to set the volume name in this iteration; this is the default name used by Qubes and probably common to many LVM OS installations. LUKS and LVM can be mixed. Tested LUKS (PureOS) and LUKS+LVM (Qubes). Always cd to "$ROOT_MOUNT" in a subshell, improves robustness of scripts (previously some functions only worked if they were called after another function had cd'd to "$ROOT_MOUNT"). Signed-off-by: Jonathon Hall <jonathon.hall@puri.sm>
This commit is contained in:
parent
70d249ae46
commit
80b57eb60d
@ -32,7 +32,9 @@ update_root_checksums() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "+++ Calculating hashes for all files in $CONFIG_ROOT_DIRLIST_PRETTY "
|
echo "+++ Calculating hashes for all files in $CONFIG_ROOT_DIRLIST_PRETTY "
|
||||||
cd $ROOT_MOUNT && find ${CONFIG_ROOT_DIRLIST} -type f ! -name '*kexec*' -print0 | xargs -0 sha256sum | tee ${HASH_FILE}
|
# Intentional wordsplit
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
(cd "$ROOT_MOUNT" && find ${CONFIG_ROOT_DIRLIST} -type f ! -name '*kexec*' -print0 | xargs -0 sha256sum) >"${HASH_FILE}"
|
||||||
|
|
||||||
# switch back to ro mode
|
# switch back to ro mode
|
||||||
mount -o ro,remount /boot
|
mount -o ro,remount /boot
|
||||||
@ -86,7 +88,7 @@ check_root_checksums() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "+++ Checking for new files in $CONFIG_ROOT_DIRLIST_PRETTY "
|
echo "+++ Checking for new files in $CONFIG_ROOT_DIRLIST_PRETTY "
|
||||||
find ${CONFIG_ROOT_DIRLIST} -type f ! -name '*kexec*' | sort > /tmp/new_file_list
|
(cd "$ROOT_MOUNT" && find ${CONFIG_ROOT_DIRLIST} -type f ! -name '*kexec*') | sort > /tmp/new_file_list
|
||||||
cut -d' ' -f3- ${HASH_FILE} | sort | diff -U0 - /tmp/new_file_list > /tmp/new_file_diff || new_files_found=y
|
cut -d' ' -f3- ${HASH_FILE} | sort | diff -U0 - /tmp/new_file_list > /tmp/new_file_diff || new_files_found=y
|
||||||
if [ "$new_files_found" == "y" ]; then
|
if [ "$new_files_found" == "y" ]; then
|
||||||
grep -E -v '^[+-]{3}|[@]{2} ' /tmp/new_file_diff > /tmp/new_file_diff2 # strip any output that's not a file
|
grep -E -v '^[+-]{3}|[@]{2} ' /tmp/new_file_diff > /tmp/new_file_diff2 # strip any output that's not a file
|
||||||
@ -102,7 +104,7 @@ check_root_checksums() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
echo "+++ Checking hashes for all files in $CONFIG_ROOT_DIRLIST_PRETTY (this might take a while) "
|
echo "+++ Checking hashes for all files in $CONFIG_ROOT_DIRLIST_PRETTY (this might take a while) "
|
||||||
if cd $ROOT_MOUNT && sha256sum -c ${HASH_FILE} > /tmp/hash_output 2>/dev/null; then
|
if (cd $ROOT_MOUNT && sha256sum -c ${HASH_FILE} > /tmp/hash_output 2>/dev/null); then
|
||||||
echo "+++ Verified root hashes "
|
echo "+++ Verified root hashes "
|
||||||
valid_hash='y'
|
valid_hash='y'
|
||||||
unmount_root_device
|
unmount_root_device
|
||||||
@ -156,29 +158,234 @@ check_root_checksums() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Check if a device is an LVM2 PV, and if so print the VG name
|
||||||
|
find_lvm_vg_name() {
|
||||||
|
TRACE_FUNC
|
||||||
|
local DEVICE VG
|
||||||
|
DEVICE="$1"
|
||||||
|
|
||||||
|
mkdir -p /tmp/root-hashes-gui
|
||||||
|
if ! lvm pvs "$DEVICE" >/tmp/root-hashes-gui/lvm_vg 2>/dev/null; then
|
||||||
|
DEBUG "Did not detect an LVM2 PV: $DEVICE"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
VG="$(tail -n +2 /tmp/root-hashes-gui/lvm_vg | awk '{print $2}')"
|
||||||
|
if [ -z "$VG" ]; then
|
||||||
|
DEBUG "Could not find LVM2 VG from lvm pvs output:"
|
||||||
|
DEBUG "$(cat /tmp/root-hashes-gui/lvm_vg)"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "$VG"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Open an LVM volume group, then continue looking for more layers in the 'root'
|
||||||
|
# logical volume.
|
||||||
|
open_block_device_lvm() {
|
||||||
|
TRACE_FUNC
|
||||||
|
local VG="$1"
|
||||||
|
|
||||||
|
if ! lvm vgchange -ay "$VG"; then
|
||||||
|
DEBUG "Can't open LVM VG: $VG"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use the LV 'root'. This is the default name used by Qubes. There's no
|
||||||
|
# way to configure this at the moment.
|
||||||
|
if ! [ -e "/dev/mapper/$VG-root" ]; then
|
||||||
|
DEBUG "LVM volume group does not have 'root' logical volume"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Use the root LV now
|
||||||
|
open_block_device_layers "/dev/mapper/$VG-root"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Open a LUKS device, then continue looking for more layers.
|
||||||
|
open_block_device_luks() {
|
||||||
|
TRACE_FUNC
|
||||||
|
local DEVICE="$1"
|
||||||
|
local LUKSDEV
|
||||||
|
LUKSDEV="$(basename "$DEVICE")_crypt"
|
||||||
|
|
||||||
|
# Open the LUKS device. This may prompt interactively for the passphrase, so
|
||||||
|
# hook it up to the console even if stdout/stdin have been redirected.
|
||||||
|
if ! cryptsetup open "$DEVICE" "$LUKSDEV"; then
|
||||||
|
DEBUG "Can't open LUKS volume: $DEVICE"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
open_block_device_layers "/dev/mapper/$LUKSDEV"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Open block device layers to access /root recursively. If another layer (LUKS
|
||||||
|
# or LVM) can be identified, open it and recurse into the new device. When all
|
||||||
|
# recognized layers are opened, print the final block device and exit
|
||||||
|
# successfully (open_root_device will try to mount it).
|
||||||
|
#
|
||||||
|
# This only fails if we can recognize another LUKS or LVM layer, but cannot open
|
||||||
|
# it. It succeeds otherwise, even if no layers are recognized, because we
|
||||||
|
# should try to mount the block device directly in that case.
|
||||||
|
open_block_device_layers() {
|
||||||
|
TRACE_FUNC
|
||||||
|
local DEVICE="$1"
|
||||||
|
local VG
|
||||||
|
|
||||||
|
if ! [ -e "$DEVICE" ]; then
|
||||||
|
DEBUG "Block device doesn't exit: $DEVICE"
|
||||||
|
# This shouldn't really happen, we thought we opened the last layer
|
||||||
|
# successfully. The call stack reveals what LUKS/LVM2 layers have been
|
||||||
|
# opened so far.
|
||||||
|
DEBUG_STACK
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Try to open a LUKS layer
|
||||||
|
if cryptsetup isLuks "$DEVICE" &>/dev/null; then
|
||||||
|
open_block_device_luks "$DEVICE" || return 1
|
||||||
|
# Try to open an LVM layer
|
||||||
|
elif VG="$(find_lvm_vg_name "$DEVICE")"; then
|
||||||
|
open_block_device_lvm "$VG" || return 1
|
||||||
|
else
|
||||||
|
# The given block device exists but is not any layer we understand. Stop
|
||||||
|
# opening layers and try to mount it.
|
||||||
|
echo "$DEVICE"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Try to open a block device as /root. open_block_device_layers() is used to
|
||||||
|
# open LUKS and LVM layers before mounting the filesystem.
|
||||||
|
#
|
||||||
|
# This function does not clean up anything if it is unsuccessful. Use
|
||||||
|
# try_open_root_device() to also clean up when unsuccessful.
|
||||||
|
open_root_device_no_clean_up() {
|
||||||
|
TRACE_FUNC
|
||||||
|
local DEVICE="$1"
|
||||||
|
local FS_DEVICE
|
||||||
|
|
||||||
|
# Open LUKS/LVM and get the name of the block device that should contain the
|
||||||
|
# filesystem. If there are no LUKS/LVM layers, FS_DEVICE is just DEVICE.
|
||||||
|
FS_DEVICE="$(open_block_device_layers "$DEVICE")" || return 1
|
||||||
|
|
||||||
|
# Mount the device
|
||||||
|
if ! mount -o ro "$FS_DEVICE" "$ROOT_MOUNT" &>/dev/null; then
|
||||||
|
DEBUG "Can't mount filesystem on $FS_DEVICE from $DEVICE"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The filesystem must have all of the directories configured. (Intentional
|
||||||
|
# word-split)
|
||||||
|
# shellcheck disable=SC2086
|
||||||
|
if ! (cd "$ROOT_MOUNT" && ls -d $CONFIG_ROOT_DIRLIST &>/dev/null); then
|
||||||
|
DEBUG "Root filesystem on $DEVICE lacks one of the configured directories: $CONFIG_ROOT_DIRLIST"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Root is mounted now and the directories are present
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# If an LVM VG is open, close any layers within it, then close the LVM VG.
|
||||||
|
close_block_device_lvm() {
|
||||||
|
TRACE_FUNC
|
||||||
|
local VG="$1"
|
||||||
|
|
||||||
|
# We always use the LV 'root' currently
|
||||||
|
local LV="/dev/mapper/$VG-root"
|
||||||
|
if [ -e "$LV" ]; then
|
||||||
|
close_block_device_layers "$LV"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# The LVM VG might be open even if no 'root' LV exists, still try to close it.
|
||||||
|
lvm vgchange -an "$VG" || \
|
||||||
|
DEBUG "Can't close LVM VG: $VG"
|
||||||
|
}
|
||||||
|
|
||||||
|
# If a LUKS device is open, close any layers within the LUKS device, then close
|
||||||
|
# the LUKS device.
|
||||||
|
close_block_device_luks() {
|
||||||
|
TRACE_FUNC
|
||||||
|
local DEVICE="$1"
|
||||||
|
local LUKSDEV
|
||||||
|
LUKSDEV="$(basename "$DEVICE")_crypt"
|
||||||
|
|
||||||
|
if [ -e "/dev/mapper/$LUKSDEV" ]; then
|
||||||
|
# Close inner layers before trying to close LUKS
|
||||||
|
close_block_device_layers "/dev/mapper/$LUKSDEV"
|
||||||
|
cryptsetup close "$LUKSDEV" || \
|
||||||
|
DEBUG "Can't close LUKS volume: $LUKSDEV"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Close the root device, including unmounting the filesystem and closing all
|
||||||
|
# layers. This can close a partially-opened device if an error occurs.
|
||||||
|
close_block_device_layers() {
|
||||||
|
TRACE_FUNC
|
||||||
|
local DEVICE="$1"
|
||||||
|
local VG
|
||||||
|
|
||||||
|
if ! [ -e "$DEVICE" ]; then
|
||||||
|
DEBUG "Block device doesn't exit: $DEVICE"
|
||||||
|
# Like in open_root_device(), this shouldn't really happen, show the layers
|
||||||
|
# up to this point via the call stack.
|
||||||
|
DEBUG_STACK
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if cryptsetup isLuks "$DEVICE"; then
|
||||||
|
close_block_device_luks "$DEVICE"
|
||||||
|
elif VG="$(find_lvm_vg_name "$DEVICE")"; then
|
||||||
|
close_block_device_lvm "$VG"
|
||||||
|
fi
|
||||||
|
# Otherwise, we've handled all the layers we understood, there's nothing left
|
||||||
|
# to do.
|
||||||
|
}
|
||||||
|
|
||||||
|
# Try to open the root device, and clean up if unsuccessful.
|
||||||
|
open_root_device() {
|
||||||
|
TRACE_FUNC
|
||||||
|
if ! open_root_device_no_clean_up "$1"; then
|
||||||
|
unmount_root_device
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Close the root device, including unmounting the filesystem and closing all
|
||||||
|
# layers. This can close a partially-opened device if an error occurs. This
|
||||||
|
# never fails, if an error occurs it still tries to close anything it can.
|
||||||
|
close_root_device() {
|
||||||
|
TRACE_FUNC
|
||||||
|
local DEVICE="$1"
|
||||||
|
|
||||||
|
# Unmount the filesystem if it is mounted. If it is not mounted, ignore the
|
||||||
|
# failure. If it is mounted but can't be unmounted, this will fail and we
|
||||||
|
# will fail to close any LUKS/LVM layers too.
|
||||||
|
umount "$ROOT_MOUNT" &>/dev/null || true
|
||||||
|
|
||||||
|
close_block_device_layers "$DEVICE" || true
|
||||||
|
}
|
||||||
|
|
||||||
# detect and set /root device
|
# detect and set /root device
|
||||||
# mount /root if successful
|
# mount /root if successful
|
||||||
detect_root_device()
|
detect_root_device()
|
||||||
{
|
{
|
||||||
|
TRACE_FUNC
|
||||||
|
|
||||||
echo "+++ Detecting root device "
|
echo "+++ Detecting root device "
|
||||||
|
|
||||||
if [ ! -e $ROOT_MOUNT ]; then
|
if [ ! -e $ROOT_MOUNT ]; then
|
||||||
mkdir -p $ROOT_MOUNT
|
mkdir -p $ROOT_MOUNT
|
||||||
fi
|
fi
|
||||||
# unmount $ROOT_MOUNT to be safe
|
# Ensure nothing is opened/mounted
|
||||||
cd / && umount $ROOT_MOUNT 2>/dev/null
|
unmount_root_device
|
||||||
|
|
||||||
# check $CONFIG_ROOT_DEV if set/valid
|
# check $CONFIG_ROOT_DEV if set/valid
|
||||||
if [ -e "$CONFIG_ROOT_DEV" ]; then
|
if [ -e "$CONFIG_ROOT_DEV" ] && open_root_device "$CONFIG_ROOT_DEV"; then
|
||||||
if cryptsetup isLuks $CONFIG_ROOT_DEV >/dev/null 2>&1; then
|
return 0
|
||||||
if cryptsetup open $CONFIG_ROOT_DEV rootdisk; then
|
|
||||||
if mount -o ro /dev/mapper/rootdisk $ROOT_MOUNT >/dev/null 2>&1; then
|
|
||||||
if cd $ROOT_MOUNT && ls -d $CONFIG_ROOT_DIRLIST >/dev/null 2>&1; then # CONFIG_ROOT_DEV is valid device and contains an installed OS
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# generate list of possible boot devices
|
# generate list of possible boot devices
|
||||||
@ -186,7 +393,7 @@ detect_root_device()
|
|||||||
|
|
||||||
# filter out extraneous options
|
# filter out extraneous options
|
||||||
> /tmp_root_device_list
|
> /tmp_root_device_list
|
||||||
for i in `cat /tmp/disklist`; do
|
while IFS= read -r -u 10 i; do
|
||||||
# remove block device from list if numeric partitions exist
|
# remove block device from list if numeric partitions exist
|
||||||
DEV_NUM_PARTITIONS=$((`ls -1 $i* | wc -l`-1))
|
DEV_NUM_PARTITIONS=$((`ls -1 $i* | wc -l`-1))
|
||||||
if [ ${DEV_NUM_PARTITIONS} -eq 0 ]; then
|
if [ ${DEV_NUM_PARTITIONS} -eq 0 ]; then
|
||||||
@ -194,33 +401,25 @@ detect_root_device()
|
|||||||
else
|
else
|
||||||
ls $i* | tail -${DEV_NUM_PARTITIONS} >> /tmp_root_device_list
|
ls $i* | tail -${DEV_NUM_PARTITIONS} >> /tmp_root_device_list
|
||||||
fi
|
fi
|
||||||
done
|
done 10</tmp/disklist
|
||||||
|
|
||||||
# iterate thru possible options and check for LUKS
|
# iterate through possible options
|
||||||
for i in `cat /tmp_root_device_list`; do
|
while IFS= read -r -u 10 i; do
|
||||||
if cryptsetup isLuks $i >/dev/null 2>&1; then
|
if open_root_device "$i"; then
|
||||||
if cryptsetup open $i rootdisk; then
|
# CONFIG_ROOT_DEV is valid device and contains an installed OS
|
||||||
if mount -o ro /dev/mapper/rootdisk $ROOT_MOUNT >/dev/null 2>&1; then
|
CONFIG_ROOT_DEV="$i"
|
||||||
if cd $ROOT_MOUNT && ls -d $CONFIG_ROOT_DIRLIST >/dev/null 2>&1; then
|
return 0
|
||||||
# CONFIG_ROOT_DEV is valid device and contains an installed OS
|
|
||||||
CONFIG_ROOT_DEV="$i"
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
fi
|
||||||
done
|
done 10</tmp_root_device_list
|
||||||
|
|
||||||
# no valid root device found
|
# no valid root device found
|
||||||
echo "Unable to locate $ROOT_MOUNT files on any mounted disk"
|
echo "Unable to locate $ROOT_MOUNT files on any mounted disk"
|
||||||
unmount_root_device
|
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
unmount_root_device()
|
unmount_root_device()
|
||||||
{
|
{
|
||||||
cd /
|
[ -e "$CONFIG_ROOT_DEV" ] && close_root_device "$CONFIG_ROOT_DEV"
|
||||||
umount $ROOT_MOUNT 2>/dev/null
|
|
||||||
cryptsetup close rootdisk
|
|
||||||
}
|
}
|
||||||
|
|
||||||
checkonly="n"
|
checkonly="n"
|
||||||
|
Loading…
Reference in New Issue
Block a user