Show More
@@ -1,7 +1,7 | |||||
1 | #!/usr/bin/env python |
|
1 | #!/usr/bin/env python | |
2 |
|
2 | |||
3 | # |
|
3 | # | |
4 | # This file is adapted from a paramiko demo, and thus LGPL 2.1. |
|
4 | # This file is adapted from a paramiko demo, and thus licensed under LGPL 2.1. | |
5 | # Original Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com> |
|
5 | # Original Copyright (C) 2003-2007 Robey Pointer <robeypointer@gmail.com> | |
6 | # Edits Copyright (C) 2010 The IPython Team |
|
6 | # Edits Copyright (C) 2010 The IPython Team | |
7 | # |
|
7 | # | |
@@ -83,7 +83,7 class Handler (SocketServer.BaseRequestHandler): | |||||
83 | self.request.send(data) |
|
83 | self.request.send(data) | |
84 | chan.close() |
|
84 | chan.close() | |
85 | self.request.close() |
|
85 | self.request.close() | |
86 |
verbose('Tunnel closed |
|
86 | verbose('Tunnel closed ') | |
87 |
|
87 | |||
88 |
|
88 | |||
89 | def forward_tunnel(local_port, remote_host, remote_port, transport): |
|
89 | def forward_tunnel(local_port, remote_host, remote_port, transport): | |
@@ -94,7 +94,7 def forward_tunnel(local_port, remote_host, remote_port, transport): | |||||
94 | chain_host = remote_host |
|
94 | chain_host = remote_host | |
95 | chain_port = remote_port |
|
95 | chain_port = remote_port | |
96 | ssh_transport = transport |
|
96 | ssh_transport = transport | |
97 | ForwardServer(('', local_port), SubHander).serve_forever() |
|
97 | ForwardServer(('127.0.0.1', local_port), SubHander).serve_forever() | |
98 |
|
98 | |||
99 |
|
99 | |||
100 | def verbose(s): |
|
100 | def verbose(s): |
@@ -19,6 +19,7 import zmq | |||||
19 | from zmq.eventloop import ioloop, zmqstream |
|
19 | from zmq.eventloop import ioloop, zmqstream | |
20 |
|
20 | |||
21 | from IPython.external.decorator import decorator |
|
21 | from IPython.external.decorator import decorator | |
|
22 | from IPython.zmq import tunnel | |||
22 |
|
23 | |||
23 | import streamsession as ss |
|
24 | import streamsession as ss | |
24 | # from remotenamespace import RemoteNamespace |
|
25 | # from remotenamespace import RemoteNamespace | |
@@ -117,7 +118,33 class Client(object): | |||||
117 |
|
118 | |||
118 | addr : bytes; zmq url, e.g. 'tcp://127.0.0.1:10101' |
|
119 | addr : bytes; zmq url, e.g. 'tcp://127.0.0.1:10101' | |
119 | The address of the controller's registration socket. |
|
120 | The address of the controller's registration socket. | |
120 |
|
121 | [Default: 'tcp://127.0.0.1:10101'] | ||
|
122 | context : zmq.Context | |||
|
123 | Pass an existing zmq.Context instance, otherwise the client will create its own | |||
|
124 | username : bytes | |||
|
125 | set username to be passed to the Session object | |||
|
126 | debug : bool | |||
|
127 | flag for lots of message printing for debug purposes | |||
|
128 | ||||
|
129 | #-------------- ssh related args ---------------- | |||
|
130 | # These are args for configuring the ssh tunnel to be used | |||
|
131 | # credentials are used to forward connections over ssh to the Controller | |||
|
132 | # Note that the ip given in `addr` needs to be relative to sshserver | |||
|
133 | # The most basic case is to leave addr as pointing to localhost (127.0.0.1), | |||
|
134 | # and set sshserver as the same machine the Controller is on. However, | |||
|
135 | # the only requirement is that sshserver is able to see the Controller | |||
|
136 | # (i.e. is within the same trusted network). | |||
|
137 | ||||
|
138 | sshserver : str | |||
|
139 | A string of the form passed to ssh, i.e. 'server.tld' or 'user@server.tld:port' | |||
|
140 | If keyfile or password is specified, and this is not, it will default to | |||
|
141 | the ip given in addr. | |||
|
142 | keyfile : str; path to public key file | |||
|
143 | This specifies a key to be used in ssh login, default None. | |||
|
144 | Regular default ssh keys will be used without specifying this argument. | |||
|
145 | password : str; | |||
|
146 | Your ssh password to sshserver. Note that if this is left None, | |||
|
147 | you will be prompted for it if passwordless key based login is unavailable. | |||
121 |
|
148 | |||
122 | Attributes |
|
149 | Attributes | |
123 | ---------- |
|
150 | ---------- | |
@@ -159,6 +186,7 class Client(object): | |||||
159 |
|
186 | |||
160 |
|
187 | |||
161 | _connected=False |
|
188 | _connected=False | |
|
189 | _ssh=False | |||
162 | _engines=None |
|
190 | _engines=None | |
163 | _addr='tcp://127.0.0.1:10101' |
|
191 | _addr='tcp://127.0.0.1:10101' | |
164 | _registration_socket=None |
|
192 | _registration_socket=None | |
@@ -173,18 +201,33 class Client(object): | |||||
173 | history = None |
|
201 | history = None | |
174 | debug = False |
|
202 | debug = False | |
175 |
|
203 | |||
176 |
def __init__(self, addr='tcp://127.0.0.1:10101', context=None, username=None, debug=False |
|
204 | def __init__(self, addr='tcp://127.0.0.1:10101', context=None, username=None, debug=False, | |
|
205 | sshserver=None, keyfile=None, password=None, paramiko=None): | |||
177 | if context is None: |
|
206 | if context is None: | |
178 | context = zmq.Context() |
|
207 | context = zmq.Context() | |
179 | self.context = context |
|
208 | self.context = context | |
180 | self._addr = addr |
|
209 | self._addr = addr | |
|
210 | self._ssh = bool(sshserver or keyfile or password) | |||
|
211 | if self._ssh and sshserver is None: | |||
|
212 | # default to the same | |||
|
213 | sshserver = addr.split('://')[1].split(':')[0] | |||
|
214 | if self._ssh and password is None: | |||
|
215 | if tunnel.try_passwordless_ssh(sshserver, keyfile, paramiko): | |||
|
216 | password=False | |||
|
217 | else: | |||
|
218 | password = getpass("SSH Password for %s: "%sshserver) | |||
|
219 | ssh_kwargs = dict(keyfile=keyfile, password=password, paramiko=paramiko) | |||
|
220 | ||||
181 | if username is None: |
|
221 | if username is None: | |
182 | self.session = ss.StreamSession() |
|
222 | self.session = ss.StreamSession() | |
183 | else: |
|
223 | else: | |
184 | self.session = ss.StreamSession(username) |
|
224 | self.session = ss.StreamSession(username) | |
185 |
self._registration_socket = self.context.socket(zmq. |
|
225 | self._registration_socket = self.context.socket(zmq.XREQ) | |
186 | self._registration_socket.setsockopt(zmq.IDENTITY, self.session.session) |
|
226 | self._registration_socket.setsockopt(zmq.IDENTITY, self.session.session) | |
187 | self._registration_socket.connect(addr) |
|
227 | if self._ssh: | |
|
228 | tunnel.tunnel_connection(self._registration_socket, addr, sshserver, **ssh_kwargs) | |||
|
229 | else: | |||
|
230 | self._registration_socket.connect(addr) | |||
188 | self._engines = {} |
|
231 | self._engines = {} | |
189 | self._ids = set() |
|
232 | self._ids = set() | |
190 | self.outstanding=set() |
|
233 | self.outstanding=set() | |
@@ -198,7 +241,7 class Client(object): | |||||
198 | } |
|
241 | } | |
199 | self._queue_handlers = {'execute_reply' : self._handle_execute_reply, |
|
242 | self._queue_handlers = {'execute_reply' : self._handle_execute_reply, | |
200 | 'apply_reply' : self._handle_apply_reply} |
|
243 | 'apply_reply' : self._handle_apply_reply} | |
201 | self._connect() |
|
244 | self._connect(sshserver, ssh_kwargs) | |
202 |
|
245 | |||
203 |
|
246 | |||
204 | @property |
|
247 | @property | |
@@ -229,12 +272,19 class Client(object): | |||||
229 | targets = [targets] |
|
272 | targets = [targets] | |
230 | return [self._engines[t] for t in targets], list(targets) |
|
273 | return [self._engines[t] for t in targets], list(targets) | |
231 |
|
274 | |||
232 | def _connect(self): |
|
275 | def _connect(self, sshserver, ssh_kwargs): | |
233 | """setup all our socket connections to the controller. This is called from |
|
276 | """setup all our socket connections to the controller. This is called from | |
234 | __init__.""" |
|
277 | __init__.""" | |
235 | if self._connected: |
|
278 | if self._connected: | |
236 | return |
|
279 | return | |
237 | self._connected=True |
|
280 | self._connected=True | |
|
281 | ||||
|
282 | def connect_socket(s, addr): | |||
|
283 | if self._ssh: | |||
|
284 | return tunnel.tunnel_connection(s, addr, sshserver, **ssh_kwargs) | |||
|
285 | else: | |||
|
286 | return s.connect(addr) | |||
|
287 | ||||
238 | self.session.send(self._registration_socket, 'connection_request') |
|
288 | self.session.send(self._registration_socket, 'connection_request') | |
239 | idents,msg = self.session.recv(self._registration_socket,mode=0) |
|
289 | idents,msg = self.session.recv(self._registration_socket,mode=0) | |
240 | if self.debug: |
|
290 | if self.debug: | |
@@ -245,23 +295,23 class Client(object): | |||||
245 | if content.queue: |
|
295 | if content.queue: | |
246 | self._mux_socket = self.context.socket(zmq.PAIR) |
|
296 | self._mux_socket = self.context.socket(zmq.PAIR) | |
247 | self._mux_socket.setsockopt(zmq.IDENTITY, self.session.session) |
|
297 | self._mux_socket.setsockopt(zmq.IDENTITY, self.session.session) | |
248 |
self._mux_socket |
|
298 | connect_socket(self._mux_socket, content.queue) | |
249 | if content.task: |
|
299 | if content.task: | |
250 | self._task_socket = self.context.socket(zmq.PAIR) |
|
300 | self._task_socket = self.context.socket(zmq.PAIR) | |
251 | self._task_socket.setsockopt(zmq.IDENTITY, self.session.session) |
|
301 | self._task_socket.setsockopt(zmq.IDENTITY, self.session.session) | |
252 |
self._task_socket |
|
302 | connect_socket(self._task_socket, content.task) | |
253 | if content.notification: |
|
303 | if content.notification: | |
254 | self._notification_socket = self.context.socket(zmq.SUB) |
|
304 | self._notification_socket = self.context.socket(zmq.SUB) | |
255 |
self._notification_socket |
|
305 | connect_socket(self._notification_socket, content.notification) | |
256 | self._notification_socket.setsockopt(zmq.SUBSCRIBE, "") |
|
306 | self._notification_socket.setsockopt(zmq.SUBSCRIBE, "") | |
257 | if content.query: |
|
307 | if content.query: | |
258 | self._query_socket = self.context.socket(zmq.PAIR) |
|
308 | self._query_socket = self.context.socket(zmq.PAIR) | |
259 | self._query_socket.setsockopt(zmq.IDENTITY, self.session.session) |
|
309 | self._query_socket.setsockopt(zmq.IDENTITY, self.session.session) | |
260 |
self._query_socket |
|
310 | connect_socket(self._query_socket, content.query) | |
261 | if content.control: |
|
311 | if content.control: | |
262 | self._control_socket = self.context.socket(zmq.PAIR) |
|
312 | self._control_socket = self.context.socket(zmq.PAIR) | |
263 | self._control_socket.setsockopt(zmq.IDENTITY, self.session.session) |
|
313 | self._control_socket.setsockopt(zmq.IDENTITY, self.session.session) | |
264 |
self._control_socket |
|
314 | connect_socket(self._control_socket, content.control) | |
265 | self._update_engines(dict(content.engines)) |
|
315 | self._update_engines(dict(content.engines)) | |
266 |
|
316 | |||
267 | else: |
|
317 | else: | |
@@ -852,4 +902,4 class AsynClient(Client): | |||||
852 | for stream in (self.queue_stream, self.notifier_stream, |
|
902 | for stream in (self.queue_stream, self.notifier_stream, | |
853 | self.task_stream, self.control_stream): |
|
903 | self.task_stream, self.control_stream): | |
854 | stream.flush() |
|
904 | stream.flush() | |
855 | No newline at end of file |
|
905 |
@@ -1,12 +1,21 | |||||
|
1 | """Basic ssh tunneling utilities.""" | |||
1 |
|
2 | |||
|
3 | #----------------------------------------------------------------------------- | |||
|
4 | # Copyright (C) 2008-2010 The IPython Development Team | |||
|
5 | # | |||
|
6 | # Distributed under the terms of the BSD License. The full license is in | |||
|
7 | # the file COPYING, distributed as part of this software. | |||
|
8 | #----------------------------------------------------------------------------- | |||
2 |
|
9 | |||
3 | #----------------------------------------- |
|
10 | ||
|
11 | ||||
|
12 | #----------------------------------------------------------------------------- | |||
4 | # Imports |
|
13 | # Imports | |
5 | #----------------------------------------- |
|
14 | #----------------------------------------------------------------------------- | |
6 |
|
15 | |||
7 | from __future__ import print_function |
|
16 | from __future__ import print_function | |
8 |
|
17 | |||
9 | import os,sys |
|
18 | import os,sys, atexit | |
10 | from multiprocessing import Process |
|
19 | from multiprocessing import Process | |
11 | from getpass import getpass, getuser |
|
20 | from getpass import getpass, getuser | |
12 |
|
21 | |||
@@ -16,20 +25,137 except ImportError: | |||||
16 | paramiko = None |
|
25 | paramiko = None | |
17 | else: |
|
26 | else: | |
18 | from forward import forward_tunnel |
|
27 | from forward import forward_tunnel | |
|
28 | ||||
|
29 | try: | |||
|
30 | from IPython.external import pexpect | |||
|
31 | except ImportError: | |||
|
32 | pexpect = None | |||
|
33 | ||||
|
34 | from IPython.zmq.parallel.entry_point import select_random_ports | |||
|
35 | ||||
|
36 | #----------------------------------------------------------------------------- | |||
|
37 | # Code | |||
|
38 | #----------------------------------------------------------------------------- | |||
|
39 | ||||
|
40 | #----------------------------------------------------------------------------- | |||
|
41 | # Check for passwordless login | |||
|
42 | #----------------------------------------------------------------------------- | |||
|
43 | ||||
|
44 | def try_passwordless_ssh(server, keyfile, paramiko=None): | |||
|
45 | """Attempt to make an ssh connection without a password. | |||
|
46 | This is mainly used for requiring password input only once | |||
|
47 | when many tunnels may be connected to the same server. | |||
19 |
|
|
48 | ||
20 | from IPython.external import pexpect |
|
49 | If paramiko is None, the default for the platform is chosen. | |
|
50 | """ | |||
|
51 | if paramiko is None: | |||
|
52 | paramiko = sys.platform == 'win32' | |||
|
53 | if not paramiko: | |||
|
54 | f = _try_passwordless_openssh | |||
|
55 | else: | |||
|
56 | f = _try_passwordless_paramiko | |||
|
57 | return f(server, keyfile) | |||
21 |
|
58 | |||
|
59 | def _try_passwordless_openssh(server, keyfile): | |||
|
60 | """Try passwordless login with shell ssh command.""" | |||
|
61 | if pexpect is None: | |||
|
62 | raise ImportError("pexpect unavailable, use paramiko") | |||
|
63 | cmd = 'ssh -f '+ server | |||
|
64 | if keyfile: | |||
|
65 | cmd += ' -i ' + keyfile | |||
|
66 | cmd += ' exit' | |||
|
67 | p = pexpect.spawn(cmd) | |||
|
68 | while True: | |||
|
69 | try: | |||
|
70 | p.expect('[Ppassword]:', timeout=.1) | |||
|
71 | except pexpect.TIMEOUT: | |||
|
72 | continue | |||
|
73 | except pexpect.EOF: | |||
|
74 | return True | |||
|
75 | else: | |||
|
76 | return False | |||
22 |
|
77 | |||
23 | def launch_ssh_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None, timeout=15): |
|
78 | def _try_passwordless_paramiko(server, keyfile): | |
|
79 | """Try passwordless login with paramiko.""" | |||
|
80 | if paramiko is None: | |||
|
81 | raise ImportError("paramiko unavailable, use openssh") | |||
|
82 | username, server, port = _split_server(server) | |||
|
83 | client = paramiko.SSHClient() | |||
|
84 | client.load_system_host_keys() | |||
|
85 | client.set_missing_host_key_policy(paramiko.WarningPolicy()) | |||
|
86 | try: | |||
|
87 | client.connect(server, port, username=username, key_filename=keyfile, | |||
|
88 | look_for_keys=True) | |||
|
89 | except paramiko.AuthenticationException: | |||
|
90 | return False | |||
|
91 | else: | |||
|
92 | client.close() | |||
|
93 | return True | |||
|
94 | ||||
|
95 | ||||
|
96 | def tunnel_connection(socket, addr, server, keyfile=None, password=None, paramiko=None): | |||
|
97 | """Connect a socket to an address via an ssh tunnel. | |||
|
98 | ||||
|
99 | This is a wrapper for socket.connect(addr), when addr is not accessible | |||
|
100 | from the local machine. It simply creates an ssh tunnel using the remaining args, | |||
|
101 | and calls socket.connect('tcp://localhost:lport') where lport is the randomly | |||
|
102 | selected local port of the tunnel. | |||
|
103 | ||||
|
104 | """ | |||
|
105 | lport = select_random_ports(1)[0] | |||
|
106 | transport, addr = addr.split('://') | |||
|
107 | ip,rport = addr.split(':') | |||
|
108 | rport = int(rport) | |||
|
109 | if paramiko is None: | |||
|
110 | paramiko = sys.platform == 'win32' | |||
|
111 | if paramiko: | |||
|
112 | tunnelf = paramiko_tunnel | |||
|
113 | else: | |||
|
114 | tunnelf = openssh_tunnel | |||
|
115 | tunnel = tunnelf(lport, rport, server, remoteip=ip, keyfile=keyfile, password=password) | |||
|
116 | socket.connect('tcp://127.0.0.1:%i'%lport) | |||
|
117 | return tunnel | |||
|
118 | ||||
|
119 | def openssh_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None, password=None, timeout=15): | |||
24 | """Create an ssh tunnel using command-line ssh that connects port lport |
|
120 | """Create an ssh tunnel using command-line ssh that connects port lport | |
25 | on this machine to localhost:rport on server. The tunnel |
|
121 | on this machine to localhost:rport on server. The tunnel | |
26 | will automatically close when not in use, remaining open |
|
122 | will automatically close when not in use, remaining open | |
27 | for a minimum of timeout seconds for an initial connection. |
|
123 | for a minimum of timeout seconds for an initial connection. | |
|
124 | ||||
|
125 | This creates a tunnel redirecting `localhost:lport` to `remoteip:rport`, | |||
|
126 | as seen from `server`. | |||
|
127 | ||||
|
128 | keyfile and password may be specified, but ssh config is checked for defaults. | |||
|
129 | ||||
|
130 | Parameters | |||
|
131 | ---------- | |||
|
132 | ||||
|
133 | lport : int | |||
|
134 | local port for connecting to the tunnel from this machine. | |||
|
135 | rport : int | |||
|
136 | port on the remote machine to connect to. | |||
|
137 | server : str | |||
|
138 | The ssh server to connect to. The full ssh server string will be parsed. | |||
|
139 | user@server:port | |||
|
140 | remoteip : str [Default: 127.0.0.1] | |||
|
141 | The remote ip, specifying the destination of the tunnel. | |||
|
142 | Default is localhost, which means that the tunnel would redirect | |||
|
143 | localhost:lport on this machine to localhost:rport on the *server*. | |||
|
144 | ||||
|
145 | keyfile : str; path to public key file | |||
|
146 | This specifies a key to be used in ssh login, default None. | |||
|
147 | Regular default ssh keys will be used without specifying this argument. | |||
|
148 | password : str; | |||
|
149 | Your ssh password to the ssh server. Note that if this is left None, | |||
|
150 | you will be prompted for it if passwordless key based login is unavailable. | |||
|
151 | ||||
28 | """ |
|
152 | """ | |
|
153 | if pexpect is None: | |||
|
154 | raise ImportError("pexpect unavailable, use paramiko_tunnel") | |||
29 | ssh="ssh " |
|
155 | ssh="ssh " | |
30 | if keyfile: |
|
156 | if keyfile: | |
31 | ssh += "-i " + keyfile |
|
157 | ssh += "-i " + keyfile | |
32 | cmd = ssh + " -f -L %i:127.0.0.1:%i %s sleep %i"%(lport, rport, server, timeout) |
|
158 | cmd = ssh + " -f -L 127.0.0.1:%i:127.0.0.1:%i %s sleep %i"%(lport, rport, server, timeout) | |
33 | tunnel = pexpect.spawn(cmd) |
|
159 | tunnel = pexpect.spawn(cmd) | |
34 | failed = False |
|
160 | failed = False | |
35 | while True: |
|
161 | while True: | |
@@ -48,7 +174,10 def launch_ssh_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None, | |||||
48 | else: |
|
174 | else: | |
49 | if failed: |
|
175 | if failed: | |
50 | print("Password rejected, try again") |
|
176 | print("Password rejected, try again") | |
51 | tunnel.sendline(getpass()) |
|
177 | password=None | |
|
178 | if password is None: | |||
|
179 | password = getpass("%s's password: "%(server)) | |||
|
180 | tunnel.sendline(password) | |||
52 | failed = True |
|
181 | failed = True | |
53 |
|
182 | |||
54 | def _split_server(server): |
|
183 | def _split_server(server): | |
@@ -63,28 +192,62 def _split_server(server): | |||||
63 | port = 22 |
|
192 | port = 22 | |
64 | return username, server, port |
|
193 | return username, server, port | |
65 |
|
194 | |||
66 |
def |
|
195 | def paramiko_tunnel(lport, rport, server, remoteip='127.0.0.1', keyfile=None, password=None, timeout=15): | |
67 |
"""launch a tunner with paramiko in a subprocess |
|
196 | """launch a tunner with paramiko in a subprocess. This should only be used | |
|
197 | when shell ssh is unavailable (e.g. Windows). | |||
|
198 | ||||
|
199 | This creates a tunnel redirecting `localhost:lport` to `remoteip:rport`, | |||
|
200 | as seen from `server`. | |||
|
201 | ||||
|
202 | keyfile and password may be specified, but ssh config is checked for defaults. | |||
|
203 | ||||
|
204 | Parameters | |||
|
205 | ---------- | |||
|
206 | ||||
|
207 | lport : int | |||
|
208 | local port for connecting to the tunnel from this machine. | |||
|
209 | rport : int | |||
|
210 | port on the remote machine to connect to. | |||
|
211 | server : str | |||
|
212 | The ssh server to connect to. The full ssh server string will be parsed. | |||
|
213 | user@server:port | |||
|
214 | remoteip : str [Default: 127.0.0.1] | |||
|
215 | The remote ip, specifying the destination of the tunnel. | |||
|
216 | Default is localhost, which means that the tunnel would redirect | |||
|
217 | localhost:lport on this machine to localhost:rport on the *server*. | |||
|
218 | ||||
|
219 | keyfile : str; path to public key file | |||
|
220 | This specifies a key to be used in ssh login, default None. | |||
|
221 | Regular default ssh keys will be used without specifying this argument. | |||
|
222 | password : str; | |||
|
223 | Your ssh password to the ssh server. Note that if this is left None, | |||
|
224 | you will be prompted for it if passwordless key based login is unavailable. | |||
|
225 | ||||
|
226 | """ | |||
68 | if paramiko is None: |
|
227 | if paramiko is None: | |
69 | raise ImportError("Paramiko not available") |
|
228 | raise ImportError("Paramiko not available") | |
70 | server = _split_server(server) |
|
229 | ||
71 |
if |
|
230 | if password is None: | |
72 | passwd = getpass("%s@%s's password: "%(server[0], server[1])) |
|
231 | if not _check_passwordless_paramiko(server, keyfile): | |
73 | else: |
|
232 | password = getpass("%s's password: "%(server)) | |
74 | passwd = None |
|
233 | ||
75 | p = Process(target=_paramiko_tunnel, |
|
234 | p = Process(target=_paramiko_tunnel, | |
76 | args=(lport, rport, server, remoteip), |
|
235 | args=(lport, rport, server, remoteip), | |
77 | kwargs=dict(keyfile=keyfile, password=passwd)) |
|
236 | kwargs=dict(keyfile=keyfile, password=password)) | |
78 | p.daemon=False |
|
237 | p.daemon=False | |
79 | p.start() |
|
238 | p.start() | |
|
239 | atexit.register(_shutdown_process, p) | |||
80 | return p |
|
240 | return p | |
81 |
|
241 | |||
|
242 | def _shutdown_process(p): | |||
|
243 | if p.isalive(): | |||
|
244 | p.terminate() | |||
82 |
|
245 | |||
83 | def _paramiko_tunnel(lport, rport, server, remoteip, keyfile=None, password=None): |
|
246 | def _paramiko_tunnel(lport, rport, server, remoteip, keyfile=None, password=None): | |
84 | """function for actually starting a paramiko tunnel, to be passed |
|
247 | """function for actually starting a paramiko tunnel, to be passed | |
85 | to multiprocessing.Process(target=this). |
|
248 | to multiprocessing.Process(target=this). | |
86 | """ |
|
249 | """ | |
87 | username, server, port = server |
|
250 | username, server, port = _split_server(server) | |
88 | client = paramiko.SSHClient() |
|
251 | client = paramiko.SSHClient() | |
89 | client.load_system_host_keys() |
|
252 | client.load_system_host_keys() | |
90 | client.set_missing_host_key_policy(paramiko.WarningPolicy()) |
|
253 | client.set_missing_host_key_policy(paramiko.WarningPolicy()) | |
@@ -92,20 +255,34 def _paramiko_tunnel(lport, rport, server, remoteip, keyfile=None, password=None | |||||
92 | try: |
|
255 | try: | |
93 | client.connect(server, port, username=username, key_filename=keyfile, |
|
256 | client.connect(server, port, username=username, key_filename=keyfile, | |
94 | look_for_keys=True, password=password) |
|
257 | look_for_keys=True, password=password) | |
|
258 | # except paramiko.AuthenticationException: | |||
|
259 | # if password is None: | |||
|
260 | # password = getpass("%s@%s's password: "%(username, server)) | |||
|
261 | # client.connect(server, port, username=username, password=password) | |||
|
262 | # else: | |||
|
263 | # raise | |||
95 | except Exception as e: |
|
264 | except Exception as e: | |
96 | print ('*** Failed to connect to %s:%d: %r' % (server, port, e)) |
|
265 | print ('*** Failed to connect to %s:%d: %r' % (server, port, e)) | |
97 | sys.exit(1) |
|
266 | sys.exit(1) | |
98 |
|
267 | |||
99 | print ('Now forwarding port %d to %s:%d ...' % (lport, server, rport)) |
|
268 | # print ('Now forwarding port %d to %s:%d ...' % (lport, server, rport)) | |
100 |
|
269 | |||
101 | try: |
|
270 | try: | |
102 | forward_tunnel(lport, remoteip, rport, client.get_transport()) |
|
271 | forward_tunnel(lport, remoteip, rport, client.get_transport()) | |
103 | except KeyboardInterrupt: |
|
272 | except KeyboardInterrupt: | |
104 |
print (' |
|
273 | print ('SIGINT: Port forwarding stopped cleanly') | |
105 | sys.exit(0) |
|
274 | sys.exit(0) | |
|
275 | except Exception as e: | |||
|
276 | print ("Port forwarding stopped uncleanly: %s"%e) | |||
|
277 | sys.exit(255) | |||
|
278 | ||||
|
279 | if sys.platform == 'win32': | |||
|
280 | ssh_tunnel = paramiko_tunnel | |||
|
281 | else: | |||
|
282 | ssh_tunnel = openssh_tunnel | |||
106 |
|
283 | |||
107 |
|
284 | |||
108 | __all__ = ['launch_ssh_tunnel', 'launch_paramiko_tunnel'] |
|
285 | __all__ = ['tunnel_connection', 'ssh_tunnel', 'openssh_tunnel', 'paramiko_tunnel', 'try_passwordless_ssh'] | |
109 |
|
286 | |||
110 |
|
287 | |||
111 |
|
288 |
General Comments 0
You need to be logged in to leave comments.
Login now