- keys/adacam-update-public.pem: RSA-4096 public key (private on Lucy) - services/updater/adacam-updater.sh: standalone updater (also inlined in liberate.sh) - services/updater/99-adacam-usb.rules: udev rule for auto-trigger on USB insert - scripts/sign-bundle.sh: create + sign recovery bundles on Lucy - keys/README.md: updated with signing key docs and bundle creation instructions Private key at: /boot/config/adacam/adacam-update-private.pem (Lucy, boot-persistent)
98 lines
3.7 KiB
Bash
Executable file
98 lines
3.7 KiB
Bash
Executable file
#!/bin/bash
|
|
# adacam-updater — secure USB recovery for AdaCam
|
|
#
|
|
# Triggered by udev when a USB drive is inserted.
|
|
# Looks for a signed recovery bundle on the drive.
|
|
# Verifies RSA-4096 signature before touching anything.
|
|
# No signature = no action. No exceptions.
|
|
#
|
|
# Bundle format on USB drive:
|
|
# /adacam_recovery/adacam-recovery.tar.gz
|
|
# /adacam_recovery/adacam-recovery.tar.gz.sig
|
|
|
|
set -euo pipefail
|
|
|
|
VERIFY_KEY="/etc/adacam/update-verify.pem"
|
|
USB_MOUNT="/mnt/usb-recovery"
|
|
BUNDLE_DIR="adacam_recovery"
|
|
BUNDLE_NAME="adacam-recovery.tar.gz"
|
|
SIG_NAME="adacam-recovery.tar.gz.sig"
|
|
WORK_DIR="/tmp/adacam-recovery-work"
|
|
LOG="/data/adacam/recovery.log"
|
|
MARKER="/data/adacam/recovery_in_progress"
|
|
|
|
log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG"; }
|
|
die() { log "ERROR: $*"; rm -f "$MARKER"; exit 1; }
|
|
|
|
# ── Sanity checks ─────────────────────────────────────────────────────────────
|
|
|
|
[ -f "$VERIFY_KEY" ] || die "no verify key at $VERIFY_KEY — cannot proceed"
|
|
[ "$(id -u)" = "0" ] || die "must run as root"
|
|
|
|
# Only one recovery at a time
|
|
[ -f "$MARKER" ] && die "recovery already in progress (marker exists)"
|
|
|
|
mkdir -p /data/adacam
|
|
touch "$MARKER"
|
|
log "=== AdaCam USB Recovery Starting ==="
|
|
|
|
# ── Mount USB ─────────────────────────────────────────────────────────────────
|
|
|
|
USB_DEV="${1:-}"
|
|
if [ -z "$USB_DEV" ]; then
|
|
# Auto-detect first USB mass storage device
|
|
USB_DEV=$(lsblk -rno NAME,TYPE | awk '$2=="part"{print "/dev/"$1}' | grep -v mmcblk | head -n1)
|
|
fi
|
|
|
|
[ -n "$USB_DEV" ] || die "no USB device found"
|
|
log "USB device: $USB_DEV"
|
|
|
|
mkdir -p "$USB_MOUNT"
|
|
mount -o ro "$USB_DEV" "$USB_MOUNT" || die "failed to mount $USB_DEV"
|
|
log "mounted $USB_DEV at $USB_MOUNT"
|
|
|
|
cleanup() {
|
|
umount "$USB_MOUNT" 2>/dev/null || true
|
|
rm -rf "$WORK_DIR"
|
|
rm -f "$MARKER"
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
# ── Find bundle ───────────────────────────────────────────────────────────────
|
|
|
|
BUNDLE="$USB_MOUNT/$BUNDLE_DIR/$BUNDLE_NAME"
|
|
SIG="$USB_MOUNT/$BUNDLE_DIR/$SIG_NAME"
|
|
|
|
[ -f "$BUNDLE" ] || die "no bundle found at $BUNDLE_DIR/$BUNDLE_NAME — nothing to do"
|
|
[ -f "$SIG" ] || die "no signature found at $BUNDLE_DIR/$SIG_NAME — refusing unsigned bundle"
|
|
|
|
log "found bundle: $(du -sh "$BUNDLE" | cut -f1)"
|
|
|
|
# ── Verify signature ──────────────────────────────────────────────────────────
|
|
|
|
log "verifying signature..."
|
|
if ! openssl dgst -sha256 -verify "$VERIFY_KEY" -signature "$SIG" "$BUNDLE" >> "$LOG" 2>&1; then
|
|
die "SIGNATURE VERIFICATION FAILED — bundle rejected, device unchanged"
|
|
fi
|
|
|
|
log "signature OK — bundle is authentic"
|
|
|
|
# ── Extract and run ───────────────────────────────────────────────────────────
|
|
|
|
rm -rf "$WORK_DIR"
|
|
mkdir -p "$WORK_DIR"
|
|
|
|
log "extracting bundle..."
|
|
tar -xzf "$BUNDLE" -C "$WORK_DIR" || die "failed to extract bundle"
|
|
|
|
INSTALL_SCRIPT="$WORK_DIR/install.sh"
|
|
[ -f "$INSTALL_SCRIPT" ] || die "no install.sh in bundle — malformed recovery package"
|
|
[ -x "$INSTALL_SCRIPT" ] || chmod +x "$INSTALL_SCRIPT"
|
|
|
|
log "running install.sh..."
|
|
bash "$INSTALL_SCRIPT" >> "$LOG" 2>&1 || die "install.sh failed — check $LOG"
|
|
|
|
log "=== Recovery complete — rebooting in 5s ==="
|
|
sync
|
|
sleep 5
|
|
reboot
|