Last active
February 11, 2024 17:57
-
-
Save apolopena/1d31bea89bfed63182e784d5494b9d37 to your computer and use it in GitHub Desktop.
Securely launch Proxmox VM's in Spice Viewer from the command line
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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