diff --git a/Jenkinsfile b/Jenkinsfile index 7fc08b0684..017fc85797 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -3,9 +3,9 @@ import nable.cove.helpers.ShellHelper import nable.cove.SecretManager final String jobName = env.JOB_NAME.split('/')[-2] -final String branchName = env.CHANGE_BRANCH ?: env.BRANCH_NAME +final String branchName = env.CHANGE_TARGET ?: env.BRANCH_NAME final boolean isProd = jobName.endsWith('-prd') -final boolean isProdBranch = branchName == 'master' || branchName.startsWith('release') +final boolean isProdBranch = branchName == 'master' final String envType = isProd ? 'prd' : 'dev' def String repositoryName = 'rear' @@ -55,8 +55,10 @@ pipeline { steps { script { if (isProd != isProdBranch) { - echo "Environment mismatch: isProd=${isProd}, isProdBranch=${isProdBranch}. Skipping build." + echo "Environment mismatch: target branch is ${branchName}, job is ${jobName}. Skipping build." shouldBuild = false + } else { + echo "Environment match: target branch is ${branchName}, job is ${jobName}. Proceeding with build." } secretManager = new SecretManager( diff --git a/Makefile b/Makefile index 2e77d2959d..5b7bb5a30d 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,7 @@ # cp -a etc/rear/{mappings,templates} ... # assumes bash. So its better to set SHELL to bash explicitly, using the one from PATH # to also work on MacOS with Homebrew-installed bash +# test SHELL = bash # disable parallel execution of make @@ -34,12 +35,22 @@ else git_date := $(shell git log -n 1 --format="%ai") git_ref := $(shell git rev-parse HEAD | cut -c 1-8) git_count := $(shell git rev-list HEAD --no-merges | wc -l) - git_branch_suffix = $(shell echo "$${CHANGE_BRANCH:-$${BRANCH_NAME:-$$(git symbolic-ref HEAD || echo unknown)}}" | sed -e 's,^.*/,,' -e "s/[^A-Za-z0-9]//g") + git_branch_name = $(shell echo "$${CHANGE_BRANCH:-$${BRANCH_NAME:-$$(git symbolic-ref HEAD || echo unknown)}}") + git_branch_suffix = $(shell echo "$(git_branch_name)" | sed -e 's,^.*/,,' -e "s/[^A-Za-z0-9]//g") + cove_suffix = $(git_branch_suffix) + release_suffix = $(shell echo "$(git_branch_name)" | sed -nE 's/^.*?release\/([0-9.-]*-cove).*/\1/p') + ifneq ($(release_suffix),) + cove_suffix = $(release_suffix) + endif + feature_suffix = $(shell echo "$(git_branch_name)" | sed -nE 's/^.*?(feature|hotfix)\/([A-Za-z0-9_-]+-[0-9]+).*/\2/p') + ifneq ($(feature_suffix),) + cove_suffix = $(feature_suffix) + endif git_status := $(shell git status --porcelain) - git_stamp := $(git_count).$(git_ref).$(git_branch_suffix) ifneq ($(git_status),) - git_stamp := $(git_stamp).changed - endif # git_status + changed_suffix = .changed + endif + git_stamp := $(git_count).$(git_ref).$(git_branch_suffix) else # no git git_date := now git_ref := 0 @@ -50,9 +61,17 @@ else endif # has .git git_stamp ?= 0.0.unknown - distversion = $(version)-git.$(git_stamp) - debrelease = 0git.$(git_stamp) - rpmrelease = .git.$(git_stamp) + ifneq ($(release_suffix),) + distversion = $(cove_suffix)$(changed_suffix) + else + ifneq ($(feature_suffix),) + distversion = $(version)-$(git_count).$(git_ref).$(cove_suffix)$(changed_suffix) + else # is not release version + distversion = $(version)-$(git_stamp)$(changed_suffix) + endif + endif # is release version + debrelease = 0git.$(git_stamp)$(changed_suffix) + rpmrelease = .git.$(git_stamp)$(changed_suffix) obsproject = Archiving:Backup:Rear:Snapshot obspackage = $(name) diff --git a/tests/COVE/000_save_future_dangling_efi_entries.bats b/tests/COVE/000_save_future_dangling_efi_entries.bats index 41a62b5fbd..8f4479f472 100755 --- a/tests/COVE/000_save_future_dangling_efi_entries.bats +++ b/tests/COVE/000_save_future_dangling_efi_entries.bats @@ -1,3 +1,5 @@ +#!/usr/bin/env bats + setup_file() { export REAR_SHARE_DIR="$(realpath "$BATS_TEST_DIRNAME/../../usr/share/rear")" export USING_UEFI_BOOTLOADER="yes" diff --git a/tests/COVE/001_remove_dangling_efi_entries.bats b/tests/COVE/001_remove_dangling_efi_entries.bats index 026a2769d0..1e00669441 100755 --- a/tests/COVE/001_remove_dangling_efi_entries.bats +++ b/tests/COVE/001_remove_dangling_efi_entries.bats @@ -1,3 +1,5 @@ +#!/usr/bin/env bats + setup_file() { export REAR_SHARE_DIR="$(realpath "$BATS_TEST_DIRNAME/../../usr/share/rear")" export USING_UEFI_BOOTLOADER="yes" diff --git a/tests/COVE/002_upgrade_bootloaders_on_debian10.bats b/tests/COVE/002_upgrade_bootloaders_on_debian10.bats new file mode 100755 index 0000000000..7941b6eae9 --- /dev/null +++ b/tests/COVE/002_upgrade_bootloaders_on_debian10.bats @@ -0,0 +1,107 @@ +#!/usr/bin/env bats + +# +# Unit tests for upgrading Shim and GRUB bootloaders on Debian 10 +# + +function setup_file() { + REAR_SHARE_DIR="$(realpath "$BATS_TEST_DIRNAME/../../usr/share/rear")" + export REAR_SHARE_DIR +} + +function setup() { + # shellcheck disable=SC1091 + source "$REAR_SHARE_DIR/lib/global-functions.sh" + + function LogPrint() { + echo "$@" + } + + OS_VERSION=10 + USING_UEFI_BOOTLOADER=yes + function is_cove_in_azure() { + false + } + EFI_STUB=no + UEFI_BOOTLOADER=/boot/efi/EFI/debian/shimx64.efi +} + +@test "Shim and GRUB are upgraded successfully" { + function upgrade_bootloaders() { + return 0 + } + + run source "$REAR_SHARE_DIR/finalize/COVE/Debian/620_upgrade_bootloaders.sh" + [ "$status" -eq 0 ] + + local expected_output="Upgraded signed Shim and GRUB bootloaders for this system." + [ "$output" = "$expected_output" ] +} + +@test "Failed to upgrade Shim and GRUB" { + function upgrade_bootloaders() { + return 1 + } + + run source "$REAR_SHARE_DIR/finalize/COVE/Debian/620_upgrade_bootloaders.sh" + [ "$status" -eq 0 ] + + local expected_output="Failed to upgrade signed Shim and GRUB bootloaders for this system. UEFI Secure Boot might not be available." + [ "$output" = "$expected_output" ] +} + +@test "Skip upgrading Shim and GRUB if the system is not Debian 10" { + # shellcheck disable=SC2034 + OS_VERSION=11 + + run source "$REAR_SHARE_DIR/finalize/COVE/Debian/620_upgrade_bootloaders.sh" + [ "$status" -eq 0 ] + + local expected_output="" + [ "$output" = "$expected_output" ] +} + +@test "Skip upgrading Shim and GRUB if the system is not EFI" { + # shellcheck disable=SC2034 + USING_UEFI_BOOTLOADER=no + + run source "$REAR_SHARE_DIR/finalize/COVE/Debian/620_upgrade_bootloaders.sh" + [ "$status" -eq 0 ] + + local expected_output="" + [ "$output" = "$expected_output" ] +} + +@test "Skip upgrading Shim and GRUB on Azure" { + function is_cove_in_azure() { + true + } + + run source "$REAR_SHARE_DIR/finalize/COVE/Debian/620_upgrade_bootloaders.sh" + [ "$status" -eq 0 ] + + local expected_output="" + [ "$output" = "$expected_output" ] +} + +@test "Skip upgrading Shim and GRUB if the system is EFI stub" { + # shellcheck disable=SC2034 + EFI_STUB=yes + + run source "$REAR_SHARE_DIR/finalize/COVE/Debian/620_upgrade_bootloaders.sh" + [ "$status" -eq 0 ] + + local expected_output="" + [ "$output" = "$expected_output" ] +} + +@test "Skip upgrading Shim and GRUB if the bootloader is not shim" { + # shellcheck disable=SC2034 + UEFI_BOOTLOADER=/boot/efi/EFI/debian/grubx64.efi + + run source "$REAR_SHARE_DIR/finalize/COVE/Debian/620_upgrade_bootloaders.sh" + [ "$status" -eq 0 ] + + local expected_output="" + [ "$output" = "$expected_output" ] +} diff --git a/tests/COVE/003_determine_efi_bootloader.bats b/tests/COVE/003_determine_efi_bootloader.bats new file mode 100755 index 0000000000..4c23f176eb --- /dev/null +++ b/tests/COVE/003_determine_efi_bootloader.bats @@ -0,0 +1,299 @@ +#!/usr/bin/env bats + +# +# Unit tests for determining EFI bootloaders +# + +function setup_file() { + REAR_SHARE_DIR="$(realpath "$BATS_TEST_DIRNAME/../../usr/share/rear")" + export REAR_SHARE_DIR +} + +function setup() { + # shellcheck disable=SC1091 + source "$REAR_SHARE_DIR/lib/global-functions.sh" + + function LogPrint() { + echo "$@" + } +} + +function source_efi_functions { + # shellcheck disable=SC1091 + source "$REAR_SHARE_DIR/lib/uefi-functions.sh" +} + +@test "Get current boot entry" { + function efi_run_efibootmgr { + echo "BootCurrent: 0001" + echo "BootOrder: 0003,0000,0001,0002" + echo "Boot0000* EFI Virtual disk (0.0) PcieRoot(0x8)/Pci(0x0,0x0)/SCSI(0,0)" + echo "Boot0001* EFI VMware Virtual SATA CDROM Drive (0.0) PcieRoot(0x8)/Pci(0x2,0x0)/Sata(0,0,0)" + echo "Boot0002* EFI Network PcieRoot(0x8)/Pci(0x1,0x0)/MAC(00505698f752,1)" + } + + source_efi_functions + + local current_boot + current_boot=$(efi_get_current_boot) + + [ "$current_boot" = "0001" ] +} + +@test "Get EFI device path" { + function efi_run_efibootmgr { + echo "BootCurrent: 0003" + echo "BootOrder: 0003,0000,0001,0002" + echo "Boot0000* EFI Virtual disk (0.0) PcieRoot(0x8)/Pci(0x0,0x0)/SCSI(0,0)" + echo "Boot0001* EFI VMware Virtual SATA CDROM Drive (0.0) PcieRoot(0x8)/Pci(0x2,0x0)/Sata(0,0,0)" + echo "Boot0002* EFI Network PcieRoot(0x8)/Pci(0x1,0x0)/MAC(00505698f752,1)" + # shellcheck disable=SC2028 + echo "Boot0003* Red Hat Enterprise Linux HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\\EFI\\redhat\\shimx64.efi" + # shellcheck disable=SC2028 + echo "Boot0004* My GRUB HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\\EFI\\redhat\\grubx64.efi" + } + + source_efi_functions + + local dp + dp=$(efi_get_device_path "0003") + [ "$dp" = "HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\EFI\redhat\shimx64.efi" ] + + dp=$(efi_get_device_path "0004") + [ "$dp" = "HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\EFI\redhat\grubx64.efi" ] +} + +@test "Failed get EFI device path" { + function efi_run_efibootmgr { + return 1 + } + + source_efi_functions + + run efi_get_device_path "0003" + + [ $status -eq 1 ] + [ -z "$output" ] + + function efi_run_efibootmgr { + echo "Boot0000* EFI Virtual disk (0.0) PcieRoot(0x8)/Pci(0x0,0x0)/SCSI(0,0)" + } + + run efi_get_device_path "0001" + + [ $status -eq 1 ] + [ -z "$output" ] +} + +@test "Get bootloader path" { + local num="0003" + function efi_get_device_path { + if [ "$1" != "$num" ]; then + return 1 + fi + # shellcheck disable=SC2028 + echo "HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\\EFI\\redhat\\grubx64.efi" + } + + source_efi_functions + + local bootloader_path + bootloader_path=$(efi_get_bootloader_path "$num") + + [ "$bootloader_path" = "/EFI/redhat/grubx64.efi" ] +} + +@test "Get bootloader path with File(...)" { + local num="0003" + function efi_get_device_path { + if [ "$1" != "$num" ]; then + return 1 + fi + # shellcheck disable=SC2028 + echo "HD(1,GPT,16f5dcec-b713-4d7b-83a3-824c82d95c99,0x800,0x100000)/File(\EFI\debian\shimx64.efi)" + } + + source_efi_functions + + local bootloader_path + bootloader_path=$(efi_get_bootloader_path "$num") + + [ "$bootloader_path" = "/EFI/debian/shimx64.efi" ] +} + +@test "Failed to get bootloader path" { + function efi_get_device_path { + return 1 + } + + source_efi_functions + + run efi_get_bootloader_path "0003" + + [ $status -eq 1 ] + [ -z "$output" ] + + function efi_get_device_path { + echo "HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)" + } + + [ $status -eq 1 ] + [ -z "$output" ] +} + +@test "Get EFI boot partuuid for GPT" { + local num="0003" + function efi_get_device_path { + if [ "$1" != "$num" ]; then + return 1 + fi + # shellcheck disable=SC2028 + echo "HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\\EFI\\redhat\\grubx64.efi" + } + + source_efi_functions + + local partuuid + partuuid=$(efi_get_boot_partuuid "$num") + + [ "$partuuid" = "bc6f95e7-893a-434b-8b76-f3d52e6ad28d" ] +} + +@test "Get current full EFI bootloader path" { + function efi_run_efibootmgr { + echo "BootCurrent: 0003" + echo "BootOrder: 0003,0000,0001,0002" + echo "Boot0000* EFI Virtual disk (0.0) PcieRoot(0x8)/Pci(0x0,0x0)/SCSI(0,0)" + echo "Boot0001* EFI VMware Virtual SATA CDROM Drive (0.0) PcieRoot(0x8)/Pci(0x2,0x0)/Sata(0,0,0)" + echo "Boot0002* EFI Network PcieRoot(0x8)/Pci(0x1,0x0)/MAC(00505698f752,1)" + # shellcheck disable=SC2028 + echo "Boot0003* Red Hat Enterprise Linux HD(1,GPT,bc6f95e7-893a-434b-8b76-f3d52e6ad28d,0x800,0x12c000)/\\EFI\\redhat\\shimx64.efi" + } + + function efi_get_mountpoint { + if [ "$1" != "bc6f95e7-893a-434b-8b76-f3d52e6ad28d" ]; then + return 1 + fi + + echo "/boot/efi" + } + + function efi_check_bootloader_path { + return 0 + } + + source_efi_functions + + local full_path + full_path="$(efi_get_current_full_bootloader_path)" + + [ "$full_path" = "/boot/efi/EFI/redhat/shimx64.efi" ] +} + +@test "Failed to get current full EFI bootloader path: cannot get current boot" { + function efi_get_current_boot { + return 1 + } + + source_efi_functions + + run efi_get_current_full_bootloader_path + + [ $status -eq 1 ] + [ "$output" = "WARN: EFI: Failed to get current boot" ] +} + +@test "Failed to get current full EFI bootloader path: cannot get partuuid" { + function efi_get_current_boot { + echo "0000" + } + + function efi_get_boot_partuuid { + return 1 + } + + source_efi_functions + + run efi_get_current_full_bootloader_path + + [ $status -eq 1 ] + [ "$output" = "WARN: EFI: Failed to get partuuid for the current boot '0000'" ] +} + +@test "Failed to get current full EFI bootloader path: cannot get mountpoint" { + function efi_get_current_boot { + echo "0000" + } + + local uuid="bc6f95e7-893a-434b-8b76-f3d52e6ad28d" + function efi_get_boot_partuuid { + echo "$uuid" + } + + function efi_get_mountpoint { + return 1 + } + + source_efi_functions + + run efi_get_current_full_bootloader_path + + [ $status -eq 1 ] + [ "$output" = "WARN: EFI: Failed to get mountpoint for partuuid '$uuid'" ] +} + +@test "Failed to get current full EFI bootloader path: cannot get bootloader path" { + function efi_get_current_boot { + echo "0000" + } + + local uuid="bc6f95e7-893a-434b-8b76-f3d52e6ad28d" + function efi_get_boot_partuuid { + echo "$uuid" + } + + function efi_get_mountpoint { + echo "/boot/efi" + } + + function efi_get_bootloader_path { + return 1 + } + + source_efi_functions + + run efi_get_current_full_bootloader_path + + [ $status -eq 1 ] + [ "$output" = "WARN: EFI: Failed to get bootloader path for the current boot '0000'" ] +} + +@test "Failed to get current full EFI bootloader path: bootloader path does not exist" { + function efi_get_current_boot { + echo "0000" + } + + local uuid="bc6f95e7-893a-434b-8b76-f3d52e6ad28d" + function efi_get_boot_partuuid { + echo "$uuid" + } + + function efi_get_mountpoint { + echo "/boot/efi" + } + + function efi_get_bootloader_path { + echo "/EFI/redhat/shimx64.efi" + } + + function efi_check_bootloader_path { + return 1 + } + + source_efi_functions + + run efi_get_current_full_bootloader_path + + [ $status -eq 1 ] + [ "$output" = "WARN: EFI: Bootloader path '/boot/efi/EFI/redhat/shimx64.efi' does not exist" ] +} diff --git a/usr/share/rear/checkintegrity/COVE/default/001_compare_binaries.sh b/usr/share/rear/checkintegrity/COVE/default/001_compare_binaries.sh new file mode 100644 index 0000000000..ee38b70224 --- /dev/null +++ b/usr/share/rear/checkintegrity/COVE/default/001_compare_binaries.sh @@ -0,0 +1,16 @@ +# Check md5sum files +LogPrint "Checking if certain restored files are consistent with the recreated system" +local md5sum_stdout +for md5sum_file in files.md5sum cove-files.md5sum ; do + # Skip when there are no checksums for this file: + test -s "$VAR_DIR/layout/config/$md5sum_file" || continue + + DebugPrint "See $VAR_DIR/layout/config/$md5sum_file what files are checked" + if ! md5sum_stdout="$( md5sum -c --quiet < $VAR_DIR/layout/config/$md5sum_file )" ; then + LogPrintError "Restored files do not fully match the recreated system" + LogPrintError "$( sed -e 's/^/ /' <<< "$md5sum_stdout" )" + Error "Binary verification failed: checksums do not match for $md5sum_file" + fi +done + +LogPrint "Binary verification passed: all checksums match successfully" diff --git a/usr/share/rear/conf/GNU/Linux.conf b/usr/share/rear/conf/GNU/Linux.conf index ed22783725..fbfc6d39f3 100644 --- a/usr/share/rear/conf/GNU/Linux.conf +++ b/usr/share/rear/conf/GNU/Linux.conf @@ -23,7 +23,6 @@ PROGS+=( rpc.statd rpcbind mknod -blkid vol_id udev_volume_id portmap diff --git a/usr/share/rear/conf/default.conf b/usr/share/rear/conf/default.conf index 9787d44673..7ad09d5401 100644 --- a/usr/share/rear/conf/default.conf +++ b/usr/share/rear/conf/default.conf @@ -1759,6 +1759,7 @@ REQUIRED_PROGS=( awk bash bc +blkid cat cmp cp @@ -3343,6 +3344,13 @@ COVE_INSTALL_DIR= # The timestamp value is passed by the Backup Manager via an environment variable. COVE_TIMESTAMP="${COVE_TIMESTAMP:-}" +# Verify binaries hash sum after recovery +# Set to 1 to enable verification, 0 to disable +COVE_VERIFY_BINARIES=0 + +# Locations to verify during recovery when COVE_VERIFY_BINARIES is enabled +COVE_VERIFY_PATHS=(/boot/efi /usr/bin /usr/sbin /usr/lib) + ## # End of BACKUP=COVE default settings. #### diff --git a/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh b/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh new file mode 100644 index 0000000000..d7f8db0838 --- /dev/null +++ b/usr/share/rear/finalize/COVE/Debian/620_upgrade_bootloaders.sh @@ -0,0 +1,53 @@ +# +# Upgrade Shim and GRUB bootloaders on Debian 10 +# +# Shim and GRUB are upgraded because Shim from the rescue system +# which is Debian12-based adds new entries to SBAT that leads to having +# non-SecureBoot compatible device after BMR. +# + +if [ "$OS_VERSION" != "10" ]; then + return 0 +fi + +if ! is_true "$USING_UEFI_BOOTLOADER"; then + return 0 +fi + +if is_cove_in_azure; then + return 0 +fi + +if is_true "$EFI_STUB"; then + return 0 +fi + +if [ "${UEFI_BOOTLOADER##*/}" != "shimx64.efi" ]; then + return 0 +fi + +declare -F upgrade_bootloaders >/dev/null || function upgrade_bootloaders() { + local target_bootloader_dir="${UEFI_BOOTLOADER%/*}" + target_bootloader_dir="$TARGET_FS_ROOT$target_bootloader_dir" + + local shim="$target_bootloader_dir/shimx64.efi" + local grub="$target_bootloader_dir/grubx64.efi" + + if [ ! -e "$shim" ] || [ ! -e "$grub" ]; then + return 1 + fi + + cp -b /usr/lib/shim/shimx64.efi.signed "$shim" || return 1 + cp -b /usr/lib/grub/x86_64-efi-signed/grubx64.efi.signed "$grub" || \ + { mv "$shim~" "$shim"; return 1; } + + rm "$shim~" "$grub~" + + return 0 +} + +if upgrade_bootloaders; then + LogPrint "Upgraded signed Shim and GRUB bootloaders for this system." +else + LogPrint "Failed to upgrade signed Shim and GRUB bootloaders for this system. UEFI Secure Boot might not be available." +fi diff --git a/usr/share/rear/finalize/default/060_compare_files.sh b/usr/share/rear/finalize/default/060_compare_files.sh index 638c22fa70..716690a89a 100644 --- a/usr/share/rear/finalize/default/060_compare_files.sh +++ b/usr/share/rear/finalize/default/060_compare_files.sh @@ -1,3 +1,14 @@ +# Copy md5sum files to COVE_INSTALL_DIR if they exist +if is_true "$COVE_VERIFY_BINARIES" ; then + local rear_dir="$VAR_DIR/layout/config" + local cove_dir="$COVE_INSTALL_DIR/rear/var/lib/rear/layout/config" + for md5sum_file in files.md5sum cove-files.md5sum ; do + if test -f "$rear_dir/$md5sum_file" ; then + mkdir -p "$cove_dir" + cp "$rear_dir/$md5sum_file" "$cove_dir/$md5sum_file" + fi + done +fi # Skip when there are no checksums: test -s $VAR_DIR/layout/config/files.md5sum || return 0 @@ -37,3 +48,14 @@ if ! md5sum_stdout="$( chroot $TARGET_FS_ROOT md5sum -c --quiet < $VAR_DIR/layou LogPrintError "Manually check if those changed files cause issues in your recreated system" return 1 fi + +# For COVE backup with binary verification enabled, treat checksum mismatch as fatal error +if is_true "$COVE_VERIFY_BINARIES" ; then + LogPrint "Checking if COVE files are consistent" + if ! md5sum_stdout="$( chroot $TARGET_FS_ROOT md5sum -c --quiet < $VAR_DIR/layout/config/cove-files.md5sum )" ; then + LogPrintError "Restored files in $TARGET_FS_ROOT do not fully match the recreated system" + LogPrintError "$( sed -e "s|^/|$TARGET_FS_ROOT/|" -e 's/^/ /' <<< "$md5sum_stdout" )" + + Error "COVE binary verification failed: checksums do not match" + fi +fi diff --git a/usr/share/rear/layout/save/default/600_snapshot_files.sh b/usr/share/rear/layout/save/default/600_snapshot_files.sh index 3ac6b07ef4..27b9bd2027 100644 --- a/usr/share/rear/layout/save/default/600_snapshot_files.sh +++ b/usr/share/rear/layout/save/default/600_snapshot_files.sh @@ -13,3 +13,24 @@ for obj in "${CHECK_CONFIG_FILES[@]}" ; do fi done md5sum "${config_files[@]}" > $VAR_DIR/layout/config/files.md5sum + +# For COVE backup, additionally verify binaries if enabled +local item +local cove_files=() +if [ "$WORKFLOW" = "mksystemstate" ] && is_true "$COVE_VERIFY_BINARIES" ; then + for item in "${COVE_VERIFY_PATHS[@]}" ; do + if [ -d "$item" ] ; then + while IFS= read -r -d '' file ; do + cove_files+=( "$file" ) + done < <(find "$item" -type f -print0) + elif [ -f "$item" ] ; then + cove_files+=( "$item" ) + fi + done +fi + +cove_md5sum_file="$VAR_DIR/layout/config/cove-files.md5sum" +: > "$cove_md5sum_file" +for filepath in "${cove_files[@]}"; do + md5sum "$filepath" >> "$cove_md5sum_file" +done diff --git a/usr/share/rear/lib/checkintegrity-workflow.sh b/usr/share/rear/lib/checkintegrity-workflow.sh new file mode 100644 index 0000000000..8c105a0e70 --- /dev/null +++ b/usr/share/rear/lib/checkintegrity-workflow.sh @@ -0,0 +1,12 @@ +# checkintegrity-workflow.sh +# +# checkintegrity workflow for Relax-and-Recover +# +# This file is part of Relax-and-Recover, licensed under the GNU General +# Public License. Refer to the included COPYING for full text of license. + +WORKFLOW_checkintegrity_DESCRIPTION="check files consistency" +WORKFLOWS+=( checkintegrity ) +WORKFLOW_checkintegrity () { + SourceStage "checkintegrity" +} diff --git a/usr/share/rear/lib/uefi-functions.sh b/usr/share/rear/lib/uefi-functions.sh index ee52fb02de..bebc9e9616 100644 --- a/usr/share/rear/lib/uefi-functions.sh +++ b/usr/share/rear/lib/uefi-functions.sh @@ -125,3 +125,144 @@ function build_boot_efi { fi } +declare -F efi_run_efibootmgr >/dev/null || function efi_run_efibootmgr() { + if ! has_binary efibootmgr; then + return 1 + fi + + local efibootmgr_output="$TMP_DIR/efibootmgr_v_output" + + if [ ! -s "$efibootmgr_output" ]; then + if ! efibootmgr -v 2>/dev/null > "$efibootmgr_output"; then + return 1 + fi + fi + + cat "$efibootmgr_output" +} + +declare -F efi_get_current_boot >/dev/null || function efi_get_current_boot() { + local boot_current + boot_current=$(efi_run_efibootmgr | sed -n 's|^BootCurrent: ||p') || return 1 + echo "$boot_current" +} + +declare -F efi_get_device_path >/dev/null || function efi_get_device_path() { + local bootnum="Boot$1" + + local dp + dp="$(efi_run_efibootmgr | sed -n "s|^${bootnum}\* .*\\t||p")" || return 1 + + if [ -z "$dp" ]; then + return 1 + fi + + echo "$dp" +} + +declare -F efi_get_bootloader_path >/dev/null || function efi_get_bootloader_path { + local bootnum="$1" + + local dp + dp=$(efi_get_device_path "$bootnum") || return 1 + + local path + path=$(echo "$dp" | sed -n 's|HD(.*)/||; s|\\|/|g; p') || return 1 + + if [[ $path =~ ^File\((.*)\)$ ]]; then + path="${BASH_REMATCH[1]}" + fi + + if [ -z "$path" ]; then + return 1 + fi + + echo "$path" +} + +declare -F efi_get_boot_partuuid >/dev/null || function efi_get_boot_partuuid { + local bootnum="$1" + + local dp + dp=$(efi_get_device_path "$bootnum") || return 1 + + local partuuid + partuuid=$(echo "$dp" | sed -n 's/^HD([0-9]\+,GPT,\([0-9a-fA-F-]\+\),.*/\1/p') || return 1 + + if [ -z "$partuuid" ]; then + return 1 + fi + + echo "$partuuid" +} + +declare -F efi_get_mountpoint >/dev/null || function efi_get_mountpoint { + if ! has_binary findmnt; then + return 1 + fi + + local partuuid=$1 + + local mnt + mnt=$(findmnt -S PARTUUID="$partuuid" -n -o TARGET) || return 1 + + if [ -z "$mnt" ]; then + return 1 + fi + + echo "$mnt" +} + +declare -F efi_check_bootloader_path >/dev/null || function efi_check_bootloader_path { + [ -f "$1" ] +} + +declare -F efi_get_current_full_bootloader_path >/dev/null || function efi_get_current_full_bootloader_path { + local current_boot + if ! current_boot=$(efi_get_current_boot); then + LogPrint "WARN: EFI: Failed to get current boot" + return 1 + fi + + local partuuid + if ! partuuid=$(efi_get_boot_partuuid "$current_boot"); then + LogPrint "WARN: EFI: Failed to get partuuid for the current boot '$current_boot'" + return 1 + fi + + local mnt + if ! mnt=$(efi_get_mountpoint "$partuuid"); then + LogPrint "WARN: EFI: Failed to get mountpoint for partuuid '$partuuid'" + return 1 + fi + + local path + if ! path=$(efi_get_bootloader_path "$current_boot"); then + LogPrint "WARN: EFI: Failed to get bootloader path for the current boot '$current_boot'" + return 1 + fi + + if [ "${path::1}" != '/' ]; then + path="/$path" + fi + + local full_path="$mnt$path" + + if ! efi_check_bootloader_path "$full_path"; then + LogPrint "WARN: EFI: Bootloader path '$full_path' does not exist" + return 1 + fi + + echo "$full_path" +} + +declare -F efi_sb_enabled >/dev/null || function efi_sb_enabled { + if ! has_binary mokutil; then + return 1 + fi + + local sb_state + sb_state=$(mokutil --sb-state 2>&1) || return 1 + + grep -q "SecureBoot enabled" <<<"$sb_state" +} diff --git a/usr/share/rear/rescue/systemstate/default/840_determine_efi_bootloader.sh b/usr/share/rear/rescue/systemstate/default/840_determine_efi_bootloader.sh new file mode 100644 index 0000000000..668f0ba2a7 --- /dev/null +++ b/usr/share/rear/rescue/systemstate/default/840_determine_efi_bootloader.sh @@ -0,0 +1,21 @@ +# +# Determine current EFI bootloader +# + +if ! is_true "$USING_UEFI_BOOTLOADER"; then + return 0 +fi + +if is_true "$EFI_STUB"; then + return 0 +fi + +if [ -f "$UEFI_BOOTLOADER" ] && ! efi_sb_enabled; then + return 0 +fi + +if ! EFI_BOOTLOADER_PATH=$(efi_get_current_full_bootloader_path); then + return 0 +fi + +SECURE_BOOT_BOOTLOADER="$EFI_BOOTLOADER_PATH"