Add secure boot helper
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env bash
|
||||
# fleet-sb-enroll — enrol the fleet's custom Secure Boot keys (PK/KEK/db) from the
|
||||
# baked PUBLIC .auth files into firmware, via efivarfs.
|
||||
#
|
||||
# EL9 ships no efitools (no efi-updatevar), so this is the OS-side equivalent: it
|
||||
# prepends the EFI authenticated-variable attribute mask to each signed .auth
|
||||
# payload and writes it to /sys/firmware/efi/efivars in a single write(), in the
|
||||
# order db -> KEK -> PK (PK last leaves Setup Mode and turns Secure Boot on).
|
||||
#
|
||||
# Run as root over SSH on a workstation that is in Secure Boot SETUP MODE. Only
|
||||
# PUBLIC material is used (the .auth files carry certs + signatures, never the
|
||||
# PK/KEK/db private keys, which stay offline/HSM — see docs/secure-boot-keys.md).
|
||||
#
|
||||
# Usage:
|
||||
# fleet-sb-enroll --check # print SB state + which keys are baked; do nothing
|
||||
# fleet-sb-enroll # enrol db, KEK, PK (PK last) — requires Setup Mode
|
||||
# fleet-sb-enroll --force # attempt even if not in Setup Mode (User-Mode
|
||||
# # writes only succeed if each .auth is signed by
|
||||
# # the in-place parent key)
|
||||
#
|
||||
# NOTE: this is a deliberate trust-bootstrap action. In Setup Mode the firmware
|
||||
# accepts these writes unauthenticated, so only run it on a host you control, on a
|
||||
# trusted/isolated network. See docs/TODO.md #10 for the auto-enrol risk model.
|
||||
set -euo pipefail
|
||||
|
||||
KEYDIR=/usr/share/fleet-secureboot
|
||||
EFIVARS=/sys/firmware/efi/efivars
|
||||
GUID_GLOBAL=8be4df61-93ca-11d2-aa0d-00e098032b8c # PK, KEK, SetupMode
|
||||
GUID_DB=d719b2cb-3d3a-4596-a3bc-dad00e67656f # db, dbx
|
||||
# EFI var attributes: NON_VOLATILE|BOOTSERVICE_ACCESS|RUNTIME_ACCESS|
|
||||
# TIME_BASED_AUTHENTICATED_WRITE_ACCESS = 0x27, little-endian 4 bytes.
|
||||
ATTR=$'\x27\x00\x00\x00'
|
||||
|
||||
die() { echo "fleet-sb-enroll: $*" >&2; exit 1; }
|
||||
|
||||
[ "$(id -u)" = 0 ] || die "must run as root (use sudo)"
|
||||
[ -d "$EFIVARS" ] || die "efivarfs not present ($EFIVARS) — not a UEFI system / not booted via UEFI?"
|
||||
|
||||
setup_mode() { # echoes yes|no|unknown
|
||||
local f="$EFIVARS/SetupMode-$GUID_GLOBAL"
|
||||
[ -e "$f" ] || { echo unknown; return; }
|
||||
# 4 attribute bytes then 1 data byte: 1 = Setup Mode, 0 = User Mode.
|
||||
if [ "$(od -An -tu1 -j4 -N1 "$f" | tr -d ' ')" = 1 ]; then echo yes; else echo no; fi
|
||||
}
|
||||
|
||||
show_state() {
|
||||
echo "Secure Boot: $(mokutil --sb-state 2>/dev/null | tr '\n' ' ' || echo '?')"
|
||||
echo "Setup Mode: $(setup_mode)"
|
||||
echo "Baked keys ($KEYDIR):"
|
||||
local k
|
||||
for k in db KEK PK; do
|
||||
if [ -f "$KEYDIR/$k.auth" ]; then echo " $k.auth present"; else echo " $k.auth MISSING"; fi
|
||||
done
|
||||
}
|
||||
|
||||
enrol_one() { # $1=var name $2=guid $3=.auth path
|
||||
local f="$EFIVARS/$1-$2" tmp
|
||||
[ -f "$3" ] || die "missing $3"
|
||||
tmp=$(mktemp)
|
||||
printf '%s' "$ATTR" > "$tmp"
|
||||
cat "$3" >> "$tmp"
|
||||
# Existing EFI vars carry the immutable flag; clear it before overwriting.
|
||||
[ -e "$f" ] && chattr -i "$f" 2>/dev/null || true
|
||||
# efivarfs requires the whole variable in ONE write() — dd, single block.
|
||||
dd if="$tmp" of="$f" bs="$(stat -c%s "$tmp")" count=1 status=none \
|
||||
|| { rm -f "$tmp"; die "write to '$1' failed (in Setup Mode? .auth signed by the right parent key?)"; }
|
||||
rm -f "$tmp"
|
||||
echo " enrolled $1"
|
||||
}
|
||||
|
||||
FORCE=0
|
||||
case "${1:-}" in
|
||||
--check) show_state; exit 0 ;;
|
||||
--force) FORCE=1 ;;
|
||||
"") FORCE=0 ;;
|
||||
*) die "usage: fleet-sb-enroll [--check|--force]" ;;
|
||||
esac
|
||||
|
||||
# Require all three baked keys.
|
||||
for k in db KEK PK; do
|
||||
[ -f "$KEYDIR/$k.auth" ] || die "no baked keys at $KEYDIR/$k.auth — supply public PK/KEK/db .auth via the build (files/secureboot/keys/)"
|
||||
done
|
||||
|
||||
if [ "$(setup_mode)" != yes ] && [ "$FORCE" != 1 ]; then
|
||||
show_state
|
||||
die "not in Setup Mode — clear/reset Secure Boot keys in firmware first, or pass --force"
|
||||
fi
|
||||
|
||||
echo "Enrolling fleet Secure Boot keys (order: db -> KEK -> PK)..."
|
||||
enrol_one db "$GUID_DB" "$KEYDIR/db.auth"
|
||||
enrol_one KEK "$GUID_GLOBAL" "$KEYDIR/KEK.auth"
|
||||
enrol_one PK "$GUID_GLOBAL" "$KEYDIR/PK.auth" # PK last -> exits Setup Mode
|
||||
echo "Done."
|
||||
echo "Verify with: mokutil --db and mokutil --sb-state"
|
||||
Reference in New Issue
Block a user