Last active
October 3, 2025 10:30
-
-
Save diraneyya/137530405ece8cd4c2cef4a9a9fb65b6 to your computer and use it in GitHub Desktop.
A jump script for the mobile shell (Mosh) when used to connect to Proxmox containers/VMs via its root PVE node
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
| #!/usr/bin/env bash | |
| # The public IP address or the Dynamic DNS hostname of the | |
| # proxmox host, which can be resolved to a public IP address | |
| PROXMOX_HOST=host.dyndns.org | |
| # A single TCP port that is used for initiating a connection | |
| PROXMOX_TCP_PORT=2222 | |
| # A range of UDP ports that can be used for each Mosh session | |
| PROXMOX_UDP_PORTS=60001:60008 | |
| PROXMOSH_BIN=/root/.local/bin | |
| PROXMOSH_LOG=$PROXMOSH_BIN/proxmosh.log | |
| # This is a new feature I added considering how painful are | |
| # the qm and the pct commands to use. Instead, now you have | |
| # the choice of configuring SSH so that it can be used instead | |
| # to access the container/VM from the root PVE node. | |
| USE_SSH_WHEN_POSSILE=1 | |
| declare -A container=( | |
| [singular]='LXC container' | |
| [plural]='LXC containers' | |
| [base_command]='pct' | |
| [list_subcommand]='list' | |
| [jump_subcommand]='console' | |
| ) | |
| declare -A vm=( | |
| [singular]='VM' | |
| [plural]='VMs' | |
| [base_command]='qm' | |
| [list_subcommand]='list' | |
| [jump_subcommand]='terminal' | |
| ) | |
| technologies=(container vm) | |
| find_target() { | |
| # the first argument contains the name of the VM or the container | |
| if [[ -z "$1" ]]; then return 1; fi | |
| local return_code | |
| local output | |
| for ref in "${technologies[@]}"; do | |
| declare -n tech="$ref" | |
| output=$("${tech[base_command]}" \ | |
| "${tech[list_subcommand]}" | awk -v name="$1" ' | |
| function get_field(field_name) { | |
| temp = substr($0, col_pos[field_name]) | |
| match(temp, /\S+/) | |
| value = substr(temp, RSTART, RLENGTH) | |
| return value | |
| } | |
| NR == 1 { | |
| for (i = 1; i <= NF; i++) { | |
| col_pos[tolower($i)] = index($0, $i) | |
| } | |
| exit_code = -1 | |
| next | |
| } | |
| get_field("name") == name { | |
| vmid = get_field("vmid") | |
| status = get_field("status") | |
| if (status != "running") { | |
| print "Error: '"${tech[singular]}"' " name " is " status > "/dev/stderr" | |
| exit_code = 1 | |
| } else { | |
| print vmid | |
| exit_code = 0 | |
| } | |
| } | |
| END { | |
| if (exit_code == -1) { | |
| exit_code = 2; | |
| } | |
| exit exit_code | |
| }') | |
| return_code=$? | |
| if [[ $return_code -eq 0 ]]; then | |
| declare -gir TARGET_ID="$output"; | |
| declare -gr TARGET_TECH="$ref"; | |
| fi | |
| if [[ $return_code -ne 2 ]]; then | |
| break | |
| fi | |
| done | |
| if [[ $return_code -eq 2 ]]; then | |
| local plural_terms=() | |
| for ref in "${technologies[@]}"; do | |
| declare -n tech="$ref" | |
| plural_terms+=("${tech[plural]}") | |
| done | |
| local IFS='/' | |
| echo "Error: no ${plural_terms[*]} were found using the name '$1'." >&2 | |
| fi | |
| return $return_code | |
| } | |
| running_in_proxmox=1 | |
| for ref in "${technologies[@]}"; do | |
| declare -n tech="$ref" | |
| if ! command -v "${tech[base_command]}" &>/dev/null; then | |
| running_in_proxmox= | |
| fi | |
| done | |
| if [[ -n "$running_in_proxmox" ]]; then | |
| target_invoked= | |
| if [[ -n "$1" ]]; then | |
| if [[ -n "$USE_SSH_WHEN_POSSILE" ]]; then | |
| ssh -T "$1" true &>/dev/null | |
| if [[ $? -eq 0 ]]; then | |
| target_invoked=1 | |
| exec ssh "$1" | |
| fi | |
| fi | |
| if [[ -z "$target_invoked" ]] && find_target "$1"; then | |
| mkdir -p "$PROXMOSH_BIN" | |
| declare -n tech="$TARGET_TECH" | |
| jump_command="${tech[base_command]} ${tech[jump_subcommand]}" | |
| echo "$(date): $jump_command '$TARGET_ID'" >> "$PROXMOSH_LOG" | |
| target_invoked=1 | |
| $jump_command "$TARGET_ID" | |
| fi | |
| fi | |
| if [[ -z "$target_invoked" ]]; then | |
| exec /usr/bin/env bash -l | |
| fi | |
| else | |
| ssh -p $PROXMOX_TCP_PORT $PROXMOX_HOST "mkdir -p $PROXMOSH_BIN" | |
| ssh_command="ssh -p $PROXMOX_TCP_PORT" | |
| if command -v rsync &>/dev/null; then | |
| rsync -az -e "$ssh_command" "$0" $PROXMOX_HOST:"$PROXMOX_BIN/proxmosh" | |
| elif command -v scp &>/dev/null; then | |
| scp -P "$PROXMOX_TCP_PORT" "$0" $PROXMOX_HOST:"$PROXMOSH_BIN" | |
| else | |
| echo "Error: script requires either 'rsync' or 'scp' to be installed." >&2 | |
| exit 1 | |
| fi | |
| if [[ $? -ne 0 ]]; then | |
| exit 1 | |
| fi | |
| mosh --ssh="$ssh_command" -p "$PROXMOX_UDP_PORTS" $PROXMOX_HOST -- "$PROXMOSH_BIN/proxmosh" "${1:-}" | |
| fi |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Setup: Change the values of
PROXMOX_HOST,PROXMOX_TCP_PORT, andPROXMOX_UDP_PORTSto match your setup.Usage:
proxmoshto connect via Mosh to the root node, andproxmosh nameto connect to a container/VM with the namename. In case of preferring SSH, be sure to add the public PVE node SSH certificate toauthorized_hostson the container/VM and configure the user you wish to log in using in/root/.ssh/configon the root PVE node.For more information, check the supporting tutorial at https://forum.proxmox.com/threads/use-mobile-shell-mosh-with-proxmox.173209/