#!/bin/bash CONFDIR="$HOME/.config/ssh-tunnelier" CONF="$CONFDIR/config" MAGIC_TIME=873749328 function _helpexit() { echo "SSH tunnel manager Runs and monitors preconfigured background ssh tunnels, with the ability to kill existing ssh processes. You don't need to keep the program on to remember connected sessions. Configuration stored in: $CONF If run with arguments, runs an ssh tunnel with arguments: ssh-tunnelier hostname [local-port] remote-port If local port missing, the same port assumed in local " exit 0 } for (( i=1; i<=$#; i++ )); do [[ ${!i} = "-h" ]] && _helpexit [[ ${!i} = "--help" ]] && _helpexit done [[ -f "$CONF" ]] || { echo No config file. echo "$CONF" each line like: echo -L 8888:localhost:8888 servername mkdir -p "$CONFDIR" echo -e "# example:\n -L 8888:localhost:8888 servername" > "$CONF" } number_re='^[0-9]+$' colZ="\033[0m" colTitle="\033[0;1;32m" colRow="\033[0;1m" colMenu="\033[0;33m" colWarn="\033[0;1;31m" function fpgrep() { pgrep -x -u $UID "$@" | xargs --no-run-if-empty ps -o pid,bsdstart,args; test ${PIPESTATUS[0]} -ne 0 && echo '----' } function get_id() { id=$( printf "%s" "$line" | base64 -w 0 ) echo -n $id } function get_command() { switches=$( echo "$1" | base64 -d ) echo -n "ssh -f -n $switches \"sleep $MAGIC_TIME; echo tunneler $1\"" } function get_pid() { pgrep -f "$MAGIC_TIME.*echo tunneler $1" | head -n 1 } function list_all_ssh() { printf "\n${colTitle}=============================================\n" printf "${colTitle} List of SSH:${colZ}\n" printf "${colTitle}Enter PID to kill, empty to return $colZ\n" printf "${colTitle}Outgoing SSH processes\n" printf "${colTitle}=============================================\n${colRow}" fpgrep ssh printf "${colTitle}Incoming SSH processes\n" printf "${colTitle}=============================================\n${colRow}" fpgrep sshd read -t 600 inputpid [[ "$inputpid" =~ $number_re ]] && { ask_to_kill $inputpid } } function read_config() { while read line; do id=$( get_id "$line" ) pid=$( get_pid "$id" ) printf "%-3d %-7s %s\n" $i "$pid" "$line" ids+=( $id ) i=$(( i + 1 )) done < <( grep -v ^# "$CONF" | grep '[a-zA-Z]' ) } function run_args() { HOST="$1" LOCAL="$2" REMOTE="$3" [[ -z "$REMOTE" ]] && REMOTE="$LOCAL" echo Connect to $HOST ssh -f -n -L "${LOCAL}:localhost:${REMOTE}" "$HOST" "sleep $MAGIC_TIME" } function run_command() { eval $( get_command "$1" ) } function ask_to_kill() { printf "${colRow}\nPID: $1\n k kill\n t terminate${colZ}\n" read -t 600 input2 [[ "$input2" = "k" ]] && kill $1 [[ "$input2" = "t" ]] && kill -9 $1 } [[ -n "$2" ]] && { run_args "$@" } while true; do ids=() i=1 printf "\n${colTitle}=============================================\n" printf "${colTitle} List of tunnels:${colZ}\n" printf "${colTitle} (q)uit (e)dit (l)ist ssh [%s]${colZ}\n" $( date +%H:%M ) printf "${colTitle}=============================================\n" printf "${colRow}ID PID command\n" read_config printf "$colZ" read -t 600 input [[ "$input" = "q" ]] && exit 0 [[ "$input" = "e" ]] && vim "$CONF" [[ "$input" = "l" ]] && list_all_ssh [[ "$input" = "0" ]] && continue [[ "$input" =~ $number_re ]] && { j=$(( $input - 1 )) [[ $j -gt $(( $i - 1 )) ]] && { printf "${colWarn}No such tunnel number${colZ}\n" continue } printf "\n${colTitle}Tunnel command: %s${colZ}\n" "$( echo "${ids[$j]}" | base64 -d )" this_pid="$( get_pid "${ids[$j]}" )" [[ -n "$this_pid" ]] && { # PID exists, ask to kill ask_to_kill "$this_pid" } [[ -z "$this_pid" ]] && { # PID empty, run run_command "${ids[$j]}" sleep 1 } } done