When running './run.sh test:iso destroy', also delete the ISO file and its checksums. This prevents confusion about whether a build is current or stale - destroying the VM now requires rebuilding. Reference: User request 💘 Generated with Crush Assisted-by: GLM-5 via Crush <crush@charm.land>
1260 lines
41 KiB
Bash
Executable File
1260 lines
41 KiB
Bash
Executable File
#!/bin/bash
|
|
# KNEL-Football ISO Builder - Main Entry Point
|
|
# Orchestrates Docker-based build process and VM testing
|
|
# Copyright © 2026 Known Element Enterprises LLC
|
|
# License: GNU Affero General Public License v3.0 only
|
|
|
|
set -euo pipefail
|
|
|
|
# Configuration variables
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
readonly SCRIPT_DIR
|
|
readonly DOCKER_IMAGE="knel-football-dev:latest"
|
|
readonly OUTPUT_DIR="${SCRIPT_DIR}/output"
|
|
readonly BUILD_DIR="${SCRIPT_DIR}/tmp"
|
|
readonly BUILD_LOG="/tmp/knel-iso-build.log"
|
|
|
|
# VM Testing Configuration (system libvirt for virt-manager visibility, /tmp for no sudo)
|
|
readonly ISO_PATH="${SCRIPT_DIR}/output/knel-football-secure.iso"
|
|
readonly VM_NAME="knel-football-test"
|
|
readonly VM_RAM="2048"
|
|
readonly VM_CPUS="2"
|
|
readonly VM_DISK_SIZE="10"
|
|
readonly LIBVIRT_URI="qemu:///system"
|
|
VM_DISK_PATH="/tmp/${VM_NAME}.qcow2"
|
|
readonly VM_DISK_PATH
|
|
VM_ISO_PATH="/tmp/${VM_NAME}.iso"
|
|
readonly VM_ISO_PATH
|
|
|
|
# Colors for output
|
|
readonly RED='\033[0;31m'
|
|
readonly GREEN='\033[0;32m'
|
|
readonly YELLOW='\033[1;33m'
|
|
readonly NC='\033[0m' # No Color
|
|
|
|
# Logging functions
|
|
log_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
|
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
|
|
|
# Create output and build directories if they don't exist
|
|
mkdir -p "${OUTPUT_DIR}" "${BUILD_DIR}"
|
|
|
|
# ============================================================================
|
|
# HOST FDE CHECK (MANDATORY)
|
|
# ============================================================================
|
|
|
|
# Check if host system has full disk encryption enabled
|
|
# This is MANDATORY - building or testing a secure OS on an unencrypted host
|
|
# defeats the entire security model
|
|
check_host_fde() {
|
|
log_info "Checking host system for Full Disk Encryption..."
|
|
|
|
local has_luks=false
|
|
local encrypted_root=false
|
|
|
|
# Method 1: Check for LUKS devices via lsblk
|
|
if lsblk -o TYPE,FSTYPE 2>/dev/null | grep -q "crypt"; then
|
|
has_luks=true
|
|
log_info "Found LUKS encrypted partitions"
|
|
fi
|
|
|
|
# Method 2: Check if root filesystem is on a dm-crypt device
|
|
if [[ -e /dev/mapper/root ]] || [[ -e /dev/mapper/rootfs ]]; then
|
|
encrypted_root=true
|
|
log_info "Root filesystem appears to be on encrypted device"
|
|
fi
|
|
|
|
# Method 3: Check /etc/crypttab for configured encrypted partitions
|
|
if [[ -f /etc/crypttab ]] && grep -qE "^[^#]" /etc/crypttab 2>/dev/null; then
|
|
has_luks=true
|
|
log_info "Found encrypted partitions in /etc/crypttab"
|
|
fi
|
|
|
|
# Method 4: Check for dm-crypt devices in /sys/block
|
|
if find /sys/block -maxdepth 1 -name 'dm-*' -print -quit 2>/dev/null | grep -q .; then
|
|
for dm_dev in /sys/block/dm-*; do
|
|
if [[ -f "${dm_dev}/dm/name" ]]; then
|
|
local dm_name
|
|
dm_name=$(cat "${dm_dev}/dm/name" 2>/dev/null)
|
|
# Check if this is a LUKS device
|
|
if [[ -f "${dm_dev}/dm/uuid" ]] && grep -qi "CRYPT-LUKS" "${dm_dev}/dm/uuid" 2>/dev/null; then
|
|
has_luks=true
|
|
log_info "Found LUKS device: ${dm_name}"
|
|
fi
|
|
fi
|
|
done
|
|
fi
|
|
|
|
# Method 5: Check root mount point for encryption
|
|
local root_device
|
|
root_device=$(findmnt -n -o SOURCE / 2>/dev/null || echo "")
|
|
if [[ "$root_device" == /dev/mapper/* ]] || [[ "$root_device" == *"crypt"* ]]; then
|
|
encrypted_root=true
|
|
log_info "Root filesystem is on encrypted device: $root_device"
|
|
fi
|
|
|
|
# Require at least one indicator of FDE
|
|
if [[ "$has_luks" == "true" || "$encrypted_root" == "true" ]]; then
|
|
log_info "Host FDE check PASSED"
|
|
return 0
|
|
fi
|
|
|
|
# FDE not detected - this is a FATAL error
|
|
log_error "============================================================"
|
|
log_error "SECURITY REQUIREMENT VIOLATION"
|
|
log_error "============================================================"
|
|
log_error "Host system does NOT have Full Disk Encryption enabled."
|
|
log_error ""
|
|
log_error "Building or testing KNEL-Football Secure OS requires the"
|
|
log_error "host system to be encrypted with LUKS. An unencrypted host"
|
|
log_error "defeats the entire security model."
|
|
log_error ""
|
|
log_error "To enable FDE on Debian/Ubuntu:"
|
|
log_error " 1. Backup all data"
|
|
log_error " 2. Reinstall with 'Guided - use entire disk and set up encrypted LVM'"
|
|
log_error " 3. Or use: https://github.com/The Firefoxlyer/encrypt-existing-debian"
|
|
log_error ""
|
|
log_error "This check is MANDATORY and cannot be bypassed."
|
|
log_error "============================================================"
|
|
return 1
|
|
}
|
|
|
|
# ============================================================================
|
|
# VM TESTING FUNCTIONS (merged from test-iso.sh)
|
|
# ============================================================================
|
|
|
|
# Check VM testing prerequisites
|
|
vm_check_prerequisites() {
|
|
log_info "Checking VM testing prerequisites..."
|
|
|
|
# Check for virsh command
|
|
if ! command -v virsh &> /dev/null; then
|
|
log_error "virsh command not found"
|
|
log_error "Install libvirt: sudo apt install libvirt-clients libvirt-daemon-system qemu-system-x86"
|
|
return 1
|
|
fi
|
|
|
|
# Check system libvirt access (required for virt-manager visibility)
|
|
if ! virsh -c "$LIBVIRT_URI" list &> /dev/null; then
|
|
log_error "Cannot connect to system libvirt ($LIBVIRT_URI)"
|
|
log_error "Ensure libvirtd is running and you have access"
|
|
log_error "Try: sudo usermod -aG libvirt \$USER && logout/login"
|
|
return 1
|
|
fi
|
|
|
|
# Check for qemu-img command (required for disk creation)
|
|
if ! command -v qemu-img &> /dev/null; then
|
|
log_error "qemu-img command not found"
|
|
log_error "Install qemu: sudo apt install qemu-utils"
|
|
return 1
|
|
fi
|
|
|
|
# Check ISO exists
|
|
if [[ ! -f "$ISO_PATH" ]]; then
|
|
log_error "ISO not found at: $ISO_PATH"
|
|
log_error "Build the ISO first: ./run.sh iso"
|
|
return 1
|
|
fi
|
|
|
|
# Check if libvirt images directory exists
|
|
if [[ ! -d "/var/lib/libvirt/images" ]]; then
|
|
log_error "Libvirt images directory not found"
|
|
log_error "Ensure libvirt is properly installed: sudo apt install libvirt-daemon-system"
|
|
return 1
|
|
fi
|
|
|
|
log_info "All prerequisites satisfied"
|
|
return 0
|
|
}
|
|
|
|
# Create and start VM using virsh define (virt-install requires storage pools)
|
|
vm_create() {
|
|
log_info "Creating VM: $VM_NAME (libvirt: $LIBVIRT_URI)"
|
|
|
|
# Destroy existing VM if present
|
|
virsh -c "$LIBVIRT_URI" destroy "$VM_NAME" 2>/dev/null || true
|
|
virsh -c "$LIBVIRT_URI" undefine "$VM_NAME" --nvram 2>/dev/null || true
|
|
|
|
# Ensure libvirt images directory exists
|
|
mkdir -p "$(dirname "$VM_ISO_PATH")"
|
|
|
|
# Copy ISO to user storage (no root required for session libvirt)
|
|
log_info "Copying ISO to libvirt storage..."
|
|
if ! cp -f "$ISO_PATH" "$VM_ISO_PATH"; then
|
|
log_error "Failed to copy ISO"
|
|
return 1
|
|
fi
|
|
|
|
# Find UEFI firmware with Secure Boot support
|
|
local uefi_code=""
|
|
local uefi_vars=""
|
|
for fw_dir in /usr/share/OVMF /usr/share/qemu; do
|
|
if [[ -f "$fw_dir/OVMF_CODE_4M.secboot.fd" ]]; then
|
|
uefi_code="$fw_dir/OVMF_CODE_4M.secboot.fd"
|
|
uefi_vars="$fw_dir/OVMF_VARS_4M.fd"
|
|
break
|
|
elif [[ -f "$fw_dir/OVMF_CODE_4M.fd" ]]; then
|
|
uefi_code="$fw_dir/OVMF_CODE_4M.fd"
|
|
uefi_vars="$fw_dir/OVMF_VARS_4M.fd"
|
|
break
|
|
elif [[ -f "$fw_dir/OVMF_CODE.fd" ]]; then
|
|
uefi_code="$fw_dir/OVMF_CODE.fd"
|
|
uefi_vars="$fw_dir/OVMF_VARS.fd"
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [[ -z "$uefi_code" || ! -f "$uefi_code" ]]; then
|
|
log_error "UEFI firmware (OVMF) not found"
|
|
log_error "Install required: sudo apt install ovmf"
|
|
return 1
|
|
fi
|
|
|
|
# Determine if Secure Boot is available
|
|
local secure_boot="no"
|
|
if [[ "$uefi_code" == *"secboot"* ]]; then
|
|
secure_boot="yes"
|
|
log_info "Using UEFI with Secure Boot: $uefi_code"
|
|
else
|
|
log_warn "Using UEFI WITHOUT Secure Boot: $uefi_code"
|
|
fi
|
|
|
|
# Pre-create disk image (no root required for session libvirt)
|
|
log_info "Creating disk image: $VM_DISK_PATH"
|
|
rm -f "$VM_DISK_PATH" 2>/dev/null || true
|
|
mkdir -p "$(dirname "$VM_DISK_PATH")"
|
|
if ! qemu-img create -f qcow2 "$VM_DISK_PATH" "${VM_DISK_SIZE}G"; then
|
|
log_error "Failed to create disk image"
|
|
return 1
|
|
fi
|
|
|
|
# Use XML template for VM definition
|
|
local template="${SCRIPT_DIR}/vm/template.xml"
|
|
if [[ ! -f "$template" ]]; then
|
|
log_error "VM template not found: $template"
|
|
return 1
|
|
fi
|
|
|
|
# Generate UUID
|
|
local vm_uuid
|
|
vm_uuid=$(cat /proc/sys/kernel/random/uuid)
|
|
|
|
# Create VM XML from template
|
|
local vm_xml="/tmp/${VM_NAME}.xml"
|
|
sed -e "s|@VM_NAME@|${VM_NAME}|g" \
|
|
-e "s|@VM_UUID@|${vm_uuid}|g" \
|
|
-e "s|@VM_RAM@|${VM_RAM}|g" \
|
|
-e "s|@VM_CPUS@|${VM_CPUS}|g" \
|
|
-e "s|@SECURE_BOOT@|${secure_boot}|g" \
|
|
-e "s|@UEFI_CODE@|${uefi_code}|g" \
|
|
-e "s|@UEFI_VARS_TEMPLATE@|${uefi_vars}|g" \
|
|
-e "s|@VM_DISK@|${VM_DISK_PATH}|g" \
|
|
-e "s|@ISO_PATH@|${VM_ISO_PATH}|g" \
|
|
"$template" > "$vm_xml"
|
|
|
|
log_info "Defining VM from XML..."
|
|
|
|
# Define the VM
|
|
if ! virsh -c "$LIBVIRT_URI" define "$vm_xml"; then
|
|
log_error "Failed to define VM from XML"
|
|
cat "$vm_xml"
|
|
return 1
|
|
fi
|
|
|
|
# Start the VM
|
|
log_info "Starting VM..."
|
|
if ! virsh -c "$LIBVIRT_URI" start "$VM_NAME"; then
|
|
log_error "Failed to start VM"
|
|
return 1
|
|
fi
|
|
|
|
# Verify VM is running
|
|
sleep 2
|
|
if virsh -c "$LIBVIRT_URI" domstate "$VM_NAME" 2>/dev/null | grep -q "running"; then
|
|
log_info "VM created and STARTED successfully"
|
|
else
|
|
log_warn "VM created but may not be running - check virt-manager"
|
|
fi
|
|
|
|
# Get VNC display info
|
|
local vnc_display
|
|
vnc_display=$(virsh -c "$LIBVIRT_URI" vncdisplay "$VM_NAME" 2>/dev/null || echo "unknown")
|
|
|
|
log_info "VNC display: $vnc_display"
|
|
log_info ""
|
|
log_info "Open virt-manager - VM '$VM_NAME' should be visible under QEMU/KVM"
|
|
}
|
|
|
|
# Connect to VM console
|
|
vm_console() {
|
|
log_info "Connecting to VM console..."
|
|
virsh -c "$LIBVIRT_URI" console "$VM_NAME"
|
|
}
|
|
|
|
# Get VM status
|
|
vm_status() {
|
|
log_info "VM Status for: $VM_NAME"
|
|
virsh -c "$LIBVIRT_URI" dominfo "$VM_NAME" 2>/dev/null || log_error "VM not running"
|
|
}
|
|
|
|
# Check if VM is running
|
|
vm_is_running() {
|
|
if virsh -c "$LIBVIRT_URI" domstate "$VM_NAME" 2>/dev/null | grep -q "running"; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
# Capture boot screenshot
|
|
vm_capture_screen() {
|
|
local output_dir="${SCRIPT_DIR}/tmp/vm-screenshots"
|
|
mkdir -p "$output_dir"
|
|
|
|
log_info "Capturing boot screen..."
|
|
virsh -c "$LIBVIRT_URI" screenshot "$VM_NAME" "${output_dir}/boot-screen.ppm" 2>/dev/null || {
|
|
log_warn "Could not capture screenshot"
|
|
}
|
|
}
|
|
|
|
# Destroy VM and cleanup
|
|
vm_destroy() {
|
|
log_info "Destroying VM: $VM_NAME"
|
|
virsh -c "$LIBVIRT_URI" destroy "$VM_NAME" 2>/dev/null || true
|
|
virsh -c "$LIBVIRT_URI" undefine "$VM_NAME" --nvram 2>/dev/null || true
|
|
rm -f "$VM_DISK_PATH" "$VM_ISO_PATH" "/tmp/${VM_NAME}.xml" "/tmp/${VM_NAME}_VARS.fd"
|
|
|
|
# Also delete ISO to avoid confusion over build status
|
|
if [[ -f "$ISO_PATH" ]]; then
|
|
log_info "Removing ISO: $ISO_PATH"
|
|
rm -f "$ISO_PATH" "${ISO_PATH}.md5" "${ISO_PATH}.sha256"
|
|
fi
|
|
|
|
log_info "Cleanup complete"
|
|
}
|
|
|
|
# Run automated boot test
|
|
vm_boot_test() {
|
|
log_info "Running automated boot test..."
|
|
|
|
if ! vm_check_prerequisites; then
|
|
return 1
|
|
fi
|
|
|
|
vm_create
|
|
|
|
log_info "Waiting for VM to boot (30 seconds)..."
|
|
sleep 30
|
|
|
|
if vm_is_running; then
|
|
log_info "VM is running - boot test PASSED"
|
|
vm_status
|
|
vm_capture_screen
|
|
return 0
|
|
else
|
|
log_error "VM not running - boot test FAILED"
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# Test Secure Boot
|
|
vm_test_secure_boot() {
|
|
log_info "Testing Secure Boot..."
|
|
|
|
if ! vm_is_running; then
|
|
log_error "VM not running, start it first"
|
|
return 1
|
|
fi
|
|
|
|
log_info "Secure Boot verification requires manual console inspection"
|
|
log_info "Use: ./run.sh test:iso console"
|
|
log_info "Then check: dmesg | grep -i secure"
|
|
}
|
|
|
|
# Test FDE passphrase prompt
|
|
vm_test_fde() {
|
|
log_info "Testing FDE passphrase prompt..."
|
|
|
|
if ! vm_is_running; then
|
|
log_error "VM not running, start it first"
|
|
return 1
|
|
fi
|
|
|
|
log_info "FDE prompt verification requires manual console inspection"
|
|
log_info "Use: ./run.sh test:iso console"
|
|
log_info "Watch for 'Please unlock disk' prompt during boot"
|
|
}
|
|
|
|
# ============================================================================
|
|
# BUILD MONITOR FUNCTION (merged from monitor-build.sh)
|
|
# ============================================================================
|
|
|
|
monitor_build() {
|
|
local check_interval="${1:-180}"
|
|
|
|
echo "=== ISO Build Monitor ==="
|
|
echo "Started: $(date)"
|
|
echo "Checking every ${check_interval}s"
|
|
echo "Log file: $BUILD_LOG"
|
|
echo ""
|
|
|
|
while true; do
|
|
if [ -f "$BUILD_LOG" ]; then
|
|
local lines
|
|
lines=$(wc -l < "$BUILD_LOG")
|
|
local last_stage
|
|
last_stage=$(grep -E "^\[.*\] lb (bootstrap|chroot|installer|binary|source)" "$BUILD_LOG" 2>/dev/null | tail -1)
|
|
local errors
|
|
errors=$(grep -ic "error\|failed\|fatal" "$BUILD_LOG" 2>/dev/null || echo "0")
|
|
|
|
echo "[$(date '+%H:%M:%S')] Lines: $lines | Errors: $errors"
|
|
[ -n "$last_stage" ] && echo " Stage: $last_stage"
|
|
|
|
# Check if build completed
|
|
if grep -q "ISO build completed" "$BUILD_LOG" 2>/dev/null; then
|
|
echo ""
|
|
echo "=== BUILD COMPLETED ==="
|
|
echo "Finished: $(date)"
|
|
ls -lh "${OUTPUT_DIR}"/*.iso 2>/dev/null || echo "No ISO found in output/"
|
|
break
|
|
fi
|
|
|
|
# Check if build failed
|
|
if grep -q "ISO build failed" "$BUILD_LOG" 2>/dev/null; then
|
|
echo ""
|
|
echo "=== BUILD FAILED ==="
|
|
echo "Check log: $BUILD_LOG"
|
|
tail -20 "$BUILD_LOG"
|
|
break
|
|
fi
|
|
else
|
|
echo "[$(date '+%H:%M:%S')] Waiting for build log..."
|
|
fi
|
|
|
|
sleep "$check_interval"
|
|
done
|
|
}
|
|
|
|
# ============================================================================
|
|
# SECURE BOOT FUNCTIONS
|
|
# ============================================================================
|
|
|
|
# Secure Boot Configuration
|
|
readonly SB_KEY_DIR="${BUILD_DIR}/secureboot-keys"
|
|
readonly SB_KEYS_SRC="${SCRIPT_DIR}/config/secureboot-keys"
|
|
|
|
# Generate Secure Boot keys (PK, KEK, db)
|
|
# Returns: 0 on success, 1 on failure
|
|
sb_generate_keys() {
|
|
log_info "Generating Secure Boot keys..."
|
|
|
|
mkdir -p "${SB_KEY_DIR}"
|
|
|
|
# Check for existing keys in source
|
|
if [[ -d "${SB_KEYS_SRC}" ]]; then
|
|
log_info "Using existing keys from ${SB_KEYS_SRC}"
|
|
cp -r "${SB_KEYS_SRC}"/* "${SB_KEY_DIR}/"
|
|
return 0
|
|
fi
|
|
|
|
# Generate Platform Key (PK) - Root of trust
|
|
log_info "Generating Platform Key (PK)..."
|
|
openssl req -new -x509 -newkey rsa:4096 -sha256 -days 3650 \
|
|
-nodes -subj "/CN=KNEL-Football PK/" \
|
|
-keyout "${SB_KEY_DIR}/PK.key" \
|
|
-out "${SB_KEY_DIR}/PK.crt" 2>/dev/null
|
|
|
|
# Generate Key Exchange Key (KEK)
|
|
log_info "Generating Key Exchange Key (KEK)..."
|
|
openssl req -new -x509 -newkey rsa:4096 -sha256 -days 3650 \
|
|
-nodes -subj "/CN=KNEL-Football KEK/" \
|
|
-keyout "${SB_KEY_DIR}/KEK.key" \
|
|
-out "${SB_KEY_DIR}/KEK.crt" 2>/dev/null
|
|
|
|
# Generate Signature Database Key (db)
|
|
log_info "Generating Signature Database Key (db)..."
|
|
openssl req -new -x509 -newkey rsa:4096 -sha256 -days 3650 \
|
|
-nodes -subj "/CN=KNEL-Football db/" \
|
|
-keyout "${SB_KEY_DIR}/db.key" \
|
|
-out "${SB_KEY_DIR}/db.crt" 2>/dev/null
|
|
|
|
# Verify all keys were created
|
|
for key in PK KEK db; do
|
|
if [[ ! -f "${SB_KEY_DIR}/${key}.key" ]] || [[ ! -f "${SB_KEY_DIR}/${key}.crt" ]]; then
|
|
log_error "Failed to generate ${key} key"
|
|
return 1
|
|
fi
|
|
done
|
|
|
|
log_info "Secure Boot keys generated successfully"
|
|
return 0
|
|
}
|
|
|
|
# Create EFI Signature List (ESL) from certificate
|
|
# Args: $1 = key name (PK, KEK, or db)
|
|
sb_create_esl() {
|
|
local key_name="$1"
|
|
local crt_file="${SB_KEY_DIR}/${key_name}.crt"
|
|
local esl_file="${SB_KEY_DIR}/${key_name}.esl"
|
|
|
|
if [[ ! -f "$crt_file" ]]; then
|
|
log_error "Certificate not found: $crt_file"
|
|
return 1
|
|
fi
|
|
|
|
log_info "Creating ESL for ${key_name}..."
|
|
cert-to-efi-sig-list -g "$(uuidgen)" "$crt_file" "$esl_file"
|
|
|
|
if [[ ! -f "$esl_file" ]]; then
|
|
log_error "Failed to create ESL for ${key_name}"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Sign ESL file to create .auth file for UEFI enrollment
|
|
# Args: $1 = key name to sign, $2 = signing key name
|
|
sb_sign_esl() {
|
|
local target_key="$1"
|
|
local signing_key="$2"
|
|
local esl_file="${SB_KEY_DIR}/${target_key}.esl"
|
|
local auth_file="${SB_KEY_DIR}/${target_key}.auth"
|
|
local sign_key="${SB_KEY_DIR}/${signing_key}.key"
|
|
local sign_crt="${SB_KEY_DIR}/${signing_key}.crt"
|
|
|
|
if [[ ! -f "$esl_file" ]]; then
|
|
log_error "ESL file not found: $esl_file"
|
|
return 1
|
|
fi
|
|
|
|
if [[ ! -f "$sign_key" ]] || [[ ! -f "$sign_crt" ]]; then
|
|
log_error "Signing key not found: ${signing_key}"
|
|
return 1
|
|
fi
|
|
|
|
log_info "Signing ${target_key}.esl with ${signing_key}..."
|
|
sign-efi-sig-list -t "$(date +'%Y-%m-%d %H:%M:%S')" \
|
|
-k "$sign_key" -c "$sign_crt" \
|
|
"$target_key" "$esl_file" "$auth_file"
|
|
|
|
if [[ ! -f "$auth_file" ]]; then
|
|
log_error "Failed to create auth file for ${target_key}"
|
|
return 1
|
|
fi
|
|
|
|
return 0
|
|
}
|
|
|
|
# Build Unified Kernel Image (UKI)
|
|
# Args: $1 = output directory containing chroot/binary
|
|
uki_build() {
|
|
local build_dir="$1"
|
|
local kernel_version
|
|
local uki_output="${build_dir}/binary/boot/efi/EFI/BOOT/BOOTX64.EFI"
|
|
|
|
log_info "Building Unified Kernel Image (UKI)..."
|
|
|
|
# Find kernel version
|
|
kernel_version=$(find "${build_dir}/chroot/boot" -maxdepth 1 -name 'vmlinuz-*' -print -quit 2>/dev/null | sed 's/.*vmlinuz-//')
|
|
if [[ -z "$kernel_version" ]]; then
|
|
log_error "Kernel not found in chroot"
|
|
return 1
|
|
fi
|
|
|
|
log_info "Kernel version: ${kernel_version}"
|
|
|
|
local kernel="${build_dir}/chroot/boot/vmlinuz-${kernel_version}"
|
|
local initrd="${build_dir}/chroot/boot/initrd.img-${kernel_version}"
|
|
|
|
if [[ ! -f "$kernel" ]]; then
|
|
log_error "Kernel not found: $kernel"
|
|
return 1
|
|
fi
|
|
|
|
if [[ ! -f "$initrd" ]]; then
|
|
log_error "Initrd not found: $initrd"
|
|
return 1
|
|
fi
|
|
|
|
# Create output directory
|
|
mkdir -p "$(dirname "$uki_output")"
|
|
|
|
# Build UKI using ukify (systemd) or manual objcopy
|
|
if command -v ukify &>/dev/null; then
|
|
log_info "Building UKI with ukify..."
|
|
ukify build \
|
|
--linux "$kernel" \
|
|
--initrd "$initrd" \
|
|
--cmdline "quiet splash" \
|
|
--output "$uki_output" \
|
|
--efi-arch x64
|
|
else
|
|
log_info "Building UKI with objcopy (manual method)..."
|
|
# Manual UKI construction using objcopy
|
|
local stub="${build_dir}/chroot/lib/systemd/boot/efi/linuxx64.efi.stub"
|
|
if [[ ! -f "$stub" ]]; then
|
|
stub="${build_dir}/chroot/usr/lib/systemd/boot/efi/linuxx64.efi.stub"
|
|
fi
|
|
if [[ ! -f "$stub" ]]; then
|
|
log_error "EFI stub not found - install systemd-boot"
|
|
return 1
|
|
fi
|
|
|
|
# Create cmdline file
|
|
local cmdline_file="${build_dir}/cmdline.txt"
|
|
echo "quiet splash" > "$cmdline_file"
|
|
|
|
# Build UKI with objcopy
|
|
objcopy \
|
|
--add-section .osrel="${build_dir}/chroot/etc/os-release" --change-section-vma .osrel=0x20000 \
|
|
--add-section .cmdline="$cmdline_file" --change-section-vma .cmdline=0x30000 \
|
|
--add-section .linux="$kernel" --change-section-vma .linux=0x40000 \
|
|
--add-section .initrd="$initrd" --change-section-vma .initrd=0x500000 \
|
|
"$stub" "$uki_output"
|
|
fi
|
|
|
|
if [[ ! -f "$uki_output" ]]; then
|
|
log_error "Failed to build UKI"
|
|
return 1
|
|
fi
|
|
|
|
log_info "UKI built successfully: $uki_output"
|
|
return 0
|
|
}
|
|
|
|
# Sign UKI with db key
|
|
# Args: $1 = path to UKI file
|
|
uki_sign() {
|
|
local uki_file="$1"
|
|
local db_key="${SB_KEY_DIR}/db.key"
|
|
local db_crt="${SB_KEY_DIR}/db.crt"
|
|
|
|
if [[ ! -f "$uki_file" ]]; then
|
|
log_error "UKI not found: $uki_file"
|
|
return 1
|
|
fi
|
|
|
|
if [[ ! -f "$db_key" ]] || [[ ! -f "$db_crt" ]]; then
|
|
log_error "db key not found - run sb_generate_keys first"
|
|
return 1
|
|
fi
|
|
|
|
log_info "Signing UKI with db key..."
|
|
sbsign --key "$db_key" --cert "$db_crt" --output "$uki_file" "$uki_file"
|
|
|
|
# Verify signature
|
|
if sbverify "$uki_file" 2>/dev/null | grep -q "Signature verification OK"; then
|
|
log_info "UKI signed successfully"
|
|
return 0
|
|
else
|
|
log_warn "UKI signed but verification uncertain"
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
# Full Secure Boot setup - generate keys, create ESL, sign ESL, prepare for enrollment
|
|
secureboot_setup() {
|
|
log_info "Setting up Secure Boot..."
|
|
|
|
# Generate keys
|
|
if ! sb_generate_keys; then
|
|
log_error "Failed to generate Secure Boot keys"
|
|
return 1
|
|
fi
|
|
|
|
# Create ESL files
|
|
for key in PK KEK db; do
|
|
if ! sb_create_esl "$key"; then
|
|
log_error "Failed to create ESL for $key"
|
|
return 1
|
|
fi
|
|
done
|
|
|
|
# Sign ESL files to create .auth files
|
|
# PK is self-signed
|
|
if ! sb_sign_esl "PK" "PK"; then
|
|
log_error "Failed to sign PK"
|
|
return 1
|
|
fi
|
|
|
|
# KEK is signed by PK
|
|
if ! sb_sign_esl "KEK" "PK"; then
|
|
log_error "Failed to sign KEK"
|
|
return 1
|
|
fi
|
|
|
|
# db is signed by KEK
|
|
if ! sb_sign_esl "db" "KEK"; then
|
|
log_error "Failed to sign db"
|
|
return 1
|
|
fi
|
|
|
|
log_info "Secure Boot setup complete"
|
|
log_info "Keys location: ${SB_KEY_DIR}"
|
|
log_info "Auth files ready for UEFI enrollment:"
|
|
ls -la "${SB_KEY_DIR}"/*.auth 2>/dev/null || true
|
|
|
|
return 0
|
|
}
|
|
|
|
# Get Secure Boot build script for embedding in Docker
|
|
# This outputs the Secure Boot functions as a string for embedding in Docker bash -c
|
|
get_secureboot_script() {
|
|
cat << 'SECUREBOOT_SCRIPT'
|
|
# Secure Boot functions for Docker build
|
|
sb_docker_setup() {
|
|
local key_dir="/tmp/secureboot-keys"
|
|
mkdir -p "$key_dir"
|
|
|
|
echo "[SecureBoot] Generating keys..."
|
|
|
|
# Generate PK
|
|
openssl req -new -x509 -newkey rsa:4096 -sha256 -days 3650 \
|
|
-nodes -subj "/CN=KNEL-Football PK/" \
|
|
-keyout "${key_dir}/PK.key" \
|
|
-out "${key_dir}/PK.crt" 2>/dev/null || return 1
|
|
|
|
# Generate KEK
|
|
openssl req -new -x509 -newkey rsa:4096 -sha256 -days 3650 \
|
|
-nodes -subj "/CN=KNEL-Football KEK/" \
|
|
-keyout "${key_dir}/KEK.key" \
|
|
-out "${key_dir}/KEK.crt" 2>/dev/null || return 1
|
|
|
|
# Generate db
|
|
openssl req -new -x509 -newkey rsa:4096 -sha256 -days 3650 \
|
|
-nodes -subj "/CN=KNEL-Football db/" \
|
|
-keyout "${key_dir}/db.key" \
|
|
-out "${key_dir}/db.crt" 2>/dev/null || return 1
|
|
|
|
# Create ESL files
|
|
echo "[SecureBoot] Creating ESL files..."
|
|
for key in PK KEK db; do
|
|
cert-to-efi-sig-list -g "$(uuidgen)" \
|
|
"${key_dir}/${key}.crt" \
|
|
"${key_dir}/${key}.esl" || return 1
|
|
done
|
|
|
|
# Create auth files
|
|
echo "[SecureBoot] Creating auth files..."
|
|
sign-efi-sig-list -t "$(date +'%Y-%m-%d %H:%M:%S')" \
|
|
-k "${key_dir}/PK.key" -c "${key_dir}/PK.crt" \
|
|
PK "${key_dir}/PK.esl" "${key_dir}/PK.auth" || return 1
|
|
|
|
sign-efi-sig-list -t "$(date +'%Y-%m-%d %H:%M:%S')" \
|
|
-k "${key_dir}/PK.key" -c "${key_dir}/PK.crt" \
|
|
KEK "${key_dir}/KEK.esl" "${key_dir}/KEK.auth" || return 1
|
|
|
|
sign-efi-sig-list -t "$(date +'%Y-%m-%d %H:%M:%S')" \
|
|
-k "${key_dir}/KEK.key" -c "${key_dir}/KEK.crt" \
|
|
db "${key_dir}/db.esl" "${key_dir}/db.auth" || return 1
|
|
|
|
echo "[SecureBoot] Keys generated successfully"
|
|
echo "$key_dir"
|
|
}
|
|
|
|
sb_docker_build_uki() {
|
|
local build_dir="$1"
|
|
local key_dir="$2"
|
|
|
|
echo "[SecureBoot] Building UKI..."
|
|
|
|
# Find kernel
|
|
local kernel=$(ls "${build_dir}/chroot/boot/vmlinuz-"* 2>/dev/null | head -1)
|
|
if [[ -z "$kernel" ]]; then
|
|
echo "[SecureBoot] ERROR: Kernel not found"
|
|
return 1
|
|
fi
|
|
local kver=$(echo "$kernel" | sed 's/.*vmlinuz-//')
|
|
local initrd="${build_dir}/chroot/boot/initrd.img-${kver}"
|
|
|
|
if [[ ! -f "$initrd" ]]; then
|
|
echo "[SecureBoot] ERROR: Initrd not found"
|
|
return 1
|
|
fi
|
|
|
|
# Find EFI stub
|
|
local stub="${build_dir}/chroot/usr/lib/systemd/boot/efi/linuxx64.efi.stub"
|
|
if [[ ! -f "$stub" ]]; then
|
|
stub="${build_dir}/chroot/lib/systemd/boot/efi/linuxx64.efi.stub"
|
|
fi
|
|
if [[ ! -f "$stub" ]]; then
|
|
echo "[SecureBoot] ERROR: EFI stub not found"
|
|
return 1
|
|
fi
|
|
|
|
# Create output directory
|
|
local uki_dir="${build_dir}/binary/boot/efi/EFI/BOOT"
|
|
mkdir -p "$uki_dir"
|
|
|
|
local uki_file="${uki_dir}/BOOTX64.EFI"
|
|
local cmdline="${build_dir}/cmdline.txt"
|
|
|
|
# Create cmdline
|
|
echo "quiet splash lockdown=confidentiality" > "$cmdline"
|
|
|
|
# Build UKI
|
|
echo "[SecureBoot] Bundling kernel+initrd+cmdline..."
|
|
objcopy \
|
|
--add-section .osrel="${build_dir}/chroot/etc/os-release" --change-section-vma .osrel=0x20000 \
|
|
--add-section .cmdline="$cmdline" --change-section-vma .cmdline=0x30000 \
|
|
--add-section .linux="$kernel" --change-section-vma .linux=0x40000 \
|
|
--add-section .initrd="$initrd" --change-section-vma .initrd=0x500000 \
|
|
"$stub" "$uki_file" || return 1
|
|
|
|
# Sign UKI
|
|
echo "[SecureBoot] Signing UKI..."
|
|
sbsign --key "${key_dir}/db.key" --cert "${key_dir}/db.crt" \
|
|
--output "$uki_file" "$uki_file" || return 1
|
|
|
|
# Verify
|
|
if sbverify "$uki_file" 2>&1 | grep -q "OK"; then
|
|
echo "[SecureBoot] UKI signed and verified: $uki_file"
|
|
return 0
|
|
else
|
|
echo "[SecureBoot] WARNING: UKI verification uncertain"
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
sb_docker_copy_keys_to_binary() {
|
|
local key_dir="$1"
|
|
local build_dir="$2"
|
|
|
|
echo "[SecureBoot] Copying keys to ISO for installation..."
|
|
|
|
# Create directory for keys in the ISO
|
|
local keys_dest="${build_dir}/binary/secureboot"
|
|
mkdir -p "$keys_dest"
|
|
|
|
# Copy auth files for enrollment during installation
|
|
cp "${key_dir}"/*.auth "$keys_dest/" 2>/dev/null || true
|
|
|
|
# Copy certificates for verification
|
|
cp "${key_dir}"/*.crt "$keys_dest/" 2>/dev/null || true
|
|
|
|
echo "[SecureBoot] Keys copied to /secureboot/ on ISO"
|
|
}
|
|
SECUREBOOT_SCRIPT
|
|
}
|
|
|
|
# ============================================================================
|
|
# USAGE AND MAIN
|
|
# ============================================================================
|
|
|
|
usage() {
|
|
cat <<EOF
|
|
KNEL-Football ISO Builder - Main Entry Point
|
|
|
|
Usage: $0 <command> [args]
|
|
|
|
Build Commands:
|
|
build Build Docker image
|
|
iso Build ISO (60-90 minutes)
|
|
monitor [secs] Monitor build progress (default: check every 180s)
|
|
clean Clean build artifacts
|
|
|
|
Test Commands:
|
|
test Run all tests
|
|
test:unit Run unit tests only
|
|
test:integration Run integration tests only
|
|
test:security Run security tests only
|
|
test:system Run system tests only (requires libvirt)
|
|
lint Run linting checks (shellcheck)
|
|
|
|
VM Testing Commands (requires libvirt on host):
|
|
test:iso check Check VM testing prerequisites
|
|
test:iso create Create and start test VM (UEFI/Secure Boot)
|
|
test:iso console Connect to VM console
|
|
test:iso status Show VM status
|
|
test:iso destroy Destroy VM and cleanup
|
|
test:iso boot-test Run automated boot test
|
|
test:iso secure-boot Test Secure Boot (manual verification)
|
|
test:iso fde-test Test FDE passphrase prompt (manual verification)
|
|
|
|
Other Commands:
|
|
shell Interactive shell in build container
|
|
help Show this help message
|
|
|
|
Prerequisites for VM Testing:
|
|
- User must be in libvirt group
|
|
- libvirtd service must be running
|
|
- OVMF must be installed (sudo apt install ovmf)
|
|
- ISO must exist in output/
|
|
|
|
Examples:
|
|
$0 build # Build Docker image
|
|
$0 iso # Build ISO (60-90 min)
|
|
$0 monitor # Monitor build progress
|
|
$0 test # Run all tests
|
|
$0 test:iso boot-test # Boot test in VM
|
|
$0 test:iso console # Connect to VM console
|
|
$0 test:iso destroy # Cleanup test VM
|
|
|
|
Note: After adding user to libvirt group, logout and login again.
|
|
EOF
|
|
exit 1
|
|
}
|
|
|
|
# Main entry point
|
|
main() {
|
|
local command="${1:-help}"
|
|
|
|
case "${command}" in
|
|
build)
|
|
echo "Building KNEL-Football Docker image..."
|
|
docker build -t "${DOCKER_IMAGE}" "${SCRIPT_DIR}"
|
|
;;
|
|
test)
|
|
echo "Running KNEL-Football test suite..."
|
|
docker run --rm \
|
|
-v "${SCRIPT_DIR}:/workspace:ro" \
|
|
-v "${BUILD_DIR}:/build" \
|
|
-e BATS_TMPDIR=/build/tmp \
|
|
"${DOCKER_IMAGE}" \
|
|
bash -c "cd /workspace && bats tests/simple_test.bats tests/unit/ tests/integration/ tests/security/ tests/system/"
|
|
;;
|
|
test:unit)
|
|
echo "Running unit tests..."
|
|
docker run --rm \
|
|
-v "${SCRIPT_DIR}:/workspace:ro" \
|
|
-v "${BUILD_DIR}:/build" \
|
|
-e BATS_TMPDIR=/build/tmp \
|
|
"${DOCKER_IMAGE}" \
|
|
bash -c "cd /workspace && bats tests/unit/"
|
|
;;
|
|
test:integration)
|
|
echo "Running integration tests..."
|
|
docker run --rm \
|
|
-v "${SCRIPT_DIR}:/workspace:ro" \
|
|
-v "${BUILD_DIR}:/build" \
|
|
-e BATS_TMPDIR=/build/tmp \
|
|
"${DOCKER_IMAGE}" \
|
|
bash -c "cd /workspace && bats tests/integration/"
|
|
;;
|
|
test:security)
|
|
echo "Running security tests..."
|
|
docker run --rm \
|
|
-v "${SCRIPT_DIR}:/workspace:ro" \
|
|
-v "${BUILD_DIR}:/build" \
|
|
-e BATS_TMPDIR=/build/tmp \
|
|
"${DOCKER_IMAGE}" \
|
|
bash -c "cd /workspace && bats tests/security/"
|
|
;;
|
|
test:system)
|
|
echo "Running system tests..."
|
|
docker run --rm \
|
|
-v "${SCRIPT_DIR}:/workspace:ro" \
|
|
-v "${BUILD_DIR}:/build" \
|
|
-e BATS_TMPDIR=/build/tmp \
|
|
"${DOCKER_IMAGE}" \
|
|
bash -c "cd /workspace && bats tests/system/"
|
|
;;
|
|
lint)
|
|
echo "Running linting checks..."
|
|
docker run --rm \
|
|
-v "${SCRIPT_DIR}:/workspace:ro" \
|
|
"${DOCKER_IMAGE}" \
|
|
bash -c "find /workspace -name '*.sh' -print0 | xargs -0 shellcheck"
|
|
;;
|
|
clean)
|
|
echo "Cleaning build artifacts..."
|
|
rm -rf "${OUTPUT_DIR:?}"/*
|
|
rm -rf "${BUILD_DIR:?}"/*
|
|
;;
|
|
shell)
|
|
echo "Starting interactive shell..."
|
|
docker run --rm -it \
|
|
-v "${SCRIPT_DIR}:/workspace:ro" \
|
|
-v "${OUTPUT_DIR}:/output" \
|
|
-v "${BUILD_DIR}:/build" \
|
|
-u "$(id -u):$(id -g)" \
|
|
-e TZ="America/Chicago" \
|
|
-e DEBIAN_FRONTEND="noninteractive" \
|
|
-e LC_ALL="C" \
|
|
"${DOCKER_IMAGE}" \
|
|
bash
|
|
;;
|
|
iso)
|
|
check_host_fde || exit 1
|
|
echo "Building KNEL-Football secure ISO..."
|
|
echo "ALL operations run inside Docker container"
|
|
echo "Timezone: America/Chicago"
|
|
echo "Mandatory: Full disk encryption with LUKS2"
|
|
echo "Mandatory: Secure Boot with UKI"
|
|
docker run --rm \
|
|
--privileged \
|
|
--user root \
|
|
-v "${SCRIPT_DIR}:/workspace:ro" \
|
|
-v "${OUTPUT_DIR}:/output" \
|
|
-e TZ="America/Chicago" \
|
|
-e DEBIAN_FRONTEND="noninteractive" \
|
|
-e LC_ALL="C" \
|
|
-e USER_UID="$(id -u)" \
|
|
-e USER_GID="$(id -g)" \
|
|
"${DOCKER_IMAGE}" \
|
|
bash -c '
|
|
cd /tmp &&
|
|
rm -rf ./* &&
|
|
echo "Configuring live-build..." &&
|
|
lb config \
|
|
--distribution trixie \
|
|
--architectures amd64 \
|
|
--archive-areas "main contrib non-free" \
|
|
--mode debian \
|
|
--chroot-filesystem squashfs \
|
|
--binary-images iso-hybrid \
|
|
--iso-application "KNEL-Football Secure OS" \
|
|
--iso-publisher "KNEL-Football Security Team" \
|
|
--iso-volume "KNEL-Football Secure" \
|
|
--debian-installer netinst \
|
|
--debian-installer-gui true \
|
|
--source false \
|
|
--apt-indices false \
|
|
--apt-source-archives false &&
|
|
if [ -d /workspace/config ]; then
|
|
echo "Applying custom configuration..."
|
|
cp -r /workspace/config/* ./config/
|
|
fi &&
|
|
|
|
# Create Secure Boot binary hook inline
|
|
echo "Creating Secure Boot hook..." &&
|
|
mkdir -p config/hooks/binary &&
|
|
cat > config/hooks/binary/0200-secureboot-uki.hook << '\''SECUREBOOT_HOOK'\''
|
|
#!/bin/bash
|
|
set -e
|
|
|
|
echo "=========================================="
|
|
echo "Secure Boot UKI Build Hook"
|
|
echo "=========================================="
|
|
|
|
# Secure Boot key directory
|
|
SB_KEY_DIR="/tmp/secureboot-keys"
|
|
mkdir -p "$SB_KEY_DIR"
|
|
|
|
# Generate Secure Boot keys if not present
|
|
if [[ ! -f "$SB_KEY_DIR/db.key" ]]; then
|
|
echo "[SB] Generating Platform Key (PK)..."
|
|
openssl req -new -x509 -newkey rsa:4096 -sha256 -days 3650 \
|
|
-nodes -subj "/CN=KNEL-Football PK/" \
|
|
-keyout "$SB_KEY_DIR/PK.key" \
|
|
-out "$SB_KEY_DIR/PK.crt" 2>/dev/null
|
|
|
|
echo "[SB] Generating Key Exchange Key (KEK)..."
|
|
openssl req -new -x509 -newkey rsa:4096 -sha256 -days 3650 \
|
|
-nodes -subj "/CN=KNEL-Football KEK/" \
|
|
-keyout "$SB_KEY_DIR/KEK.key" \
|
|
-out "$SB_KEY_DIR/KEK.crt" 2>/dev/null
|
|
|
|
echo "[SB] Generating Signature Database Key (db)..."
|
|
openssl req -new -x509 -newkey rsa:4096 -sha256 -days 3650 \
|
|
-nodes -subj "/CN=KNEL-Football db/" \
|
|
-keyout "$SB_KEY_DIR/db.key" \
|
|
-out "$SB_KEY_DIR/db.crt" 2>/dev/null
|
|
|
|
# Create ESL files
|
|
echo "[SB] Creating EFI Signature Lists..."
|
|
for key in PK KEK db; do
|
|
cert-to-efi-sig-list -g "$(uuidgen)" \
|
|
"$SB_KEY_DIR/${key}.crt" \
|
|
"$SB_KEY_DIR/${key}.esl"
|
|
done
|
|
|
|
# Create auth files for UEFI enrollment
|
|
echo "[SB] Creating auth files..."
|
|
sign-efi-sig-list -t "$(date +'\''%Y-%m-%d %H:%M:%S'\'')" \
|
|
-k "$SB_KEY_DIR/PK.key" -c "$SB_KEY_DIR/PK.crt" \
|
|
PK "$SB_KEY_DIR/PK.esl" "$SB_KEY_DIR/PK.auth"
|
|
|
|
sign-efi-sig-list -t "$(date +'\''%Y-%m-%d %H:%M:%S'\'')" \
|
|
-k "$SB_KEY_DIR/PK.key" -c "$SB_KEY_DIR/PK.crt" \
|
|
KEK "$SB_KEY_DIR/KEK.esl" "$SB_KEY_DIR/KEK.auth"
|
|
|
|
sign-efi-sig-list -t "$(date +'\''%Y-%m-%d %H:%M:%S'\'')" \
|
|
-k "$SB_KEY_DIR/KEK.key" -c "$SB_KEY_DIR/KEK.crt" \
|
|
db "$SB_KEY_DIR/db.esl" "$SB_KEY_DIR/db.auth"
|
|
|
|
echo "[SB] Keys generated successfully"
|
|
fi
|
|
|
|
# Build UKI (Unified Kernel Image)
|
|
echo "[SB] Building Unified Kernel Image..."
|
|
|
|
# Find kernel in chroot
|
|
KERNEL=$(ls chroot/boot/vmlinuz-* 2>/dev/null | head -1)
|
|
if [[ -z "$KERNEL" ]]; then
|
|
echo "[SB] ERROR: No kernel found in chroot"
|
|
exit 1
|
|
fi
|
|
KVER=$(echo "$KERNEL" | sed '\''s/.*vmlinuz-//'\'')
|
|
INITRD="chroot/boot/initrd.img-${KVER}"
|
|
|
|
echo "[SB] Kernel: $KERNEL"
|
|
echo "[SB] Version: $KVER"
|
|
|
|
if [[ ! -f "$INITRD" ]]; then
|
|
echo "[SB] ERROR: Initrd not found: $INITRD"
|
|
exit 1
|
|
fi
|
|
|
|
# Find EFI stub
|
|
STUB=""
|
|
for stub_path in chroot/usr/lib/systemd/boot/efi/linuxx64.efi.stub \
|
|
chroot/lib/systemd/boot/efi/linuxx64.efi.stub; do
|
|
if [[ -f "$stub_path" ]]; then
|
|
STUB="$stub_path"
|
|
break
|
|
fi
|
|
done
|
|
|
|
if [[ -z "$STUB" ]]; then
|
|
echo "[SB] ERROR: EFI stub not found - need systemd-boot package"
|
|
exit 1
|
|
fi
|
|
|
|
echo "[SB] EFI Stub: $STUB"
|
|
|
|
# Create UKI output directory
|
|
UKI_DIR="binary/boot/efi/EFI/BOOT"
|
|
mkdir -p "$UKI_DIR"
|
|
|
|
UKI_FILE="${UKI_DIR}/BOOTX64.EFI"
|
|
CMDLINE_FILE="/tmp/cmdline.txt"
|
|
|
|
# Kernel command line with lockdown mode
|
|
echo "quiet splash lockdown=confidentiality module.sig_enforce=1" > "$CMDLINE_FILE"
|
|
|
|
# Build UKI using objcopy
|
|
echo "[SB] Bundling kernel + initramfs + cmdline into UKI..."
|
|
objcopy \
|
|
--add-section .osrel="chroot/etc/os-release" --change-section-vma .osrel=0x20000 \
|
|
--add-section .cmdline="$CMDLINE_FILE" --change-section-vma .cmdline=0x30000 \
|
|
--add-section .linux="$KERNEL" --change-section-vma .linux=0x40000 \
|
|
--add-section .initrd="$INITRD" --change-section-vma .initrd=0x500000 \
|
|
"$STUB" "$UKI_FILE"
|
|
|
|
echo "[SB] UKI created: $UKI_FILE"
|
|
|
|
# Sign the UKI
|
|
echo "[SB] Signing UKI with db key..."
|
|
sbsign --key "$SB_KEY_DIR/db.key" --cert "$SB_KEY_DIR/db.crt" \
|
|
--output "$UKI_FILE" "$UKI_FILE"
|
|
|
|
# Verify signature
|
|
echo "[SB] Verifying UKI signature..."
|
|
if sbverify "$UKI_FILE" 2>&1 | grep -q "Signature verification"; then
|
|
echo "[SB] UKI signature verified successfully"
|
|
else
|
|
echo "[SB] WARNING: UKI signature verification uncertain"
|
|
fi
|
|
|
|
# Copy keys to ISO for installation enrollment
|
|
echo "[SB] Copying keys to ISO..."
|
|
KEYS_DEST="binary/secureboot"
|
|
mkdir -p "$KEYS_DEST"
|
|
cp "$SB_KEY_DIR"/*.auth "$KEYS_DEST/" 2>/dev/null || true
|
|
cp "$SB_KEY_DIR"/*.crt "$KEYS_DEST/" 2>/dev/null || true
|
|
|
|
# Create key enrollment README
|
|
cat > "$KEYS_DEST/README.txt" << '\''README'\''
|
|
KNEL-Football Secure Boot Keys
|
|
==============================
|
|
|
|
These files are used to enroll Secure Boot keys on the target system.
|
|
|
|
During installation, the following keys must be enrolled in UEFI firmware:
|
|
|
|
1. PK.auth - Platform Key (root of trust)
|
|
2. KEK.auth - Key Exchange Key
|
|
3. db.auth - Signature Database (signs the UKI)
|
|
|
|
Enrollment Methods:
|
|
- Use KeyTool EFI application
|
|
- Use efivar from Linux: efi-updatevar -f db.auth db
|
|
- BIOS/UEFI setup utility (manufacturer dependent)
|
|
|
|
SECUREBOOT_HOOK
|
|
chmod +x config/hooks/binary/0200-secureboot-uki.hook &&
|
|
|
|
echo "Starting ISO build..." &&
|
|
timeout 3600 lb build &&
|
|
ISO_FILE=$(find . -name "*.iso" -type f | head -1) &&
|
|
if [ -n "$ISO_FILE" ]; then
|
|
echo "ISO created: $ISO_FILE"
|
|
sha256sum "$ISO_FILE" > "${ISO_FILE}.sha256"
|
|
md5sum "$ISO_FILE" > "${ISO_FILE}.md5"
|
|
FINAL_ISO="knel-football-secure.iso"
|
|
mv "$ISO_FILE" "$FINAL_ISO"
|
|
mv "${ISO_FILE}.sha256" "${FINAL_ISO}.sha256"
|
|
mv "${ISO_FILE}.md5" "${FINAL_ISO}.md5"
|
|
USER_UID=${USER_UID:-1000}
|
|
USER_GID=${USER_GID:-1000}
|
|
chown "$USER_UID:$USER_GID" "$FINAL_ISO" "${FINAL_ISO}.sha256" "${FINAL_ISO}.md5"
|
|
cp "$FINAL_ISO" "${FINAL_ISO}.sha256" "${FINAL_ISO}.md5" /output/
|
|
chown "$USER_UID:$USER_GID" /output/"$FINAL_ISO" /output/"${FINAL_ISO}.sha256" /output/"${FINAL_ISO}.md5"
|
|
echo "ISO build completed"
|
|
echo "=========================================="
|
|
echo "Secure Boot: ENABLED"
|
|
echo "UKI: SIGNED"
|
|
echo "Keys: /secureboot/ on ISO"
|
|
echo "=========================================="
|
|
ls -lh /output/
|
|
else
|
|
echo "ISO build failed"
|
|
exit 1
|
|
fi
|
|
' 2>&1 | tee "$BUILD_LOG"
|
|
;;
|
|
monitor)
|
|
monitor_build "${2:-180}"
|
|
;;
|
|
test:iso)
|
|
check_host_fde || exit 1
|
|
shift # Remove 'test:iso' from args
|
|
local subcmd="${1:-help}"
|
|
case "$subcmd" in
|
|
check)
|
|
vm_check_prerequisites
|
|
;;
|
|
create)
|
|
vm_check_prerequisites && vm_create
|
|
;;
|
|
console)
|
|
vm_console
|
|
;;
|
|
status)
|
|
vm_status
|
|
;;
|
|
destroy)
|
|
vm_destroy
|
|
;;
|
|
boot-test)
|
|
vm_boot_test
|
|
;;
|
|
secure-boot)
|
|
vm_test_secure_boot
|
|
;;
|
|
fde-test)
|
|
vm_test_fde
|
|
;;
|
|
help|*)
|
|
echo "VM Testing Commands:"
|
|
echo " check Check prerequisites"
|
|
echo " create Create and start test VM"
|
|
echo " console Connect to VM console"
|
|
echo " status Show VM status"
|
|
echo " destroy Destroy VM and cleanup"
|
|
echo " boot-test Run automated boot test"
|
|
echo " secure-boot Test Secure Boot"
|
|
echo " fde-test Test FDE passphrase prompt"
|
|
;;
|
|
esac
|
|
;;
|
|
help|*)
|
|
usage
|
|
;;
|
|
esac
|
|
}
|
|
|
|
main "$@"
|