From 1ccf90cb27f773f0659a8e2e603e77d421d52afa Mon Sep 17 00:00:00 2001 From: "CanbiZ (MickLesk)" <47820557+MickLesk@users.noreply.github.com> Date: Mon, 12 Jan 2026 14:04:28 +0100 Subject: [PATCH] core: validate container ID before pct create to prevent failures (#10729) * feat: validate container ID before pct create to prevent failures Add validation to ensure container IDs are not already in use before attempting to create containers. This prevents pct create failures when an ID is already assigned to a VM/LXC or used in LVM volumes. Changes: - Add validate_container_id() and get_valid_container_id() functions to build.func - Validate ID in base_settings() for default installation method - Validate ID in advanced_settings() dialog with user prompt for next available ID - Add validation to turnkey.sh with interactive dialog - Add validation to all-templates.sh with automatic ID correction Checks performed: - Container config file existence (/etc/pve/lxc/*.conf) - VM config file existence (/etc/pve/qemu-server/*.conf) - LVM logical volume usage Fixes issue where container creation would fail if the suggested/chosen ID was already in use. * feat: validate container ID before pct create to prevent failures Add validation to ensure container IDs are not already in use before attempting to create containers. This prevents pct create failures when an ID is already assigned to a VM/LXC or used in LVM volumes. Changes: - Add validate_container_id() and get_valid_container_id() functions to build.func - Validate ID in base_settings() for default installation method - Validate ID in advanced_settings() dialog with user prompt for next available ID - Add validation to turnkey.sh with interactive dialog - Add validation to all-templates.sh with automatic ID correction Checks performed: - Container config file existence (/etc/pve/lxc/*.conf) - VM config file existence (/etc/pve/qemu-server/*.conf) - LVM logical volume usage Fixes issue where container creation would fail if the suggested/chosen ID was already in use. * Update turnkey.sh * Update build.func --- misc/build.func | 124 ++++++++++++++++++++++++++++++++++- tools/addon/all-templates.sh | 31 +++++++++ turnkey/turnkey.sh | 48 ++++++++++++-- 3 files changed, 197 insertions(+), 6 deletions(-) diff --git a/misc/build.func b/misc/build.func index bcc237a7d..9aa69ef56 100644 --- a/misc/build.func +++ b/misc/build.func @@ -231,6 +231,100 @@ install_ssh_keys_into_ct() { return 0 } +# ------------------------------------------------------------------------------ +# validate_container_id() +# +# - Validates if a container ID is available for use +# - Checks if ID is already used by VM or LXC container +# - Checks if ID is used in LVM logical volumes +# - Returns 0 if ID is available, 1 if already in use +# ------------------------------------------------------------------------------ +validate_container_id() { + local ctid="$1" + + # Check if ID is numeric + if ! [[ "$ctid" =~ ^[0-9]+$ ]]; then + return 1 + fi + + # Check if config file exists for VM or LXC + if [[ -f "/etc/pve/qemu-server/${ctid}.conf" ]] || [[ -f "/etc/pve/lxc/${ctid}.conf" ]]; then + return 1 + fi + + # Check if ID is used in LVM logical volumes + if lvs --noheadings -o lv_name 2>/dev/null | grep -qE "(^|[-_])${ctid}($|[-_])"; then + return 1 + fi + + return 0 +} + +# ------------------------------------------------------------------------------ +# get_valid_container_id() +# +# - Returns a valid, unused container ID +# - If provided ID is valid, returns it +# - Otherwise increments from suggested ID until a free one is found +# - Calls validate_container_id() to check availability +# ------------------------------------------------------------------------------ +get_valid_container_id() { + local suggested_id="${1:-$(pvesh get /cluster/nextid)}" + + while ! validate_container_id "$suggested_id"; do + suggested_id=$((suggested_id + 1)) + done + + echo "$suggested_id" +} + +# ------------------------------------------------------------------------------ +# validate_container_id() +# +# - Validates if a container ID is available for use +# - Checks if ID is already used by VM or LXC container +# - Checks if ID is used in LVM logical volumes +# - Returns 0 if ID is available, 1 if already in use +# ------------------------------------------------------------------------------ +validate_container_id() { + local ctid="$1" + + # Check if ID is numeric + if ! [[ "$ctid" =~ ^[0-9]+$ ]]; then + return 1 + fi + + # Check if config file exists for VM or LXC + if [[ -f "/etc/pve/qemu-server/${ctid}.conf" ]] || [[ -f "/etc/pve/lxc/${ctid}.conf" ]]; then + return 1 + fi + + # Check if ID is used in LVM logical volumes + if lvs --noheadings -o lv_name 2>/dev/null | grep -qE "(^|[-_])${ctid}($|[-_])"; then + return 1 + fi + + return 0 +} + +# ------------------------------------------------------------------------------ +# get_valid_container_id() +# +# - Returns a valid, unused container ID +# - If provided ID is valid, returns it +# - Otherwise increments from suggested ID until a free one is found +# - Calls validate_container_id() to check availability +# ------------------------------------------------------------------------------ +get_valid_container_id() { + local suggested_id="${1:-$(pvesh get /cluster/nextid)}" + + while ! validate_container_id "$suggested_id"; do + suggested_id=$((suggested_id + 1)) + done + + echo "$suggested_id" +} + # ------------------------------------------------------------------------------ # find_host_ssh_keys() # @@ -483,7 +577,15 @@ base_settings() { RAM_SIZE="${final_ram}" VERBOSE=${var_verbose:-"${1:-no}"} PW=${var_pw:-""} - CT_ID=${var_ctid:-$NEXTID} + + # Validate and set Container ID + local requested_id="${var_ctid:-$NEXTID}" + if ! validate_container_id "$requested_id"; then + msg_warn "Container ID $requested_id is already in use. Using next available ID: $(get_valid_container_id "$requested_id")" + requested_id=$(get_valid_container_id "$requested_id") + fi + CT_ID="$requested_id" + HN=${var_hostname:-$NSAPP} BRG=${var_brg:-"vmbr0"} NET=${var_net:-"dhcp"} @@ -1308,7 +1410,25 @@ advanced_settings() { --ok-button "Next" --cancel-button "Back" \ --inputbox "\nSet Container ID" 10 58 "$_ct_id" \ 3>&1 1>&2 2>&3); then - _ct_id="${result:-$NEXTID}" + local input_id="${result:-$NEXTID}" + + # Validate that ID is numeric + if ! [[ "$input_id" =~ ^[0-9]+$ ]]; then + whiptail --backtitle "Proxmox VE Helper Scripts" --title "Invalid ID" --msgbox "Container ID must be numeric." 8 58 + continue + fi + + # Check if ID is already in use + if ! validate_container_id "$input_id"; then + if whiptail --backtitle "Proxmox VE Helper Scripts" --title "ID Already In Use" \ + --yesno "Container/VM ID $input_id is already in use.\n\nWould you like to use the next available ID ($(get_valid_container_id "$input_id"))?" 10 58; then + _ct_id=$(get_valid_container_id "$input_id") + else + continue + fi + else + _ct_id="$input_id" + fi ((STEP++)) else ((STEP--)) diff --git a/tools/addon/all-templates.sh b/tools/addon/all-templates.sh index 59bba3256..ed06360a5 100644 --- a/tools/addon/all-templates.sh +++ b/tools/addon/all-templates.sh @@ -42,6 +42,29 @@ function msg() { local TEXT="$1" echo -e "$TEXT" } +function validate_container_id() { + local ctid="$1" + # Check if ID is numeric + if ! [[ "$ctid" =~ ^[0-9]+$ ]]; then + return 1 + fi + # Check if config file exists for VM or LXC + if [[ -f "/etc/pve/qemu-server/${ctid}.conf" ]] || [[ -f "/etc/pve/lxc/${ctid}.conf" ]]; then + return 1 + fi + # Check if ID is used in LVM logical volumes + if lvs --noheadings -o lv_name 2>/dev/null | grep -qE "(^|[-_])${ctid}($|[-_])"; then + return 1 + fi + return 0 +} +function get_valid_container_id() { + local suggested_id="${1:-$(pvesh get /cluster/nextid)}" + while ! validate_container_id "$suggested_id"; do + suggested_id=$((suggested_id + 1)) + done + echo "$suggested_id" +} function cleanup_ctid() { if pct status $CTID &>/dev/null; then if [ "$(pct status $CTID | awk '{print $2}')" == "running" ]; then @@ -76,7 +99,15 @@ TEMPLATE=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "All Templat # Setup script environment NAME=$(echo "$TEMPLATE" | grep -oE '^[^-]+-[^-]+') PASS="$(openssl rand -base64 8)" + +# Get valid Container ID CTID=$(pvesh get /cluster/nextid) +if ! validate_container_id "$CTID"; then + warn "Container ID $CTID is already in use." + CTID=$(get_valid_container_id "$CTID") + info "Using next available ID: $CTID" +fi + PCT_OPTIONS=" -features keyctl=1,nesting=1 -hostname $NAME diff --git a/turnkey/turnkey.sh b/turnkey/turnkey.sh index bbfa7e128..6b53cf747 100644 --- a/turnkey/turnkey.sh +++ b/turnkey/turnkey.sh @@ -42,6 +42,29 @@ function msg() { local TEXT="$1" echo -e "$TEXT" } +function validate_container_id() { + local ctid="$1" + # Check if ID is numeric + if ! [[ "$ctid" =~ ^[0-9]+$ ]]; then + return 1 + fi + # Check if config file exists for VM or LXC + if [[ -f "/etc/pve/qemu-server/${ctid}.conf" ]] || [[ -f "/etc/pve/lxc/${ctid}.conf" ]]; then + return 1 + fi + # Check if ID is used in LVM logical volumes + if lvs --noheadings -o lv_name 2>/dev/null | grep -qE "(^|[-_])${ctid}($|[-_])"; then + return 1 + fi + return 0 +} +function get_valid_container_id() { + local suggested_id="${1:-$(pvesh get /cluster/nextid)}" + while ! validate_container_id "$suggested_id"; do + suggested_id=$((suggested_id + 1)) + done + echo "$suggested_id" +} function cleanup_ctid() { if pct status $CTID &>/dev/null; then if [ "$(pct status $CTID | awk '{print $2}')" == "running" ]; then @@ -99,9 +122,26 @@ turnkey=$(whiptail --backtitle "Proxmox VE Helper Scripts" --title "TurnKey LXCs # Setup script environment PASS="$(openssl rand -base64 8)" # Prompt user to confirm container ID - CTID=$(whiptail --backtitle "Container ID" --title "Choose the Container ID" --inputbox "Enter the conatiner ID..." 8 40 $(pvesh get /cluster/nextid) 3>&1 1>&2 2>&3) +while true; do + CTID=$(whiptail --backtitle "Container ID" --title "Choose the Container ID" --inputbox "Enter the container ID..." 8 40 $(pvesh get /cluster/nextid) 3>&1 1>&2 2>&3) + + # Check if user cancelled + [ -z "$CTID" ] && die "No Container ID selected" + + # Validate Container ID + if ! validate_container_id "$CTID"; then + SUGGESTED_ID=$(get_valid_container_id "$CTID") + if whiptail --backtitle "Container ID" --title "ID Already In Use" --yesno "Container/VM ID $CTID is already in use.\n\nWould you like to use the next available ID ($SUGGESTED_ID)?" 10 58; then + CTID="$SUGGESTED_ID" + break + fi + # User declined, loop back to input + else + break + fi +done # Prompt user to confirm Hostname - HOST_NAME=$(whiptail --backtitle "Hostname" --title "Choose the Hostname" --inputbox "Enter the containers Hostname..." 8 40 "turnkey-${turnkey}" 3>&1 1>&2 2>&3) +HOST_NAME=$(whiptail --backtitle "Hostname" --title "Choose the Hostname" --inputbox "Enter the containers Hostname..." 8 40 "turnkey-${turnkey}" 3>&1 1>&2 2>&3) PCT_OPTIONS=" -features keyctl=1,nesting=1 -hostname $HOST_NAME @@ -206,8 +246,8 @@ echo "TurnKey ${turnkey} password: ${PASS}" >>~/turnkey-${turnkey}.creds # file TUN_DEVICE_REQUIRED=("openvpn") # Setup this way in case future turnkeys also need tun access if printf '%s\n' "${TUN_DEVICE_REQUIRED[@]}" | grep -qw "${turnkey}"; then info "${turnkey} requires access to /dev/net/tun on the host. Modifying the container configuration to allow this." - echo "lxc.cgroup2.devices.allow: c 10:200 rwm" >> /etc/pve/lxc/${CTID}.conf - echo "lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file 0 0" >> /etc/pve/lxc/${CTID}.conf + echo "lxc.cgroup2.devices.allow: c 10:200 rwm" >>/etc/pve/lxc/${CTID}.conf + echo "lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file 0 0" >>/etc/pve/lxc/${CTID}.conf sleep 5 fi