-
Notifications
You must be signed in to change notification settings - Fork 0
/
jumpssh
executable file
·307 lines (270 loc) · 9.02 KB
/
jumpssh
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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
#!/usr/bin/env bash
####################################################################################################
# Help message
if [[ $# -eq 0 ]] || [[ $1 == "-h" ]] || [[ $1 == "--help" ]]; then
echo "jumpssh [--echo] [--exec command] [-h] [--help] [-L port:[intport:]hostport]"
echo "[-R port:[intport:]hostport] [-???] [user@]hostname[:port]"
echo "[user@]hostname[:port] [user@]hostname[:port] ..."
echo "
[user@]hostname1"
echo " [[user@]hostname2] ... [[user@]hostnameN"
if [[ $# -gt 0 ]]; then
echo "Usage:"
echo "jumpssh"
echo " --echo Print the generated ssh command instead of"
echo " executing it"
echo " --exec command Execute command on remote server instead of a"
echo " login shell"
echo " -h --help This help screen"
echo " -L port:[intport:]hostport Traffic on client port $port is forwarded to port"
echo " $hostport on the remote. If connecting through"
echo " intermediate hosts, port $intport is used as the"
echo " intermediate port to forward the connection"
echo " through. If $intport is unspecified, a random port"
echo " will be used. The same ports is used on"
echo " consecutive invocations of the same jumpssh"
echo " command, as command line arguments are used to"
echo " seed the random generator"
echo " -R port:intport:hostport Traffic on remote port $port is forwarded to port"
echo " $hostport on the client. Otherwise works as -L"
echo " -??? All other flags (beginning with a dash) are"
echo " applied to every ssh 'jump' command generated."
echo " [user@]hostname[:port] Host(s) to connect to/through. Will do a \"jump\""
echo " to the next server specified, until connecting to"
echo " the last host in the list"
echo ""
echo "Examples:"
echo ""
echo "Create an ssh connection to a host through other hosts, and set up ssh port"
echo "forwards from source to target and back"
echo ""
echo "Examples:"
echo ""
echo "jumpssh -L 2222:22 user@hostname"
echo " connects to hostname. Remote (hostname) port 22 will be accessible from"
echo " client machine through port 2222."
echo ""
echo "jumpssh -L 2222:22 -R 2223:22 user@host1 host2 host3 host4"
echo " connects to host4 through host1, 2 and 3. Client port 22 will be accessible"
echo " from host4 through port 2223, and host4 port 22 will be accessible from the"
echo " client through port 22. The ports are forwarded through random ports on"
echo " host 2 and 3"
echo ""
echo "jumpssh -L 8080:8181:80 user@host1 host2"
echo " connects to host2 through host1. host2 port 80 will be available on"
echo " client port 8080. host1 port 8181 is used as the intermediate port for this"
echo " port forward."
echo ""
echo "Note that the \"randomly\" assigned ports will always be the same for the same"
echo "command. 'jumpssh -L 2222:22 user@hostname' will use the same random ports"
echo "upon every invocation, as the random generator is seeded using the jumpssh"
echo "arguments"
fi
exit 0
fi
####################################################################################################
# initialise the semi-random generator.
# get random seed from input arguments to always get same "random" ports for jump
randports=()
randcount=0
randport=0
function initrand()
{
n=$(md5sum <<< $@)
RANDOM=$((0x${n%% *}))
temprand=()
# get a bunch of random unique port numbers
for (( i=1;i<=50;i++ )); do
temprand+=("$(( ( RANDOM % 9998 ) + 30000 )) ")
done
# remove duplicates and save the list of ports in global scope
for r in $(for r in ${temprand[@]}; do echo $r; done | awk ' !x[$0]++'); do
randports+=("$r")
done
}
initrand "$@"
function nextrandport()
{
randport=${randports[$randcount]}
randcount=$(($randcount+1))
}
####################################################################################################
# first server we're jumping to?
first=true
# second server we're jumping to?
second=true
# print or execute final command?
onlyecho=false
# contains all port forwards
forwards=()
# contains all hosts we're jumping to
hosts=()
# final command we're building
cmd=""
# extra ssh arguments we need to add to each ssh command
sshargs=""
# the command to run instead of a login shell
remotecmd=""
# repeat until all arguments have been popped
while test $# -gt 0
do
# is this a port spec argument?
if [[ "$1" == "-L" ]] || [[ "$1" == "-R" ]] ; then
#get forward request
if [ "$1" == "-L" ] ; then
forwardtype="L"
elif [ "$1" == "-R" ] ; then
forwardtype="R"
fi
# next argument must be the port number(s)
shift
ports=($(echo $1 | tr ":" " "))
portslen=${#ports[@]}
# port array contains one of these combinations:
# [ sourceport, intermediateport, destinationport]
# [ sourceport, destinationport]
# [ sourceport]
# start by setting some default ports, for the ones
# the user hasn't explicitely stated
nextrandport
left=$randport
nextrandport
mid=$randport
nextrandport
right=$randport
# overwrite the ports with the ones the user has specified
if [[ $portslen > 0 ]] ; then
# left hand port is set
right=${ports[0]}
fi
if [[ $portslen == 1 ]] ; then
# user is not allowed to only specified target port
echo "Invalid port forward specifed. Must be \$port:\$hostport or \$port:\$intermediateport:\$hostport"
exit 100
elif [[ $portslen == 2 ]] ; then
# right hand port is set
left=${ports[0]}
right=${ports[1]}
elif [[ $portslen == 3 ]] ; then
# mid and right hand port is set.
left=${ports[0]}
mid=${ports[1]}
right=${ports[2]}
fi
forwards+=("$forwardtype:$left:$mid:$right")
#pop the ports argument and continue parsing arguments
shift
continue
elif [[ "$1" == "--echo" ]] ; then
onlyecho=true
shift
continue
elif [[ "$1" == "--exec" ]] ; then
if [[ "$remotecmd" != "" ]]; then
echo "Only one --exec command must be specified"
exit 100
fi
shift
remotecmd=" $1"
shift
continue
elif [ ${1:0:1} = "-" ]; then
# this is a different argument, that we want passed to EVERY ssh connection
sshargs+=" $1"
shift
continue
else
# otherwise, it should be a hostname, parse it
hostport=($(echo $1 | tr ":" " "))
host=${hostport[0]}
if [ ${#hostport[@]} = 2 ]; then
portarg="${hostport[1]}"
else
# no explicit host port specified, so use an empty string for the -p argument
portarg="22"
fi
hosts+=("$host:$portarg")
shift
continue
fi
done
# we've parsed all arguments, now construct the ssh command
hostslen=${#hosts[*]}
for hostport in ${hosts[@]}; do
hostport=($(echo $hostport | tr ":" " "))
host=${hostport[0]}
port=${hostport[1]}
forwardfailure=""
forwardarg=""
for forward in ${forwards[@]}; do
forward=($(echo $forward | tr ":" " "))
porttype=${forward[0]}
left=${forward[1]}
mid=${forward[2]}
right=${forward[3]}
# by default, we'll forward a port to the intermediate port
# but if we're connecting to the last server, then we need to set dest
# to the actual destination port we want
if [ "$hostslen" = "1" ]; then
ldest=$right
rdest=$left
else
ldest=$mid
rdest=$mid
fi
# first and second ssh command has slightly different syntax, 3th to nth have similar syntax
if [ "$first" = true ]; then
if [[ "$porttype" = "L" ]]; then
arg="-L $left:localhost:$ldest"
elif [[ "$porttype" = "R" ]]; then
arg="-R $rdest:localhost:$right"
fi
elif [ "$second" = true ]; then
if [[ "$porttype" = "L" ]]; then
arg="-L $mid:localhost:$ldest"
elif [[ "$forwardtype" = "R" ]]; then
arg="-R $rdest:localhost:$mid"
fi
else
if [[ "$porttype" = "L" ]]; then
arg="-L $mid:localhost:$ldest"
elif [[ "$porttype" = "R" ]]; then
arg="-R $rdest:localhost:$mid"
fi
fi
forwardarg+="$arg "
done
# if user isn't running a command, then create a pseudo terminal
#if [[ "$remotecmd" == "" ]] && [[ "$localcmd" == "" ]]; then
if [[ "$remotecmd" == "" ]] ; then
termarg="-t"
else
termarg=""
# let the connection die if we're expected to run a command
# this, however, should only be set on the first connect
if [ "$first" = true ]; then
forwardfailure="-o ExitOnForwardFailure=yes"
fi
fi
# build command
thiscmd="ssh $forwardfailure $termarg $sshargs -p $port $forwardarg $host"
# add remote command if this is the last jump
if [ "$hostslen" = "1" ] && [[ $remotecmd != "" ]]; then
thiscmd+=" $remotecmd"
fi
if [ "$first" = true ]; then
first=false
cmd+="$thiscmd"
elif [ "$second" = true ]; then
second=false
cmd+=" $thiscmd"
else
cmd+=" \"$thiscmd\""
fi
hostslen=$((hostslen - 1))
done
if [ "$onlyecho" = true ]; then
echo $cmd
else
$cmd
fi