# -*- mode: ruby -*- # vi: set ft=ruby : # # tools/verify-vm/Vagrantfile — parameterized verifier VM. # # Driven by env vars set by tools/verify-vm/verify.sh: # # SKK_VM_BOX generic/ name (e.g. generic/debian11) # SKK_VM_KERNEL_PKG optional apt package for the vulnerable kernel # (e.g. linux-image-5.13.0-19-generic). Empty = use stock. # SKK_VM_KERNEL_VERSION expected kernel version after install # SKK_VM_HOSTNAME hostname for this VM (used in vagrant box name) # # The Vagrantfile mounts the repo root at /vagrant (Vagrant default) so the # in-VM `make` builds against your live source — no rebuild loop. require "yaml" REPO_ROOT = File.expand_path("../..", __dir__) box = ENV["SKK_VM_BOX"] || "generic/debian12" pkg = ENV["SKK_VM_KERNEL_PKG"] || "" mainline = ENV["SKK_VM_MAINLINE_VERSION"] || "" kver = ENV["SKK_VM_KERNEL_VERSION"] || "" host = ENV["SKK_VM_HOSTNAME"] || "skk-verify" Vagrant.configure("2") do |c| # Define ONE Vagrant machine named after SKK_VM_HOSTNAME. Per-module # isolation: each module gets its own `skk-` machine that # vagrant tracks in .vagrant/machines/skk-/parallels/. c.vm.define host do |m| m.vm.box = box # Guest hostnames forbid underscores per RFC 952. Vagrant machine # names allow them (we keep skk-cgroup_release_agent so per-module # state stays isolated in .vagrant/machines/), but inside the VM # we translate to hyphens so the hostname is RFC-valid. m.vm.hostname = host.gsub("_", "-") m.vm.synced_folder REPO_ROOT, "/vagrant", type: "rsync", rsync__exclude: ["build/", ".git/", "*.o", "skeletonkey-test*"] m.vm.provider "parallels" do |p| p.memory = 2048 p.cpus = 2 p.name = host # Don't auto-update Parallels Tools: the installer fails on older # guest kernels (e.g. Ubuntu 20.04's 5.4.0-169 is "outdated and # not supported" by latest tools). We use rsync over SSH for # sync_folder, which doesn't need the guest tools at all. p.update_guest_tools = false p.check_guest_tools = false end # 1. Always install build deps + sudo (needed for module verification). m.vm.provision "shell", inline: <<-SHELL set -e if command -v apt-get >/dev/null 2>&1; then export DEBIAN_FRONTEND=noninteractive apt-get update -qq apt-get install -y -qq build-essential libglib2.0-dev pkg-config sudo curl ca-certificates elif command -v dnf >/dev/null 2>&1; then dnf install -y -q gcc make glib2-devel pkgconfig sudo curl fi SHELL # 2a. Pin via apt if requested. Reboot needed afterward. if !pkg.empty? m.vm.provision "shell", name: "pin-kernel-#{pkg}", inline: <<-SHELL set -e if dpkg-query -W -f='${Status}' #{pkg} 2>/dev/null | grep -q 'install ok installed'; then echo "[=] #{pkg} already installed" else echo "[+] installing #{pkg} (kernel target #{kver})" export DEBIAN_FRONTEND=noninteractive apt-get install -y -qq #{pkg} echo "[i] kernel #{pkg} installed" fi # Pin grub default to this specific kernel. Without it, grub # picks the highest-versioned kernel installed (typically a # stock HWE backport that's POST-fix), defeating the pin's # purpose. Find the kver string by stripping linux-image- # prefix from the pkg name. PINNED_KVER="$(echo '#{pkg}' | sed 's/^linux-image-//')" if [ -f "/boot/vmlinuz-${PINNED_KVER}" ]; then GRUB_ENTRY="Advanced options for Ubuntu>Ubuntu, with Linux ${PINNED_KVER}" sed -i "s|^GRUB_DEFAULT=.*|GRUB_DEFAULT=\\"${GRUB_ENTRY}\\"|" /etc/default/grub echo "[+] GRUB_DEFAULT pinned to: ${GRUB_ENTRY}" update-grub 2>&1 | tail -3 fi SHELL end # 2b. Pin via kernel.ubuntu.com/mainline/ if mainline_version is set. # Fetches the four .debs (linux-headers _all, linux-headers _amd64 # generic, linux-image-unsigned generic, linux-modules generic), # dpkg -i's them, regenerates grub, and prints a reboot hint. # Mainline kernel package version like "5.15.5-051505" sorts ABOVE # Ubuntu's stock "5.15.0-91" in debian-version-compare (numeric # 51505 > 91), so update-grub puts it at boot index 0 and the next # boot lands on it automatically. if !mainline.empty? m.vm.provision "shell", name: "pin-mainline-#{mainline}", inline: <<-SHELL set -e KVER="#{mainline}" # already booted into it? Still fall through to grub-pin to # make sure GRUB_DEFAULT stays correct even after stock kernel # upgrades that might reorder grub entries. BOOTED_INTO_TARGET=0 if uname -r | grep -q "^${KVER}-[0-9]\\+-generic"; then echo "[=] mainline ${KVER} already booted ($(uname -r))" BOOTED_INTO_TARGET=1 fi # already installed on disk? Skip the download/install but # still run the grub-pin block at the end. SKIP_INSTALL=0 if ls /boot/vmlinuz-${KVER}-* >/dev/null 2>&1; then echo "[=] mainline ${KVER} already installed on disk" SKIP_INSTALL=1 fi if [ "$SKIP_INSTALL" -eq 0 ]; then echo "[+] fetching kernel.ubuntu.com mainline v${KVER}" # Newer mainline kernels live under /v${KVER}/amd64/; older ones # (≤ ~4.15) put debs at /v${KVER}/ directly. Try /amd64/ first; # fall back to bare. linux-image-unsigned was renamed from # linux-image- around 4.18 — old kernels use the plain name. BASE="https://kernel.ubuntu.com/mainline/v${KVER}" for URL in "${BASE}/amd64/" "${BASE}/"; do INDEX=$(curl -sL "$URL") if echo "$INDEX" | grep -q '\\.deb"'; then break fi done TMP=$(mktemp -d) cd "$TMP" # Pick the 4 canonical generic-kernel .debs by pattern match against # the directory index. Skip lowlatency variants. Accept both # 'linux-image-unsigned-' (newer) and 'linux-image-' (older). DEBS=$(echo "$INDEX" | \\ grep -oE 'href="[^"]+\\.deb"' | sed 's/href="//; s/"$//' | \\ grep -E '(linux-image(-unsigned)?|linux-modules|linux-headers)-[0-9.]+-[0-9]+-generic_|linux-headers-[0-9.]+-[0-9]+_[^_]+_all\\.deb' | \\ grep -v lowlatency) if [ -z "$DEBS" ]; then echo "[-] no .debs found at ${BASE}/ (tried /amd64/ and bare)" >&2 exit 2 fi for f in $DEBS; do echo "[+] $f" curl -fsSL -O "${URL}${f}" done export DEBIAN_FRONTEND=noninteractive # --force-depends so packages still install even when t64-transition # libs (libssl3t64, libelf1t64) are missing on a pre-24.04 rootfs. # The kernel image + modules don't actually need those at boot — # the dependency is for signing/integrity checks at build time. dpkg -i --force-depends *.deb || apt-get install -f -y -qq || true fi # end SKIP_INSTALL guard # Pin grub default to the just-installed mainline kernel. Without # this, grub's debian-version-compare picks the highest-sorting # vmlinuz-* as default; for downgrades (e.g. stock 4.15 → mainline # 4.14.70), the OLD kernel wins because 4.15 > 4.14 numerically. MAINLINE_VMLINUZ=$(ls /boot/vmlinuz-${KVER}-* 2>/dev/null | head -1) if [ -n "$MAINLINE_VMLINUZ" ]; then MAINLINE_KVER=$(basename "$MAINLINE_VMLINUZ" | sed 's/^vmlinuz-//') # The "Advanced options" submenu entry id is stable across # update-grub runs as "gnulinux-advanced->gnulinux--advanced-". # Easier: use the human menuentry path. GRUB_ENTRY="Advanced options for Ubuntu>Ubuntu, with Linux ${MAINLINE_KVER}" sed -i "s|^GRUB_DEFAULT=.*|GRUB_DEFAULT=\\"${GRUB_ENTRY}\\"|" /etc/default/grub echo "[+] GRUB_DEFAULT pinned to: ${GRUB_ENTRY}" fi update-grub 2>&1 | tail -3 echo "[i] mainline ${KVER} installed; reboot via 'vagrant reload'" SHELL end # 2c. Optional per-module provisioner. If # tools/verify-vm/provisioners/.sh exists, run it as root # before build-and-verify. Used for things only meaningful per-module: # build sudo 1.9.16 from source (sudo_chwoot), drop a polkit allow # rule (udisks_libblockdev), add a sudoers grant (sudo_runas_neg1). skk_mod = ENV["SKK_MODULE"] || "" if !skk_mod.empty? prov_path = File.join(__dir__, "provisioners", "#{skk_mod}.sh") if File.exist?(prov_path) m.vm.provision "shell", name: "module-provision-#{skk_mod}", path: prov_path end end # 3. Build SKELETONKEY in-VM and run --explain --active for the target # module. Runs as the unprivileged 'vagrant' user (NOT root) — most # detect()s gate on "are you already root?" and short-circuit if so, # which would invalidate every verification (pack2theroot was the # motivating case). 'privileged: false' is how vagrant downshifts. # SKK_MODULE is set by verify.sh on the second-pass `vagrant # provision` call (post-reboot if kernel was pinned). m.vm.provision "shell", name: "build-and-verify", run: "never", privileged: false, env: { "SKK_MODULE" => ENV["SKK_MODULE"] || "" }, inline: <<-SHELL set -e cd /vagrant echo "[*] running as $(id)" echo "[*] kernel: $(uname -r)" echo "[*] building skeletonkey..." make clean >/dev/null 2>&1 || true make 2>&1 | tail -3 echo echo "[*] running: skeletonkey --explain ${SKK_MODULE} --active" echo ./skeletonkey --explain "${SKK_MODULE}" --active 2>&1 || true SHELL end end