diff --git a/overlays/base/usr/lib/systemd/system/arcline-mok-enroll.service b/overlays/base/usr/lib/systemd/system/arcline-mok-enroll.service new file mode 100644 index 0000000..f7bd1b0 --- /dev/null +++ b/overlays/base/usr/lib/systemd/system/arcline-mok-enroll.service @@ -0,0 +1,13 @@ +[Unit] +Description=Enroll Arcline secure boot MOK (one-time) +Documentation=docs/secureboot.md +DefaultDependencies=no +Before=sysinit.target + +[Service] +Type=oneshot +ExecStart=/usr/local/sbin/arcline-mok-enroll.sh +RemainAfterExit=yes + +[Install] +WantedBy=sysinit.target diff --git a/overlays/base/usr/local/sbin/arcline-mok-enroll.sh b/overlays/base/usr/local/sbin/arcline-mok-enroll.sh new file mode 100755 index 0000000..8ab185c --- /dev/null +++ b/overlays/base/usr/local/sbin/arcline-mok-enroll.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash +# Arcline OS — one-time MOK enrollment (runs at first boot, then disables itself) +# No-op when no key was shipped in the image (secure boot off by default). +set -euo pipefail + +KEY=/etc/arcline/MOK.der +UNIT=arcline-mok-enroll.service + +if [[ ! -f "$KEY" ]]; then + echo "arcline: no MOK key shipped — secure boot enrollment skipped" + systemctl disable --now "$UNIT" 2>/dev/null || true + exit 0 +fi + +if mokutil --test-key "$KEY" 2>/dev/null | grep -q enabled; then + echo "arcline: MOK already enrolled" +else + echo "arcline: importing MOK — you will be prompted to confirm it on the next reboot" + mokutil --import "$KEY" || true +fi + +# enrollment is one-time; disable ourselves so this never runs again +systemctl disable --now "$UNIT" 2>/dev/null || true diff --git a/scripts/build-iso.sh b/scripts/build-iso.sh index 5dee636..5bde7e4 100755 --- a/scripts/build-iso.sh +++ b/scripts/build-iso.sh @@ -45,6 +45,16 @@ INITRD="$(find "$ROOTFS/boot" -maxdepth 1 -name 'initrd.img-*' | sort -V | tail cp -L "$KERNEL" "$ISOFILES/live/vmlinuz" cp -L "$INITRD" "$ISOFILES/live/initrd.img" +# ── 2b. secure boot (optional: ARCLINE_SIGN=1 + a MOK keypair) ────────────── +if [[ "${ARCLINE_SIGN:-0}" == "1" ]]; then + log "secure boot: signing boot chain and shipping MOK in the live image" + [[ -f "$BUILD_DIR/keys/MOK.der" ]] || die "ARCLINE_SIGN=1 but no MOK keypair — run: scripts/secureboot/gen-keys.sh" + mkdir -p "$ROOTFS/etc/arcline" + cp "$BUILD_DIR/keys/MOK.der" "$ROOTFS/etc/arcline/MOK.der" + "$ROOT/scripts/secureboot/sign-image.sh" "$ROOTFS" --keydir "$BUILD_DIR/keys" + "$ROOT/scripts/secureboot/sign-image.sh" "$ISOFILES" --keydir "$BUILD_DIR/keys" +fi + log "compressing rootfs → squashfs (this takes a while)" # The squashfs is the live root. Keep it complete — offline man pages and # docs are a product promise (see the landing page), so nothing is excluded. diff --git a/scripts/secureboot/gen-keys.sh b/scripts/secureboot/gen-keys.sh new file mode 100755 index 0000000..2202ba2 --- /dev/null +++ b/scripts/secureboot/gen-keys.sh @@ -0,0 +1,45 @@ +#!/usr/bin/env bash +# ───────────────────────────────────────────────────────────────────────────── +# Arcline OS — secure boot: MOK key generation +# +# scripts/secureboot/gen-keys.sh [--keydir build/keys] +# +# Generates a Machine Owner Key (MOK) signing keypair for self-signing the +# kernel and EFI boot chain. MOK is the pragmatic secure-boot path for a +# self-hosted distro: you enroll the key once (one-time prompt at first boot) +# and then every Arcline update is verified against it. No third-party CA. +# +# Outputs (default build/keys/): +# MOK.priv — private signing key (keep this secret, back it up) +# MOK.pem — certificate in PEM form (for sbsign) +# MOK.der — certificate in DER form (for mokutil enrollment) +# +# Requires openssl. Enrollment on a target system is handled by +# arcline-mok-enroll.service (see overlays/base). +# ───────────────────────────────────────────────────────────────────────────── +set -euo pipefail +source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/common.sh" + +KEYDIR="${1:-$BUILD_DIR/keys}" +[[ "${1:-}" == "--keydir" ]] && KEYDIR="${2:?usage: gen-keys.sh [--keydir