diff --git a/scripts/proxmox/create-proxmox-resource.sh b/scripts/proxmox/create-proxmox-resource.sh index 79a1455..c836c58 100755 --- a/scripts/proxmox/create-proxmox-resource.sh +++ b/scripts/proxmox/create-proxmox-resource.sh @@ -193,6 +193,16 @@ done ssh_target="${PROXMOX_SSH_USER}@${node}" +# Proxmox tools (pvesh, qm, pct) require root access to the cluster IPC +# socket. When SSH-ing as a non-root user with sudo, prefix every remote +# Proxmox command with sudo. +sudo_prefix="" +sudo_display="" +if [[ "$PROXMOX_SSH_USER" != "root" ]]; then + sudo_prefix="sudo" + sudo_display="sudo " +fi + remote() { if [[ "$dry_run" -eq 1 ]]; then echo "[dry-run] ssh ${ssh_target} -- $*" @@ -214,10 +224,10 @@ cmd_modify() { echo "Looking up VMID ${vmid} on ${node}..." local kind current_cores current_memory disk_key - if ssh "$ssh_target" "qm status ${vmid}" >/dev/null 2>&1; then + if ssh "$ssh_target" "${sudo_prefix} qm status ${vmid}" >/dev/null 2>&1; then kind="vm" disk_key="scsi0" - elif ssh "$ssh_target" "pct status ${vmid}" >/dev/null 2>&1; then + elif ssh "$ssh_target" "${sudo_prefix} pct status ${vmid}" >/dev/null 2>&1; then kind="lxc" disk_key="rootfs" else @@ -225,8 +235,8 @@ cmd_modify() { exit 1 fi - local config_cmd="qm config ${vmid}" - [[ "$kind" == "lxc" ]] && config_cmd="pct config ${vmid}" + local config_cmd="${sudo_prefix} qm config ${vmid}" + [[ "$kind" == "lxc" ]] && config_cmd="${sudo_prefix} pct config ${vmid}" local current_config current_config="$(ssh "$ssh_target" "$config_cmd")" current_cores="$(echo "$current_config" | grep -oP '^cores:\s*\K\S+' || echo '?')" @@ -250,9 +260,9 @@ cmd_modify() { exit 1 fi - local set_cmd="qm set" - local resize_cmd="qm resize" - [[ "$kind" == "lxc" ]] && set_cmd="pct set" && resize_cmd="pct resize" + local set_cmd="${sudo_prefix} qm set" + local resize_cmd="${sudo_prefix} qm resize" + [[ "$kind" == "lxc" ]] && set_cmd="${sudo_prefix} pct set" && resize_cmd="${sudo_prefix} pct resize" if [[ -n "$cores" || -n "$memory" ]]; then local args="" @@ -359,14 +369,15 @@ else echo echo "==> Checking ${node} for an existing VM/CT identified as '${host}'..." ssh_check_status=0 - existing="$(ssh "$ssh_target" bash -s -- "$host" <<'REMOTE_SCRIPT' + existing="$(ssh "$ssh_target" bash -s -- "$host" "$sudo_prefix" <<'REMOTE_SCRIPT' target="$1" -for id in $(qm list 2>/dev/null | awk 'NR>1{print $1}'); do - n="$(qm config "$id" 2>/dev/null | grep -oP '^name:\s*\K\S+' || true)" +sudo_pfx="$2" +for id in $($sudo_pfx qm list 2>/dev/null | awk 'NR>1{print $1}'); do + n="$($sudo_pfx qm config "$id" 2>/dev/null | grep -oP '^name:\s*\K\S+' || true)" [[ "$n" == "$target" ]] && echo "vm ${id} ${n}" done -for id in $(pct list 2>/dev/null | awk 'NR>1{print $1}'); do - n="$(pct config "$id" 2>/dev/null | grep -oP '^hostname:\s*\K\S+' || true)" +for id in $($sudo_pfx pct list 2>/dev/null | awk 'NR>1{print $1}'); do + n="$($sudo_pfx pct config "$id" 2>/dev/null | grep -oP '^hostname:\s*\K\S+' || true)" [[ "$n" == "$target" ]] && echo "lxc ${id} ${n}" done exit 0 @@ -453,12 +464,12 @@ REMOTE_SCRIPT if [[ "$kind" == "vm" ]]; then # qm destroy has no --force to stop-then-destroy in one call (pct's # does) -- stop explicitly first if it's running. - if ssh "$ssh_target" "qm status ${id}" 2>/dev/null | grep -q running; then - ssh "$ssh_target" "qm stop ${id}" + if ssh "$ssh_target" "${sudo_prefix} qm status ${id}" 2>/dev/null | grep -q running; then + ssh "$ssh_target" "${sudo_prefix} qm stop ${id}" fi - ssh "$ssh_target" "qm destroy ${id} --purge 1" + ssh "$ssh_target" "${sudo_prefix} qm destroy ${id} --purge 1" else - ssh "$ssh_target" "pct destroy ${id} --force 1 --purge 1" + ssh "$ssh_target" "${sudo_prefix} pct destroy ${id} --force 1 --purge 1" fi done fi @@ -487,7 +498,7 @@ if [[ -z "$vmid" ]]; then vmid="" echo "[dry-run] would ask ${node} for the next free VMID (pvesh get /cluster/nextid)" else - vmid="$(ssh "$ssh_target" "pvesh get /cluster/nextid" | tr -d '[:space:]')" + vmid="$(ssh "$ssh_target" "${sudo_prefix} pvesh get /cluster/nextid" | tr -d '[:space:]')" echo "Auto-assigned VMID: ${vmid}" fi else @@ -501,8 +512,8 @@ if [[ "$dry_run" -eq 0 ]]; then # both. Any success here means something is already using this ID -- # refuse to go anywhere near it. (Reconfiguring an existing resource is # --modify's job, not this one's.) - if ssh "$ssh_target" "qm status ${vmid}" >/dev/null 2>&1 \ - || ssh "$ssh_target" "pct status ${vmid}" >/dev/null 2>&1; then + if ssh "$ssh_target" "${sudo_prefix} qm status ${vmid}" >/dev/null 2>&1 \ + || ssh "$ssh_target" "${sudo_prefix} pct status ${vmid}" >/dev/null 2>&1; then echo "ERROR: VMID ${vmid} already exists on ${node}. Refusing to touch an" >&2 echo "existing resource here -- use --modify to reconfigure it, pick a" >&2 echo "different --vmid, or omit it to auto-assign." >&2 @@ -702,15 +713,6 @@ REMOTE_SCRIPT echo "Built on ${node}: ${remote_path}" fi else - # PROXMOX_SSH_USER defaults to root (env.sh), which needs no sudo and - # can't assume it's even installed on a minimal node -- only shell out - # through sudo when actually running as a non-root SSH user. - sudo_prefix="sudo" - sudo_display="sudo " - if [[ "$PROXMOX_SSH_USER" == "root" ]]; then - sudo_prefix="" - sudo_display="" - fi if [[ "$dry_run" -eq 1 ]]; then echo "[dry-run] would build on ${node}: nix build --no-use-registries --no-accept-flake-config${nix_opts_display} \\" echo "[dry-run] .#nixosConfigurations.${flake_target}.config.system.build.diskoImagesScript" @@ -812,9 +814,9 @@ if [[ "$type" == "lxc" ]]; then # hands the whole string to `ssh` as a single command for the *remote* # shell to parse -- unquoted, that `;` would be read as a remote # command separator and silently truncate this into two commands. - create_cmd="pct create ${vmid} ${iso_storage}:vztmpl/${remote_filename} --unprivileged ${unprivileged_flag} --features '${PROXMOX_DEFAULT_LXC_FEATURES}' --rootfs ${storage}:${local_disk_size} --hostname ${name} --cores ${cores} --memory ${memory} --swap ${local_swap} --net0 name=eth0,bridge=${bridge},ip=dhcp" + create_cmd="${sudo_prefix} pct create ${vmid} ${iso_storage}:vztmpl/${remote_filename} --unprivileged ${unprivileged_flag} --features '${PROXMOX_DEFAULT_LXC_FEATURES}' --rootfs ${storage}:${local_disk_size} --hostname ${name} --cores ${cores} --memory ${memory} --swap ${local_swap} --net0 name=eth0,bridge=${bridge},ip=dhcp" remote "$create_cmd" - remote "pct start ${vmid}" + remote "${sudo_prefix} pct start ${vmid}" else echo "==> Creating VM ${vmid} (${name})..." # pre-enrolled-keys=0 disables OVMF's Secure Boot key pre-enrollment -- @@ -825,29 +827,29 @@ else # without this flag Proxmox never creates the channel it listens on, so # `qm guest exec`/`qm agent` and the UI's IP-address display silently # never work for any VM this script creates. - remote "qm create ${vmid} --name ${name} --memory ${memory} --cores ${cores} \ + remote "${sudo_prefix} qm create ${vmid} --name ${name} --memory ${memory} --cores ${cores} \ --net0 virtio,bridge=${bridge} --bios ovmf --machine q35 --scsihw virtio-scsi-pci \ --efidisk0 ${storage}:1,efitype=4m,pre-enrolled-keys=0 --agent enabled=1" if [[ "$dry_run" -eq 1 ]]; then - echo "[dry-run] ssh ${ssh_target} -- qm importdisk ${vmid} ${remote_path} ${storage}" + echo "[dry-run] ssh ${ssh_target} -- ${sudo_display}qm importdisk ${vmid} ${remote_path} ${storage}" echo "[dry-run] (would parse the resulting disk identifier from that output)" - echo "[dry-run] ssh ${ssh_target} -- qm set ${vmid} --scsi0 ${storage}:" + echo "[dry-run] ssh ${ssh_target} -- ${sudo_display}qm set ${vmid} --scsi0 ${storage}:" else - importdisk_output="$(ssh "$ssh_target" "qm importdisk ${vmid} ${remote_path} ${storage}")" + importdisk_output="$(ssh "$ssh_target" "${sudo_prefix} qm importdisk ${vmid} ${remote_path} ${storage}")" echo "$importdisk_output" disk_id="$(echo "$importdisk_output" | grep -oP "(?<=Successfully imported disk as ')[^']+" | sed 's/^unused[0-9]*://')" if [[ -z "$disk_id" ]]; then echo "ERROR: couldn't parse the imported disk identifier from qm importdisk's output above." >&2 echo "The VM shell (${vmid}) and imported disk both exist -- finish attaching it by hand:" >&2 - echo " ssh ${ssh_target} -- qm set ${vmid} --scsi0 ${storage}:" >&2 - echo " ssh ${ssh_target} -- qm set ${vmid} --boot order=scsi0" >&2 + echo " ssh ${ssh_target} -- ${sudo_display}qm set ${vmid} --scsi0 ${storage}:" >&2 + echo " ssh ${ssh_target} -- ${sudo_display}qm set ${vmid} --boot order=scsi0" >&2 exit 1 fi - remote "qm set ${vmid} --scsi0 ${disk_id}" + remote "${sudo_prefix} qm set ${vmid} --scsi0 ${disk_id}" fi - remote "qm set ${vmid} --boot order=scsi0" - remote "qm start ${vmid}" + remote "${sudo_prefix} qm set ${vmid} --boot order=scsi0" + remote "${sudo_prefix} qm start ${vmid}" fi echo