Last active
May 21, 2021 10:36
-
-
Save nimaid/8ccc99065a19f0db42bab50159acbb3a to your computer and use it in GitHub Desktop.
Reverse SSH Tunnel Port Forwarding Helper
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 | |
| set -e | |
| echo | |
| echo "Reverse SSH Tunnel Port Forwarding Helper" | |
| echo "v0.8.2 by nimaid" | |
| echo | |
| function usage | |
| { | |
| echo "Usage: $(basename $0) -p PORTLIST -s HOST [-u USER -k KEY]" | |
| echo " " | |
| echo " -p | --ports Comma seperated list of ports or port pairs" | |
| echo " Port pairs are in the format local:remote" | |
| echo " Single ports are equivalent to port:port" | |
| echo " Example: -p 80:1337,8080:31337,8888" | |
| echo " -s | --server The port forwarding server address" | |
| echo " -u | --user Username to log in with" | |
| echo " -k | --key Path to private key file" | |
| echo " " | |
| echo " -h | --help Display this message" | |
| exit | |
| } | |
| function parse_args | |
| { | |
| # positional args | |
| args=() | |
| # named args | |
| while [ "$1" != "" ]; do | |
| case "$1" in | |
| -p | --ports ) IN_PORTS="$2"; shift;; | |
| -s | --server ) IN_HOST="$2"; shift;; | |
| -u | --user ) IN_USER="$2"; shift;; | |
| -k | --key ) IN_KEY="$2"; shift;; | |
| -h | --help ) usage; exit;; # quit and show usage | |
| * ) args+=("$1") # if no match, add it to the positional args | |
| esac | |
| shift # move to next kv pair | |
| done | |
| # restore positional args | |
| set -- "${args[@]}" | |
| # don't allow extra arguments | |
| if [ "${#args[@]}" -gt "0" ]; then | |
| echo "Too many arguments" | |
| echo | |
| usage | |
| fi | |
| # validate required args | |
| if [[ -z "${IN_PORTS}" || -z "${IN_HOST}" ]]; then | |
| echo "Missing required argument(s)" | |
| echo | |
| usage | |
| fi | |
| # set defaults | |
| if [[ -z "$IN_USER" ]]; then | |
| IN_USER=""; | |
| fi | |
| } | |
| function is_valid_port() { | |
| if [ -z "$1" ]; then | |
| false | |
| elif [ -n "$1" ] && [ "$1" -eq "$1" ] 2> /dev/null; then | |
| if [ "$1" -gt "0" ] && [ "$1" -le "65535" ]; then | |
| true | |
| else | |
| false | |
| fi | |
| else | |
| false | |
| fi | |
| } | |
| function is_valid_user_port() { | |
| if [ -z "$1" ]; then | |
| false | |
| elif [ -n "$1" ] && [ "$1" -eq "$1" ] 2> /dev/null; then | |
| if [ "$1" -ge "1024" ] && [ "$1" -le "65535" ]; then | |
| true | |
| else | |
| false | |
| fi | |
| else | |
| false | |
| fi | |
| } | |
| function is_valid_ip() | |
| { | |
| local ip=$1 | |
| local stat=1 | |
| if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then | |
| OIFS=$IFS | |
| IFS='.' | |
| ip=($ip) | |
| IFS=$OIFS | |
| [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \ | |
| && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]] | |
| stat=$? | |
| fi | |
| return $stat | |
| } | |
| function check_ping() { | |
| if $(ping -c 1 $1 &> /dev/null); then | |
| true | |
| else | |
| false | |
| fi | |
| } | |
| function resolve_ip() { | |
| if [ -z "$1" ]; then | |
| return | |
| elif is_valid_ip $1; then | |
| echo $1 | |
| elif ! check_ping $1; then | |
| return | |
| else | |
| RESOLVED_IP=$(dig +short $1) | |
| if [ -z "$RESOLVED_IP" ]; then | |
| RESOLVED_IP=$1 | |
| fi | |
| echo $RESOLVED_IP | |
| return | |
| fi | |
| } | |
| parse_args "$@" | |
| IFS="," read -ra PORTSETS <<< $IN_PORTS | |
| FWHOST=$IN_HOST | |
| FWARGS= | |
| FWMSG= | |
| for PORTSET in ${PORTSETS[@]}; do | |
| IFS=":" read -ra PORTS <<< $PORTSET | |
| NUMPORTS=${#PORTS[@]} | |
| if [ "$NUMPORTS" -eq "1" ]; then | |
| LPORT=${PORTS} | |
| RPORT=${PORTS} | |
| elif [ "$NUMPORTS" -eq "2" ]; then | |
| LPORT=${PORTS[0]} | |
| RPORT=${PORTS[1]} | |
| else | |
| echo "Misformatted port pair(s)" | |
| usage | |
| fi | |
| if ! is_valid_port $LPORT; then | |
| echo "Local port $LPORT is not a valid integer in range 1-65535" | |
| echo | |
| usage | |
| fi | |
| if ! is_valid_user_port $RPORT; then | |
| echo "Remote port $LPORT is not a valid integer in range 1024-65535" | |
| echo | |
| usage | |
| fi | |
| FWARGS="$FWARGS -R ${RPORT}:localhost:${LPORT}" | |
| FWMSG="${FWMSG}Forwarding localhost:${LPORT} => ${FWHOST}:${RPORT}\n" | |
| done | |
| SSHUSER=$IN_USER | |
| KEYFILE=$IN_KEY | |
| if [ ! -z "$KEYFILE" ] && [ ! -f "$KEYFILE" ]; then | |
| echo "Private key file is non-existent" | |
| usage | |
| fi | |
| if check_ping $FWHOST; then | |
| echo "Host $FWHOST is reachable." | |
| else | |
| echo "Host $FWHOST is not reachable!" | |
| exit 1 | |
| fi | |
| echo | |
| printf "$FWMSG" | |
| FWHOST_IP=$(resolve_ip $FWHOST) | |
| if [ "$FWHOST_IP" != "$FWHOST" ]; then | |
| echo | |
| echo "$FWHOST => $FWHOST_IP" | |
| fi | |
| echo | |
| if [ -z "$SSHUSER" ]; then | |
| FULLFWHOST=$FWHOST | |
| else | |
| echo "Logging in with user account ${SSHUSER}" | |
| FULLFWHOST=${SSHUSER}@${FWHOST} | |
| if [ ! -z "$KEYFILE" ]; then | |
| echo "Using private key file ${KEYFILE}" | |
| fi | |
| echo | |
| fi | |
| if $(command -v autossh &> /dev/null); then | |
| echo "autossh found, will auto-reconnect..." | |
| SSH_CLIENT=autossh | |
| else | |
| echo "autossh not found, using ssh \(will not auto-reconnect\)..." | |
| SSH_CLIENT=ssh | |
| fi | |
| FWCMD="$SSH_CLIENT -nNT $FWARGS" | |
| if [ ! -z "$KEYFILE" ]; then | |
| FWCMD="$FWCMD -i "$KEYFILE"" | |
| fi | |
| $FWCMD $FULLFWHOST |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment