-
Notifications
You must be signed in to change notification settings - Fork 34
/
ssh-tunnel
executable file
·104 lines (92 loc) · 3.11 KB
/
ssh-tunnel
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
#!/bin/bash
usage() {
bin=$(basename $0)
echo >&2 "Usage:"
echo >&2 " $bin [opts] <ssh -L forwarding spec> <ssh destination spec> [<ssh opts>]"
echo >&2 " $bin <ssh -L forwarding spec> <ssh destination spec> { kill | -k }"
echo >&2
echo >&2 "Starts non-interactive ssh -L tunnel through specified destination host,"
echo >&2 " saving pid file in $XDG_RUNTIME_DIR,"
echo >&2 " which can be used to stop tunnel via kill/-k option."
echo >&2 "Example: $bin 8080:127.0.0.1:80 root@my.example.host"
echo >&2
echo >&2 "Options:"
echo >&2 " -1 - oneshot - start tunnel in the foreground."
echo >&2 " Can be useful when setting up tunnel for the first time."
echo >&2 " -t - tping - use 'tping' tool before running ssh, to avoid restarting ssh"
echo >&2 " every interval when remote host is more likely to be down than otherwise."
echo >&2 " -r - reverse-ssh - run reverse ssh tunnel (using -R option instead of -L)."
echo >&2 " -i <interval> - interval between connection attempts"
echo >&2 " in seconds (default=5), applied to both ssh/tping."
echo >&2 " -x - debug - Use 'set -x' at the start of the script."
exit ${1:-0}
}
[[ "$#" -ge 2 ]] || usage 1
[[ "$1" = -h || "$1" = --help ]] && usage
oneshot= tping= interval=5 tun=-L
while getopts ":h1txi:r" opt
do case "$opt" in
h) usage 0 ;;
i) [[ $OPTARG =~ ^[0-9]+$ ]] && interval=$OPTARG \
|| { echo >&2 -e "ERROR: Invalid -i interval value: $OPTARG\n"; usage 1; } ;;
1) oneshot=t ;;
t) tping=t ;;
r) tun=-R ;;
x) set -x ;;
*) { echo >&2 -e "ERROR: Invalid option: -$OPTARG\n"; usage 1; } ;;
esac; done
shift $((OPTIND-1))
fwd=$1 dst=$2
shift; shift
pid_file="$XDG_RUNTIME_DIR/.$(basename $0)"`
`".$(echo "$fwd.$dst" | md5sum | cut -b-5)"
[[ "$1" = kill || "$1" = -k ]] && {
pid=$(cat "$pid_file" 2>/dev/null)
[[ -n "$pid" ]] || exit 1
flock -n 3 3<"$pid_file" && exit 1
pgrp=$(ps -o pgrp= "$pid" | tr -d '\040\011\012\015')
[[ -n "$pgrp" && "$pgrp" -gt 1 ]] || exit 1
exec kill -- -"$pgrp"
}
OPTIND=0 ssh_port= ssh_opts_ext=( "$@" )
while getopts ":p:" opt
do case "$opt" in p) ssh_port=$OPTARG;; esac; done
ssh_opts=(
-oControlPath=none
-oControlMaster=no
-oServerAliveInterval=3
-oServerAliveCountMax=5
-oConnectTimeout=5
-oBatchMode=yes
-oPasswordAuthentication=no
-oNumberOfPasswordPrompts=0
-oExitOnForwardFailure=yes
-TnN
)
[[ -n "$oneshot" ]] || ssh_opts+=( -qy )
ssh_opts+=( "${ssh_opts_ext[@]}" )
tping_ct=$(( $interval * 2 ))
tping_ct=$(( $tping_ct > 5 ? $tping_ct : 5 ))
tping_opts=( -p"${ssh_port:-22}" -r"$interval" --conn-timeout="$tping_ct" )
[[ -z "$oneshot" ]] || {
[[ -z "$tping" ]] || tping "${tping_opts[@]}" "$dst"
exec ssh "${ssh_opts[@]}" "$tun" "$fwd" "$dst"
}
touch "$pid_file"
flock -n 3 3<"$pid_file" || exit 0
exec 3>"$pid_file"
( flock -n 3 || exit 0
trap "rm -f '$pid_file'" EXIT
interval_base=$interval
while :; do
[[ -z "$tping" ]] || {
ts0=$(printf '%(%s)T' -1)
tping "${tping_opts[@]}" "$dst"; }
touch "$pid_file"
ssh "${ssh_opts[@]}" "$tun" "$fwd" "$dst"
[[ -z "$tping" ]] || interval=$((
$ts0 + $interval_base - $(printf '%(%s)T' -1) ))
[[ "$interval" -le 0 ]] || sleep "$interval"
done ) &
echo $! >&3
exit 0