#!/usr/bin/env bash # ============================================================================= # install-grub-entry.sh # # Installa la voce GRUB per BastionGuard Recovery. # Supporta: # - BIOS / Legacy # - UEFI senza Secure Boot # - UEFI + shim signed + MOK # - UEFI + custom Secure Boot keys # # SECUREBOOT_MODE: # auto -> rileva e sceglie shim/custom/off # shim -> forza shim + grub signed + MOK # custom -> forza custom EFI signing chain # off -> nessuna chain SB richiesta # # CUSTOM_SIGN_BACKEND: # auto -> usa sbctl se presente, altrimenti verifica passiva # sbctl -> usa sbctl per verifiche/info su chain custom # none -> nessun backend custom specifico # ============================================================================= set -Eeuo pipefail # ----------------------------------------------------------------------------- # Config # ----------------------------------------------------------------------------- SECUREBOOT_MODE="${SECUREBOOT_MODE:-auto}" # auto | shim | custom | off CUSTOM_SIGN_BACKEND="${CUSTOM_SIGN_BACKEND:-auto}" # auto | sbctl | none GRUB_SOURCE="/usr/share/bastionguard-backup/grub/42_bastionguard-recovery" GRUB_DEST="/etc/grub.d/42_bastionguard-recovery" RECOVERY_DIR="/boot/bastionguard-recovery" LIVE_DIR="$RECOVERY_DIR/live" KERNEL_FILE="$RECOVERY_DIR/vmlinuz" INITRD_FILE="$RECOVERY_DIR/initrd.img" SQUASHFS_FILE="$LIVE_DIR/filesystem.squashfs" SIZE_FILE="$LIVE_DIR/filesystem.size" EFI_BOOT_DIR="$RECOVERY_DIR/EFI/BOOT" EFI_DEBIAN_DIR="$RECOVERY_DIR/EFI/debian" EFI_GRUB_DIR="$RECOVERY_DIR/grub" BOOTX64_EFI="$EFI_BOOT_DIR/BOOTx64.EFI" GRUBX64_EFI="$EFI_BOOT_DIR/grubx64.efi" MMX64_EFI="$EFI_BOOT_DIR/mmx64.efi" EFI_BOOT_CFG="$EFI_BOOT_DIR/grub.cfg" EFI_DEBIAN_CFG="$EFI_DEBIAN_DIR/grub.cfg" MAIN_GRUB_CFG="$EFI_GRUB_DIR/grub.cfg" ROOT_STUB_CFG="$RECOVERY_DIR/grub.cfg" SECUREBOOT_DIR="/etc/bastionguard-backup/secureboot" MOK_DER="$SECUREBOOT_DIR/MOK.der" CUSTOM_SB_DIR="/etc/bastionguard-backup/custom-secureboot" CUSTOM_EFI_DER="$CUSTOM_SB_DIR/DB.der" CUSTOM_EFI_CRT="$CUSTOM_SB_DIR/DB.crt" CUSTOM_EFI_KEY="$CUSTOM_SB_DIR/DB.key" LOG_PREFIX="[BastionGuard Recovery]" SB_FIRMWARE_MODE="bios" SB_ENABLED="no" SB_HAS_MS_KEYS="no" SB_HAS_CUSTOM_KEYS="no" SB_EFFECTIVE_MODE="off" CUSTOM_SIGN_BACKEND_EFFECTIVE="none" GRUB_CFG_TARGET="" # ----------------------------------------------------------------------------- # Logging / errors # ----------------------------------------------------------------------------- log() { echo "$LOG_PREFIX [INFO] $*" } warn() { echo "$LOG_PREFIX [WARN] $*" >&2 } die() { echo "$LOG_PREFIX [ERROR] $*" >&2 exit 1 } # ----------------------------------------------------------------------------- # Helpers # ----------------------------------------------------------------------------- require_root() { [ "$EUID" -eq 0 ] || die "Eseguire come root." } require_file() { local f="$1" [ -f "$f" ] || die "File non trovato: $f" } optional_file_info() { local f="$1" if [ -f "$f" ]; then echo " $f ($(du -sh "$f" | cut -f1))" fi } detect_firmware_mode() { if [ -d /sys/firmware/efi ]; then SB_FIRMWARE_MODE="uefi" else SB_FIRMWARE_MODE="bios" fi } detect_secure_boot_state() { SB_ENABLED="no" [ "$SB_FIRMWARE_MODE" = "uefi" ] || return 0 if command -v mokutil >/dev/null 2>&1; then if mokutil --sb-state 2>/dev/null | grep -qi "SecureBoot enabled"; then SB_ENABLED="yes" return 0 fi if mokutil --sb-state 2>/dev/null | grep -qi "SecureBoot disabled"; then SB_ENABLED="no" return 0 fi fi local sb_var sb_var="$(find /sys/firmware/efi/efivars -maxdepth 1 -name 'SecureBoot-*' 2>/dev/null | head -n1 || true)" if [ -n "$sb_var" ] && [ -r "$sb_var" ]; then local value value="$(od -An -t u1 -j4 -N1 "$sb_var" 2>/dev/null | tr -d ' ')" if [ "${value:-0}" = "1" ]; then SB_ENABLED="yes" fi fi } detect_microsoft_keys() { SB_HAS_MS_KEYS="no" [ "$SB_FIRMWARE_MODE" = "uefi" ] || return 0 if command -v efi-readvar >/dev/null 2>&1; then local tmp_db tmp_kek tmp_db="$(mktemp)" tmp_kek="$(mktemp)" efi-readvar -v db >"$tmp_db" 2>/dev/null || true efi-readvar -v KEK >"$tmp_kek" 2>/dev/null || true if grep -Eqi 'Microsoft|Windows UEFI CA|Microsoft Corporation UEFI' "$tmp_db" "$tmp_kek"; then SB_HAS_MS_KEYS="yes" fi rm -f "$tmp_db" "$tmp_kek" else warn "efi-readvar non trovato sull'host; rilevamento chiavi Microsoft limitato." fi } detect_custom_keys() { if [ -f "$CUSTOM_EFI_DER" ] || [ -f "$CUSTOM_EFI_CRT" ] || [ -f "$CUSTOM_EFI_KEY" ]; then SB_HAS_CUSTOM_KEYS="yes" else SB_HAS_CUSTOM_KEYS="no" fi } detect_custom_sign_backend() { case "$CUSTOM_SIGN_BACKEND" in sbctl) command -v sbctl >/dev/null 2>&1 || die "CUSTOM_SIGN_BACKEND=sbctl ma sbctl non è installato." CUSTOM_SIGN_BACKEND_EFFECTIVE="sbctl" ;; none) CUSTOM_SIGN_BACKEND_EFFECTIVE="none" ;; auto) if command -v sbctl >/dev/null 2>&1; then CUSTOM_SIGN_BACKEND_EFFECTIVE="sbctl" else CUSTOM_SIGN_BACKEND_EFFECTIVE="none" fi ;; *) die "CUSTOM_SIGN_BACKEND non valido: $CUSTOM_SIGN_BACKEND" ;; esac } decide_secureboot_mode() { detect_firmware_mode detect_secure_boot_state detect_microsoft_keys detect_custom_keys detect_custom_sign_backend log "Firmware mode : $SB_FIRMWARE_MODE" log "Secure Boot : $SB_ENABLED" log "Microsoft keys : $SB_HAS_MS_KEYS" log "Custom cert : $SB_HAS_CUSTOM_KEYS" log "Requested mode : $SECUREBOOT_MODE" log "Custom backend : $CUSTOM_SIGN_BACKEND_EFFECTIVE" if [ "$SB_FIRMWARE_MODE" != "uefi" ]; then SB_EFFECTIVE_MODE="off" log "Effective mode : off (BIOS/Legacy)" return 0 fi case "$SECUREBOOT_MODE" in off) SB_EFFECTIVE_MODE="off" ;; shim) SB_EFFECTIVE_MODE="shim" ;; custom) SB_EFFECTIVE_MODE="custom" ;; auto) if [ "$SB_ENABLED" != "yes" ]; then SB_EFFECTIVE_MODE="off" elif [ "$SB_HAS_MS_KEYS" = "yes" ]; then SB_EFFECTIVE_MODE="shim" elif [ "$SB_HAS_CUSTOM_KEYS" = "yes" ] || [ "$CUSTOM_SIGN_BACKEND_EFFECTIVE" = "sbctl" ]; then SB_EFFECTIVE_MODE="custom" else die "Secure Boot attivo ma nessuna chain valida rilevata (né Microsoft/shim né custom)." fi ;; *) die "SECUREBOOT_MODE non valido: $SECUREBOOT_MODE" ;; esac log "Effective mode : $SB_EFFECTIVE_MODE" } check_recovery_files() { echo "[1/5] Verifica file recovery..." require_file "$KERNEL_FILE" require_file "$INITRD_FILE" require_file "$SQUASHFS_FILE" if [ ! -f "$SIZE_FILE" ]; then warn "filesystem.size non trovato — continuo comunque." fi echo " kernel -> $KERNEL_FILE ($(du -sh "$KERNEL_FILE" | cut -f1))" echo " initrd -> $INITRD_FILE ($(du -sh "$INITRD_FILE" | cut -f1))" echo " squashfs -> $SQUASHFS_FILE ($(du -sh "$SQUASHFS_FILE" | cut -f1))" [ -f "$SIZE_FILE" ] && echo " size -> $SIZE_FILE ($(du -sh "$SIZE_FILE" | cut -f1))" } install_grub_script() { echo "[2/5] Installazione script GRUB..." require_file "$GRUB_SOURCE" install -Dm755 "$GRUB_SOURCE" "$GRUB_DEST" echo " installato: $GRUB_DEST" } check_kernel_signature_generic() { if command -v sbverify >/dev/null 2>&1; then if sbverify --list "$KERNEL_FILE" >/dev/null 2>&1; then echo " [OK] Kernel firmato/verificabile con sbverify." else warn "Kernel non firmato o firma non verificabile con sbverify." fi else warn "sbverify non trovato, salto verifica firma kernel." fi } check_kernel_signature_sbctl() { if ! command -v sbctl >/dev/null 2>&1; then warn "sbctl non trovato, salto verifica kernel via sbctl." return 0 fi if sbctl verify "$KERNEL_FILE" >/dev/null 2>&1; then echo " [OK] Kernel verificato da sbctl." else warn "Kernel non verificato da sbctl." fi } check_efi_binary_signature_sbctl() { local f="$1" [ -f "$f" ] || return 0 if ! command -v sbctl >/dev/null 2>&1; then return 0 fi if sbctl verify "$f" >/dev/null 2>&1; then echo " [OK] Verificato da sbctl: $f" else warn "Non verificato da sbctl: $f" fi } check_efi_layout_off() { echo " Modalità EFI: off" [ -f "$BOOTX64_EFI" ] || warn "BOOTx64.EFI non trovato: $BOOTX64_EFI" [ -f "$GRUBX64_EFI" ] || warn "grubx64.efi non trovato: $GRUBX64_EFI" } check_efi_layout_shim() { echo " Modalità EFI: shim" require_file "$BOOTX64_EFI" require_file "$GRUBX64_EFI" echo " [OK] Chain shim trovata:" optional_file_info "$BOOTX64_EFI" optional_file_info "$GRUBX64_EFI" optional_file_info "$MMX64_EFI" if [ -f "$MOK_DER" ]; then echo " [OK] Certificato MOK trovato: $MOK_DER" echo echo " Se la MOK non è registrata nel firmware:" echo " mokutil --import $MOK_DER" echo " (poi riavvio e conferma in MokManager)" else warn "Certificato MOK non trovato: $MOK_DER" fi } check_efi_layout_custom() { echo " Modalità EFI: custom" require_file "$BOOTX64_EFI" require_file "$GRUBX64_EFI" echo " [OK] Chain custom trovata:" optional_file_info "$BOOTX64_EFI" optional_file_info "$GRUBX64_EFI" if [ -f "$CUSTOM_EFI_DER" ]; then echo " [OK] Certificato custom trovato: $CUSTOM_EFI_DER" else warn "Certificato custom DER non trovato: $CUSTOM_EFI_DER" fi if [ "$CUSTOM_SIGN_BACKEND_EFFECTIVE" = "sbctl" ]; then echo " Backend custom: sbctl" check_kernel_signature_sbctl check_efi_binary_signature_sbctl "$BOOTX64_EFI" check_efi_binary_signature_sbctl "$GRUBX64_EFI" else check_kernel_signature_generic fi } check_efi_layout_configs() { [ -f "$EFI_BOOT_CFG" ] || warn "Stub EFI BOOT mancante: $EFI_BOOT_CFG" [ -f "$EFI_DEBIAN_CFG" ] || warn "Stub EFI debian mancante: $EFI_DEBIAN_CFG" [ -f "$MAIN_GRUB_CFG" ] || warn "Main grub.cfg mancante: $MAIN_GRUB_CFG" [ -f "$ROOT_STUB_CFG" ] || warn "Stub root grub.cfg mancante: $ROOT_STUB_CFG" } check_secureboot_and_efi() { echo "[3/5] Controllo Secure Boot / EFI..." decide_secureboot_mode if [ "$SB_FIRMWARE_MODE" != "uefi" ]; then echo " Sistema BIOS/Legacy rilevato — nessuna verifica EFI necessaria." return 0 fi echo " Sistema UEFI rilevato." case "$SB_EFFECTIVE_MODE" in off) check_kernel_signature_generic check_efi_layout_off ;; shim) check_kernel_signature_generic check_efi_layout_shim ;; custom) check_efi_layout_custom ;; *) die "Modalità Secure Boot effettiva non valida: $SB_EFFECTIVE_MODE" ;; esac check_efi_layout_configs } regenerate_grub_cfg() { echo "[4/5] Rigenerazione grub.cfg..." if command -v update-grub >/dev/null 2>&1; then update-grub if [ -f /boot/grub/grub.cfg ]; then GRUB_CFG_TARGET="/boot/grub/grub.cfg" fi elif command -v grub-mkconfig >/dev/null 2>&1; then if [ -d /boot/grub2 ]; then grub-mkconfig -o /boot/grub2/grub.cfg GRUB_CFG_TARGET="/boot/grub2/grub.cfg" else grub-mkconfig -o /boot/grub/grub.cfg GRUB_CFG_TARGET="/boot/grub/grub.cfg" fi elif command -v grub2-mkconfig >/dev/null 2>&1; then grub2-mkconfig -o /boot/grub2/grub.cfg GRUB_CFG_TARGET="/boot/grub2/grub.cfg" else die "update-grub / grub-mkconfig / grub2-mkconfig non trovati." fi [ -n "$GRUB_CFG_TARGET" ] || warn "Impossibile determinare automaticamente il grub.cfg aggiornato." } verify_menu_entry() { echo "[5/5] Verifica voce GRUB..." local found=0 if [ -n "$GRUB_CFG_TARGET" ] && [ -f "$GRUB_CFG_TARGET" ]; then if grep -q "BastionGuard Recovery" "$GRUB_CFG_TARGET"; then echo " [OK] Voce BastionGuard Recovery presente in $GRUB_CFG_TARGET" found=1 fi fi if [ "$found" -eq 0 ]; then if [ -f /boot/grub/grub.cfg ] && grep -q "BastionGuard Recovery" /boot/grub/grub.cfg; then echo " [OK] Voce trovata in /boot/grub/grub.cfg" GRUB_CFG_TARGET="/boot/grub/grub.cfg" found=1 elif [ -f /boot/grub2/grub.cfg ] && grep -q "BastionGuard Recovery" /boot/grub2/grub.cfg; then echo " [OK] Voce trovata in /boot/grub2/grub.cfg" GRUB_CFG_TARGET="/boot/grub2/grub.cfg" found=1 fi fi if [ "$found" -eq 0 ]; then warn "Voce BastionGuard Recovery non trovata nel grub.cfg — verificare manualmente." fi } print_summary() { echo echo "========================================================" echo " Installazione completata" echo "========================================================" echo " Recovery dir : $RECOVERY_DIR" echo " Live dir : $LIVE_DIR" [ -n "$GRUB_CFG_TARGET" ] && echo " grub.cfg : $GRUB_CFG_TARGET" echo " SecureBoot mode : $SB_EFFECTIVE_MODE" echo " Custom backend : $CUSTOM_SIGN_BACKEND_EFFECTIVE" echo echo " Al prossimo avvio apparirà la voce:" echo " \"BastionGuard Recovery\"" echo echo " Comandi utili:" [ -n "$GRUB_CFG_TARGET" ] && echo " grep -n 'BastionGuard' $GRUB_CFG_TARGET" echo " ls -lh $RECOVERY_DIR" echo " ls -lh $LIVE_DIR" if [ "$SB_FIRMWARE_MODE" = "uefi" ] && [ -d "$EFI_BOOT_DIR" ]; then echo " ls -lh $EFI_BOOT_DIR" fi if [ "$SB_EFFECTIVE_MODE" = "shim" ] && [ -f "$MOK_DER" ]; then echo " mokutil --import $MOK_DER" fi if [ "$SB_EFFECTIVE_MODE" = "custom" ] && [ "$CUSTOM_SIGN_BACKEND_EFFECTIVE" = "sbctl" ]; then echo " sbctl verify $KERNEL_FILE" echo " sbctl verify $BOOTX64_EFI" echo " sbctl verify $GRUBX64_EFI" fi echo "========================================================" } main() { require_root echo "========================================================" echo " BastionGuard Recovery — Installazione voce GRUB" echo "========================================================" check_recovery_files install_grub_script check_secureboot_and_efi regenerate_grub_cfg verify_menu_entry print_summary } main "$@"