aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xmain.sh245
1 files changed, 183 insertions, 62 deletions
diff --git a/main.sh b/main.sh
index c725042..98f2c19 100755
--- a/main.sh
+++ b/main.sh
@@ -1,76 +1,192 @@
#!/bin/bash
function usage() {
- echo "SYNOPSYS
- $0 [OPTIONS]
+ echo \
+"SYNOPSYS
+ $0 (-S|--ssh-host) SSHHOST (-t|--tunnel) TUNNELSPEC [OPTIONS]
DESCRIPTION
Spawn TCP tunnels between your computer and a remote SSH server. If the
connection fail for some reason, the script will try to re-create the
tunnels automatically every TIMEOUT seconds.
OPTIONS
- -S,--server=HOST
- Set the tunnel host. HOST can be either the IP or hostname of the
- server to tunnel to, as seen from the ssh host.
-
- -t, --tunnel=TUNNELPORT
- Specify the ports to tunnel.
-
- -i, --interface=IF
- Specify the interface to listen on.
-
- -H,--host=SSHHOST
+ -S,--ssh-host SSHHOST
Set the ssh host. SSHHOST can be either the full hostname as you would
- specify it on the ssh command-line (e.g. user@example.com or hostname
+ specify it on the ssh command-line (e.g. [user@]myserver.com or hostname
if it is set up in your ssh config).
- -p, --port=SSHPORT
+ -t,--tunnel TUNNELSPEC
+ Specify the ports to tunnel as a comma-separated list of either single
+ ports or port pairs (e.g. -t 443,80:8080 will establish tunnels
+ local:443-remote:443 and local:8080-remote:80). Note that you have to
+ be a privileged user (i.e. a user with the CAP_NET_BIND_SERVICE
+ capability) to be able to bind to ports below 1024.
+
+ -p,--ssh-port SSHPORT /!\\ Not implemented
Set the ssh remote port if different from the default.
+ -H,--host HOST
+ Set the tunnel host. HOST can be either the IP or hostname of the
+ server to tunnel to, as seen from the ssh host. Defaults to localhost.
+
+ -i,--interface IF /!\\ Not implemented
+ Specify the local interface to bind to. Defaults to localhost. Setting
+ it to 0.0.0.0 will make the tunnel available to other clients on your
+ network (depending on your firewall rules).
+
-f
Fork the script to the background.
-h,--help
Print this help and exit.
+EXAMPLES
+ Todo
+AUTHOR
+ Clément Zrounba
"
}
-# Help argument
-if [[ " $@" = *" -h"* || " $@" = *" --help"* ]]; then
+function parse_args() {
+ ARG_POS_PARAMS=""
+ while (( "$#" )); do
+ case "$1" in
+ -f|--fork)
+ ARG_FORK=0
+ shift
+ ;;
+ -h|--help)
+ ARG_HELP=0
+ shift
+ ;;
+ -H|--host)
+ if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
+ ARG_HOST=$2
+ shift 2;
+ else
+ echo "Error: Argument for $1 is missing" >&2
+ exit 1
+ fi
+ ;;
+ -S|--ssh-host)
+ if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
+ ARG_SSHHOST=$2
+ shift 2;
+ else
+ echo "Error: Argument for $1 is missing" >&2
+ exit 1
+ fi
+ ;;
+ -S*)
+ if [ -n "${1:2}" ]; then
+ ARG_SSHHOST=${1:2}
+ shift 1;
+ else
+ echo "Error: Option not understood: $1" >&2
+ exit 1
+ fi
+ ;;
+ --ssh-host=*)
+ if [ -n "${1:2}" ]; then
+ ARG_SSHHOST=${1:11}
+ shift 1;
+ else
+ echo "Error: Option not understood: $1" >&2
+ exit 1
+ fi
+ ;;
+ -t|--tunnel)
+ if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then
+ ARG_TUNNELSPEC=$2
+ shift 2;
+ else
+ echo "Error: Argument for $1 is missing" >&2
+ exit 1
+ fi
+ ;;
+ -*)
+ echo "Error: Unsupported flag $1" >&2
+ exit 1
+ ;;
+ *) # preserve positional arguments
+ ARG_POS_PARAMS="$ARG_POS_PARAMS $1"
+ shift
+ ;;
+ esac
+ done
+
+ # Check SSHHOST is given
+ if [ -z "$ARG_SSHHOST" ]; then
+ echo "Error: no ssh host specified" >&2
+ exit 1
+ fi
+
+ # Check TUNNELSPEC is given
+ if [ -z "$ARG_TUNNELSPEC" ]; then
+ echo "Error: no tunnel(s) specified" >&2
+ exit 1
+ fi
+}
+
+function check_low_port_cap()
+{
+ [ $(id -u) == 0 ] && echo "yes" && return
+ # Check capability as well ?
+
+ echo "no"
+}
+
+function parse_tunnel_spec() {
+ local tunnel_spec=(${1//,/ })
+ local can_access_low_local_ports=$(check_low_port_cap)
+
+ declare -ag _TUNNEL_PORTS_LOCAL
+ declare -ag _TUNNEL_PORTS_REMOTE
+
+ for tunnel_spec_entry in ${tunnel_spec[*]}; do
+ local ports=(${tunnel_spec_entry/:/ })
+
+ local local_port=${ports[0]}
+ local remote_port=${ports[1]:-$local_port}
+
+ echo "Local endpoint $BIND_IF:$local_port will be tunnelled to remote endpoint $TUNNEL_HOST:$remote_port"
+
+ if [ $local_port -lt 1024 ] && [ $can_access_low_local_ports == "no" ]; then
+ echo "Error: local port below 1024 specified but running as an unprivileged user" >&2
+ exit 1
+ fi
+
+ _TUNNEL_PORTS_LOCAL+=($local_port)
+ _TUNNEL_PORTS_REMOTE+=($remote_port)
+ done
+}
+
+# Parse command line arguments into ARG_* variables
+parse_args $@
+
+# Make positional parameters accessible through $1, $2, ...
+eval set -- "$ARG_POS_PARAMS"
+
+# Print help and exit if requested
+if [ 0 == "$ARG_HELP" ]; then
usage
exit 0
fi
-#if [[ -z "$@" || " $@" != *" --ok"* ]]; then
-# echo 'This script will only do something if `--ok` is provided'
-# echo ''
-# usage
-# echo ''
-# echo 'Nothing has been done. Exiting.'
-# exit 1
-#fi
+# Assign values or defaults to other parameters from ARG_* variables
+SSH_HOST=${ARG_SSHHOST:-}
+BIND_IF=${ARG_BIND_IF:-localhost}
+TUNNEL_HOST=${ARG_HOST:-localhost}
+SSH_PORT=${ARG_SSHPORT:-22}
+DO_FORK=$([ "$ARG_FORK" == "0" ] && echo "yes" || echo "no")
+
+# Parse tunnel args into _TUNNEL_PORTS_* variables
+parse_tunnel_spec $ARG_TUNNELSPEC
+TUNNEL_PORTS_LOCAL=(${_TUNNEL_PORTS_LOCAL[*]})
+TUNNEL_PORTS_REMOTE=(${_TUNNEL_PORTS_REMOTE[*]})
# Logfile location
# comment to use stdout/stderr
-LOGFILE=proxy.log
-LOGFILE_ERR=proxy.log
-
-# Local bind address
-# - Use 127.0.0.1 for local computer only;
-# - Use 0.0.0.0 to give access to other computers (but be careful who can
-# reach your computer in that case, if you do not have a firewall, your LAN
-# (at least) normally has access to all your ports (most routers, through
-# NAT, provide basic firewall functionality))
-#LOCAL_BIND_ADDR=0.0.0.0
-LOCAL_BIND_ADDR=127.0.0.1
-
-# License host and ports
-REMOTE_LICENSE_HOST=licence.inl90.ec-lyon.fr
-DASHBOARD_PORT=8095
-FLEX_PORT=27011
-LUMERICAL_VENDOR_PORT=42128
-
-# ssh host
-HOST=sshgate
+#LOGFILE=proxy.log
+#LOGFILE_ERR=proxy.log
# set default log to stdout/stderr
LOGFILE=${LOGFILE:-/dev/stdout}
@@ -79,32 +195,37 @@ LOGFILE_ERR=${LOGFILE_ERR:-/dev/stderr}
function start_and_monitor_tunnels() {
# This function takes port numbers as arguments and creates as many TCP
# tunnels like so:
- # LOCAL_BIND_ADDR:$port <--> HOST <--> REMOTE_LICENSE_HOST:$port
+ # BIND_IF:$local_port <--> SSH_HOST <--> TUNNEL_HOST:$remote_port
# If the tunnel fails or ssh exits, we either:
# - try again after a timeout if exit code is not 0
# - exit if exit code is 0
# NOTE: for now we just **always** try again after timeout
- local ports=$@
- local timeout_sec=10
+ local timeout_sec=${1:-10}
+
+ local ntunnels=${#TUNNEL_PORTS_LOCAL[*]}
declare -a ssh_tunnel_opt
- for port in ${ports}; do
+ for i in `seq 0 $(($ntunnels - 1))`; do
+ local local_port=${TUNNEL_PORTS_LOCAL[$i]}
+ local remote_port=${TUNNEL_PORTS_REMOTE[$i]}
+
ssh_tunnel_opt+=("-L")
- ssh_tunnel_opt+=("${LOCAL_BIND_ADDR}:${port}:${REMOTE_LICENSE_HOST}:${port}")
+ ssh_tunnel_opt+=("${BIND_IF}:${local_port}:${TUNNEL_HOST}:${remote_port}")
done
while true; do
# Try establishing the tunnel
- echo "Starting tunnel for ports ${ports[@]}"
+ echo -n "Starting tunnel... "
ssh -N \
- ${ssh_tunnel_opt[@]} \
+ ${ssh_tunnel_opt[*]} \
-o "ServerAliveInterval 10" \
-o "ServerAliveCountMax 3" \
-o "ExitOnForwardFailure yes" \
- ${HOST} &
+ ${SSH_HOST} &
local pid="$!"
# make sure to kill ssh when killed/parent exits
trap "kill '$pid'" EXIT
+ echo "OK"
wait
local exit_code="$?"
@@ -122,11 +243,13 @@ function start_and_monitor_tunnels() {
}
# Start the tunnels and redirect outputs
-echo "Setting up tunnels.
-Logs will be redirected to:
- stdout --> ${LOGFILE}
- stderr --> ${LOGFILE_ERR}
-"
+if [ ${LOGFILE} != "/dev/stdout" ] || [ ${LOGFILE_ERR} != "/dev/stderr" ]; then
+ echo "\
+ Logs will be redirected to:
+ stdout --> ${LOGFILE}
+ stderr --> ${LOGFILE_ERR}
+ "
+fi
# In the following block, the following redirections are set up:
# - 777 goes to stdout of current script (and not global /dev/stdout)
@@ -134,7 +257,7 @@ Logs will be redirected to:
# - 2 goes to $LOGFILE_ERR
{
# Enable trace
- set -x
+ #set -x
# Set up CTRL-C callback (write to stdout of script, not log)
trap_ctrl_c_callback="
@@ -143,7 +266,5 @@ Logs will be redirected to:
"
trap "${trap_ctrl_c_callback}" SIGINT
- ports=(${DASHBOARD_PORT} ${FLEX_PORT} ${LUMERICAL_VENDOR_PORT})
- start_and_monitor_tunnels ${ports[@]}
-
+ start_and_monitor_tunnels
} 777>&1 1>>${LOGFILE} 2>>${LOGFILE_ERR}