From 09f7c1f7532bd17c992a2df8c15779cc16c286dd Mon Sep 17 00:00:00 2001 From: s8n Date: Sat, 2 May 2026 04:38:23 +0100 Subject: [PATCH] build: wire 30-apply-v03-theme.sh into ks %post + SSH key auto-inject in run-vm.sh (#1) Co-authored-by: veilor-org --- kickstart/veilor-os.ks | 1 + test/run-vm.sh | 161 ++++++++++++++++++++++++++++++++++++++++- 2 files changed, 160 insertions(+), 2 deletions(-) diff --git a/kickstart/veilor-os.ks b/kickstart/veilor-os.ks index 04f2ff9..12439c5 100644 --- a/kickstart/veilor-os.ks +++ b/kickstart/veilor-os.ks @@ -224,6 +224,7 @@ bash $REPO/scripts/selinux/build-policy.sh || echo "[WARN] SELinux build failed; # Apply KDE theme + DuckSans + os-release branding bash $REPO/scripts/kde-theme-apply.sh +bash $REPO/scripts/30-apply-v03-theme.sh || echo "[WARN] v03-theme apply failed" # Force admin password set on first boot. # livecd-creator does NOT honor `user` kickstart directive (it's a LIVE diff --git a/test/run-vm.sh b/test/run-vm.sh index 880d5ea..e6a0112 100755 --- a/test/run-vm.sh +++ b/test/run-vm.sh @@ -5,6 +5,34 @@ # ./test/run-vm.sh path/to.iso # specific ISO # SECBOOT=1 ./test/run-vm.sh # use OVMF Secure Boot firmware # FRESH=1 ./test/run-vm.sh # wipe disk + nvram, re-install from scratch +# NO_INJECT=1 ./test/run-vm.sh # skip SSH-key auto-injection +# +# SSH-key auto-injection (chosen approach: dual — cloud-init NoCloud + QEMU +# monitor sendkey fallback) +# ------------------------------------------------------------------ +# Goal: previously each test required logging in at the QEMU console and +# running `passwd -d liveuser`, editing sshd_config, etc. before +# `ssh -p 2222 liveuser@localhost` worked. This script eliminates that. +# +# Primary path (works for the *installed* system, not the live image): +# * Detect host pubkey at ~/.ssh/id_ed25519.pub or ~/.ssh/id_rsa.pub +# * Build a NoCloud cloud-init ISO (user-data + meta-data) via mkisofs/xorriso +# * Mount it as a second virtual cdrom — Anaconda/cloud-init picks it up +# automatically when installing because the seed has the magic +# `cidata` volume label. +# +# Fallback path (works for the *live* image, which doesn't run cloud-init by +# default — dracut-live + livesys-scripts mount squashfs read-only and skip +# cloud-init.target): +# * Open a QEMU monitor unix socket (-monitor unix:...). +# * After ~90s (long enough for SDDM autologin → liveuser), background a +# helper that pipes a sequence of `sendkey` events to the monitor: +# Ctrl+Alt+F2 (drop to TTY) +# "sudo passwd -d liveuser && sudo systemctl reload sshd\n" +# This unblocks SSH on port 2222 without manual interaction. +# +# Both paths are best-effort; if the host has no pubkey, both are skipped +# and the script behaves exactly as before. set -euo pipefail @@ -12,6 +40,8 @@ REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)" TEST_DIR="$REPO_ROOT/test" DISK="$TEST_DIR/veilor-vm.qcow2" NVRAM="$TEST_DIR/veilor-vm.nvram" +SEED_ISO="$TEST_DIR/cloud-init-seed.iso" +MONITOR_SOCK="$TEST_DIR/veilor-vm.monitor.sock" ISO="${1:-$(ls -t "$REPO_ROOT"/build/out/*.iso 2>/dev/null | head -1)}" [[ -n ${ISO:-} && -f $ISO ]] || { echo "[ERR] No ISO found. Build first: ./build/build-iso.sh"; exit 1; } @@ -28,19 +58,144 @@ fi # Reset on FRESH=1 if [[ "${FRESH:-0}" == "1" ]]; then - rm -f "$DISK" "$NVRAM" + rm -f "$DISK" "$NVRAM" "$SEED_ISO" fi # Provision disk + per-VM nvram once [[ -f $DISK ]] || qemu-img create -f qcow2 "$DISK" 40G [[ -f $NVRAM ]] || cp "$OVMF_VARS_SRC" "$NVRAM" +# ── Locate host SSH pubkey (ed25519 preferred, rsa fallback) ── +HOST_PUBKEY="" +if [[ "${NO_INJECT:-0}" != "1" ]]; then + for cand in "$HOME/.ssh/id_ed25519.pub" "$HOME/.ssh/id_rsa.pub"; do + if [[ -f $cand ]]; then + HOST_PUBKEY="$(< "$cand")" + echo "[INFO] using host pubkey: $cand" + break + fi + done +fi + +# ── Build cloud-init NoCloud seed ISO (primary path) ── +SEED_ARGS=() +if [[ -n $HOST_PUBKEY ]]; then + SEED_DIR="$(mktemp -d)" + trap 'rm -rf "$SEED_DIR"' EXIT + + cat > "$SEED_DIR/meta-data" < "$SEED_DIR/user-data" </dev/null 2>&1; then + mkisofs -quiet -output "$SEED_ISO" \ + -volid cidata -joliet -rock \ + "$SEED_DIR/user-data" "$SEED_DIR/meta-data" + elif command -v xorriso >/dev/null 2>&1; then + xorriso -as mkisofs -quiet -output "$SEED_ISO" \ + -volid cidata -joliet -rock \ + "$SEED_DIR/user-data" "$SEED_DIR/meta-data" + elif command -v cloud-localds >/dev/null 2>&1; then + cloud-localds "$SEED_ISO" "$SEED_DIR/user-data" "$SEED_DIR/meta-data" + else + echo "[WARN] no mkisofs/xorriso/cloud-localds — skipping cloud-init seed" + SEED_ISO="" + fi + + if [[ -n $SEED_ISO && -f $SEED_ISO ]]; then + echo "[INFO] cloud-init seed ISO: $SEED_ISO" + SEED_ARGS=(-drive "file=$SEED_ISO,media=cdrom,readonly=on") + fi +fi + +# ── QEMU monitor fallback (live ISO doesn't run cloud-init) ── +# Started in the background after a delay; sends keypresses through the +# QEMU monitor unix socket to drop to a TTY and unblock SSH for liveuser. +MONITOR_ARGS=() +if [[ -n $HOST_PUBKEY ]]; then + rm -f "$MONITOR_SOCK" + MONITOR_ARGS=(-monitor "unix:$MONITOR_SOCK,server,nowait") + + ( + # Wait for the VM to reach a usable login prompt (SDDM autologin → + # liveuser session is the most realistic target). 90s is enough on + # KVM/4 vCPUs; tune via VM_BOOT_DELAY if needed. + sleep "${VM_BOOT_DELAY:-90}" + [[ -S $MONITOR_SOCK ]] || exit 0 + + # send_chord [key2 ...] — chord released between calls + send_chord() { + local IFS='-' + local chord="$*" + printf 'sendkey %s\n' "$chord" + } + + # send_str — only ASCII printable + space + return + send_str() { + local s="$1" ch + local i=0 + while (( i < ${#s} )); do + ch="${s:i:1}" + case "$ch" in + ' ') printf 'sendkey spc\n' ;; + [a-z0-9]) printf 'sendkey %s\n' "$ch" ;; + [A-Z]) printf 'sendkey shift-%s\n' "${ch,,}" ;; + '-') printf 'sendkey minus\n' ;; + '_') printf 'sendkey shift-minus\n' ;; + '/') printf 'sendkey slash\n' ;; + '.') printf 'sendkey dot\n' ;; + '&') printf 'sendkey shift-7\n' ;; + esac + i=$((i+1)) + done + } + + { + send_chord ctrl alt f2 + sleep 1 + # Type: liveuser (no password by default on live) + send_str "liveuser" + printf 'sendkey ret\n' + sleep 2 + send_str "sudo passwd -d liveuser" + printf 'sendkey ret\n' + sleep 1 + send_str "sudo systemctl reload sshd" + printf 'sendkey ret\n' + } | socat - "UNIX-CONNECT:$MONITOR_SOCK" 2>/dev/null || true + ) & + INJECT_PID=$! + trap 'kill $INJECT_PID 2>/dev/null || true; rm -f "$MONITOR_SOCK"; rm -rf "${SEED_DIR:-}"' EXIT +fi + echo "════════════════════════════════════════════════════════" echo " veilor-os :: VM test" echo " ISO : $ISO" echo " Disk : $DISK" echo " NVRAM : $NVRAM" +echo " Seed : ${SEED_ISO:-}" echo " Mode : ${SECBOOT:+secboot}${SECBOOT:-stock UEFI}" +echo " Inject: ${HOST_PUBKEY:+yes}${HOST_PUBKEY:-no (no host pubkey)}" echo "════════════════════════════════════════════════════════" exec qemu-system-x86_64 \ @@ -54,7 +209,9 @@ exec qemu-system-x86_64 \ -drive if=pflash,format=raw,readonly=on,file="$OVMF_CODE" \ -drive if=pflash,format=raw,file="$NVRAM" \ -drive file="$DISK",if=virtio,format=qcow2,cache=writeback \ - -cdrom "$ISO" \ + -drive file="$ISO",media=cdrom,readonly=on \ + "${SEED_ARGS[@]}" \ + "${MONITOR_ARGS[@]}" \ -boot menu=on,splash-time=2000 \ -netdev user,id=net0,hostfwd=tcp::2222-:22 \ -device virtio-net-pci,netdev=net0 \