Skip to content

Instantly share code, notes, and snippets.

@apolopena
Last active February 11, 2024 17:57
Show Gist options
  • Save apolopena/1d31bea89bfed63182e784d5494b9d37 to your computer and use it in GitHub Desktop.
Save apolopena/1d31bea89bfed63182e784d5494b9d37 to your computer and use it in GitHub Desktop.
Securely launch Proxmox VM's in Spice Viewer from the command line
#!/bin/bash
# Globals
version='1.1.1'
username=
password=
vmid=
node=
proxy=
verbose=
log=
usage() {
echo -e "\nUsage: lspicevm [-u <string>][-L <string>][-h][-v][-d] vmid [node [proxy]]"
echo "Options:"
echo " -u proxmox username. Defaults to: root@pam"
echo " -h display this help message"
echo " -L set the log location of Spice remote-viewer"
echo " -v show version information"
echo " -d run in verbose mode for debugging"
echo "Arguments:"
echo " vmid: integer id for VM (required)"
echo " node: Proxmox cluster node name (use hostname -f as default)"
echo -e " proxy: DNS or IP (use <node> as default)\n"
}
version() {
local decor="###################################################"
echo -e "\n${decor}\nlspicevm: Spice viewer launcher for Proxmox VE VM's"
echo "Version: ${version}"
echo "Requires: pve-manager >= 3.1-44"
echo "License: MIT"
echo -e "Written by: Apolo Pena (C) 2024\n${decor}\n"
}
err() {
echo -e "lspicevm ERROR: $1\n" >&2 && usage && exit 1
}
fail() {
echo -e "ERROR: $1\n" >&2 && exit 1
}
parse_args() {
[[ -z "$1" ]] && err 'Missing required <vmid> argument'
[[ "$1" =~ ^[0-9]+$ ]] || err "invalid <vmid>: $1\n <vmid> must be an integer"
vmid="$1"
node="$2"
proxy="$3"
[[ -z "$username" ]] && username='root@pam'
[[ -z "$node" ]] && node="$(hostname -f)" && node="${node%%\.*}"
[[ -z "$proxy" ]] && proxy="127.0.0.1"
}
launch() {
local ec ticket_url api_url data ticket csrf spiceproxy
local fail_msg="failed to fork remote-viewer process"
ticket_url="https://${proxy}:8006/api2/json/access/ticket"
data="$(curl -f -s -S -k --data-urlencode "username=${username}" --data-urlencode "password=${password}" "${ticket_url}")"
if [[ $verbose == true ]]; then
echo "PROXMOX AUTH OK"
fi
ticket="${data//\"/}"
ticket="${ticket##*ticket:}"
ticket="${ticket%%,*}"
ticket="${ticket%%\}*}"
csrf="${data//\"/}"
csrf="${csrf##*CSRFPreventionToken:}"
csrf="${csrf%%,*}"
csrf="${csrf%%\}*}"
api_url="https://$proxy:8006/api2/spiceconfig/nodes/$node/qemu/$vmid/spiceproxy"
[[ $(is_vm_running) == "false" ]] && sudo qm start "${vmid}"
curl -f -s -S -k -b "PVEAuthCookie=$ticket" -H "csrfPreventionToken: $csrf" "${api_url}" -d "proxy=$proxy" > spiceproxy
ec=$?
[[ $verbose == true ]] && debug_full "$ticket_url" "$api_url" "$ticket" "$csrf" "$data"
if [[ $ec -eq 0 ]]; then
[[ $verbose == true ]] && echo -e "SUCCESS: spice proxy file downloaded.\nFile contents:\n" && cat spiceproxy
if [[ -z "$log" ]]; then
( (nohup remote-viewer -f spiceproxy >/dev/null 2>&1 &) ) || fail "${fail_msg}"
else
( (nohup remote-viewer -f spiceproxy > "$log" 2>&1 &) ) || fail "${fail_msg}"
fi
else
fail "failed to download spice proxy"
fi
}
is_vm_running() {
[[ $(sudo qm status "${vmid}" | grep stopped) ]] && echo "false" && return
echo "true"
}
prompt_pw() {
echo -n "Enter the proxmox password for user: ${username} "
read -s password && echo
}
has_option() {
for opt in "$@"
do
[[ "$opt" =~ ^- ]] && echo "true" && return
done
echo "false"
}
debug_args() {
echo "USERNAME: ${username}"
echo "VMID: ${vmid}"
echo "NODE: ${node}"
echo "PROXY: ${proxy}"
}
debug_full() {
debug_args
echo "AUTH TICKET URL: $1"
echo "API URL: $2"
echo "AUTH TICKET: $3"
echo "CSRF: $4"
echo "JSON DATA: $5"
}
main() {
parse_args "$@" && prompt_pw && launch
}
# Handle arguments and options
if [[ $(has_option "$@") == "true" ]]; then
[[ "$#" -gt 1 ]] &&
[[ ! "$1" =~ ^- ]] &&
err "arguments cannot come before options"
fi
while getopts "hvdu:L:" option; do
case "${option}" in
h) usage && exit ;;
v) version && exit 0 ;;
d) verbose="true" ;;
u) username="${OPTARG}" ;;
L) log="${OPTARG}" ;;
\?) exit 1 ;;
:) err "option -$OPTARG requires an argument" ;;
esac
done
shift "$((OPTIND - 1))"
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment