From 0576b7c61f7d6fac081f497fe3135c8d23e1902b Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Wed, 22 Jul 2026 08:59:05 +0200 Subject: [PATCH] OPNSense: Bump FreeBSD to 15 and OPNSense to 26.7 (#15943) * OPNSense: Bump FreeBSD to 15 and OPNSense to 26.7 * fix user feedback * fix typo * fix url for image * Replace fixed OPNsense wait with screen polling Swap the hardcoded 1000-second sleep for an adaptive loop that watches VM console screendump hashes and proceeds after sustained stability. The new logic enforces a minimum 5-minute wait, checks every 30 seconds, and caps total wait at 40 minutes to handle slower hosts or screendump failures more reliably. * Harden OPNsense build wait detection Improves VM install completion detection by encapsulating screendump hashing in a helper, clearing stale dump files, and trying both `qm monitor` and `pvesh` monitor paths. The stability check now tolerates alternating A/B frames from a blinking login cursor instead of requiring one identical frame. It also adds elapsed-time progress output and a safe fallback to a fixed 12-minute wait when screendumps are unavailable, avoiding false completion or endless polling. --- vm/opnsense-vm.sh | 102 +++++++++++++++++++++++++++++++++++++--------- 1 file changed, 83 insertions(+), 19 deletions(-) diff --git a/vm/opnsense-vm.sh b/vm/opnsense-vm.sh index 4d15f25be..32fd6f076 100644 --- a/vm/opnsense-vm.sh +++ b/vm/opnsense-vm.sh @@ -25,6 +25,7 @@ METHOD="" NSAPP="opnsense-vm" var_os="opnsense" var_version="26.7" +FREEBSD_MAJOR="15" # GEN_MAC=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') GEN_MAC_LAN=02:$(openssl rand -hex 5 | awk '{print toupper($0)}' | sed 's/\(..\)/\1:/g; s/.$//') @@ -490,7 +491,7 @@ function advanced_settings() { fi echo -e "${DGN}Using LAN GATEWAY ADDRESS: ${BGN}$LAN_GW${CL}" fi - if NETMASK=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN netmmask (24 for example)" 8 58 $NETMASK --title "LAN NETMASK" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if NETMASK=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN netmask (24 for example)" 8 58 $NETMASK --title "LAN NETMASK" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $NETMASK ]; then echo -e "${DGN}Netmask needs to be set if ip is not dhcp${CL}" fi @@ -558,7 +559,7 @@ function advanced_settings() { else exit-script fi - if WAN_NETMASK=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN netmmask (24 for example)" 8 58 $WAN_NETMASK --title "WAN NETMASK" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if WAN_NETMASK=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN netmask (24 for example)" 8 58 $WAN_NETMASK --title "WAN NETMASK" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $WAN_NETMASK ]; then echo -e "${DGN}WAN Netmask needs to be set if ip is not dhcp${CL}" fi @@ -574,7 +575,7 @@ function advanced_settings() { else exit-script fi - if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN MAC Address" 8 58 $GEN_MAC --title "WAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if MAC1=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN MAC Address" 8 58 $GEN_MAC --title "LAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $MAC1 ]; then MAC="$GEN_MAC" else @@ -585,7 +586,7 @@ function advanced_settings() { exit-script fi - if MAC2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a LAN MAC Address" 8 58 $GEN_MAC_LAN --title "LAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then + if MAC2=$(whiptail --backtitle "Proxmox VE Helper Scripts" --inputbox "Set a WAN MAC Address" 8 58 $GEN_MAC_LAN --title "WAN MAC ADDRESS" --cancel-button Exit-Script 3>&1 1>&2 2>&3); then if [ -z $MAC2 ]; then WAN_MAC="$GEN_MAC_LAN" else @@ -652,23 +653,26 @@ fi msg_ok "Using ${CL}${BL}$STORAGE${CL} ${GN}for Storage Location." msg_ok "Virtual Machine ID is ${CL}${BL}$VMID${CL}." msg_info "Retrieving the URL for the OPNsense Qcow2 Disk Image" -# Use latest stable FreeBSD amd64 qcow2 VM image (generic, not UFS/ZFS) +# Use latest stable FreeBSD amd64 qcow2 VM image matching FREEBSD_MAJOR RELEASE_LIST="$(curl -s https://download.freebsd.org/releases/VM-IMAGES/ | - grep -Eo '[0-9]+\.[0-9]+-RELEASE' | + grep -Eo "${FREEBSD_MAJOR}\.[0-9]+-RELEASE" | sort -Vr | uniq)" URL="" FREEBSD_VER="" for ver in $RELEASE_LIST; do - candidate="https://download.freebsd.org/releases/VM-IMAGES/${ver}/amd64/Latest/FreeBSD-${ver}-amd64.qcow2.xz" - if curl -fsI "$candidate" >/dev/null 2>&1; then - FREEBSD_VER="$ver" - URL="$candidate" - break - fi + # FreeBSD 15+ publishes separate -ufs/-zfs images instead of a generic one + for variant in "" "-ufs" "-zfs"; do + candidate="https://download.freebsd.org/releases/VM-IMAGES/${ver}/amd64/Latest/FreeBSD-${ver}-amd64${variant}.qcow2.xz" + if curl -fsI "$candidate" >/dev/null 2>&1; then + FREEBSD_VER="$ver" + URL="$candidate" + break 2 + fi + done done if [ -z "$URL" ]; then - msg_error "Could not find generic FreeBSD amd64 qcow2 image (non-UFS/ZFS)." + msg_error "Could not find a FreeBSD ${FREEBSD_MAJOR}.x amd64 qcow2 image." exit 115 fi msg_ok "Download URL: ${CL}${BL}${URL}${CL}" @@ -768,7 +772,7 @@ DESCRIPTION=$( cat < - Logo + Logo

OPNsense VM

@@ -814,10 +818,70 @@ if [ -n "$WAN_BRG" ]; then msg_ok "WAN interface added" sleep 5 # Brief pause after adding network interface fi -send_line_to_vm "sh ./opnsense-bootstrap.sh.in -y -f -r 26.7" +# FreeBSD 15+ VM images ship the base system as pkgbase packages; the bootstrap's +# "delete all packages" step would remove the running base system (/bin/rm etc.) +# and brick the VM. Deregister them from the pkg db first - the files stay in +# place and OPNsense replaces base and kernel with its own sets afterwards. +send_line_to_vm "echo \"PRAGMA foreign_keys=ON; DELETE FROM packages WHERE name LIKE 'FreeBSD-%';\" | pkg shell" +sleep 5 +send_line_to_vm "sh ./opnsense-bootstrap.sh.in -y -f -r ${var_version}" msg_ok "OPNsense VM is being installed, do not close the terminal, or the installation will fail." -#We need to wait for the OPNsense build proccess to finish, this takes a few minutes -sleep 1000 +# The bootstrap ends with an automatic reboot into OPNsense. While it runs the +# console keeps changing (download progress, package installs); once the VM has +# settled at the login prompt the screen stays static. Poll a screendump hash +# and continue after 3 minutes without change, bounded by a floor (the build +# never finishes faster) and a ceiling for slow machines. If no screendump can +# be captured at all, fall back to a fixed wait. +SCREEN_PPM="${TEMP_DIR}/screen-${VMID}.ppm" + +function screen_hash() { + # Remove the previous dump first: a stale file from an earlier successful + # dump must not simulate a static screen when later dumps start failing. + # Note: "qm monitor" is unusable here - its readline attaches to /dev/tty + # even with piped stdin and captures the terminal, so use the API instead. + rm -f "$SCREEN_PPM" + timeout 10 pvesh create /nodes/$(hostname -s)/qemu/$VMID/monitor --command "screendump ${SCREEN_PPM}" >/dev/null 2>&1 || true + md5sum "$SCREEN_PPM" 2>/dev/null | cut -d' ' -f1 || true +} + +build_elapsed=300 +build_stable=0 +screen_ok=0 +hash_a="" +hash_b="" +sleep 300 +while [ $build_stable -lt 6 ] && [ $build_elapsed -lt 2400 ]; do + sleep 30 + build_elapsed=$((build_elapsed + 30)) + new_hash=$(screen_hash) + if [ -n "$new_hash" ]; then + screen_ok=1 + # The login prompt cursor may blink: a screen alternating between the same + # two frames (A/B/A/B) counts as stable, anything new resets the counter + if [ "$new_hash" = "$hash_a" ] || [ "$new_hash" = "$hash_b" ]; then + build_stable=$((build_stable + 1)) + else + build_stable=0 + fi + else + build_stable=0 + fi + hash_b="$hash_a" + hash_a="$new_hash" + if [ -n "$new_hash" ]; then + echo -e "${DGN}Waiting for OPNsense build: ${YW}$((build_elapsed / 60))min elapsed, screen ${new_hash:0:8}, stable ${build_stable}/6${CL}" + else + echo -e "${DGN}Waiting for OPNsense build: ${YW}$((build_elapsed / 60))min elapsed, screendump failed${CL}" + fi + # No working screendump after several attempts: fixed wait instead + if [ $screen_ok -eq 0 ] && [ $build_elapsed -ge 480 ]; then + msg_error "Console screendump not available on this system - falling back to a fixed wait (12 minutes)." + sleep 720 + build_elapsed=$((build_elapsed + 720)) + break + fi +done +msg_ok "OPNsense build finished after $((build_elapsed / 60)) minutes" send_line_to_vm "root" send_line_to_vm "opnsense" send_line_to_vm "2" @@ -855,8 +919,8 @@ if [ -n "$WAN_BRG" ] && [ "$WAN_IP_ADDR" != "" ]; then send_line_to_vm "2" send_line_to_vm "n" send_line_to_vm "${WAN_IP_ADDR}" - send_line_to_vm "${NETMASK}" - send_line_to_vm "${LAN_GW}" + send_line_to_vm "${WAN_NETMASK}" + send_line_to_vm "${WAN_GW}" send_line_to_vm "n" send_line_to_vm " " send_line_to_vm "n"