Skip to content

Commit 059cb62

Browse files
author
Dan
committed
Added proxy authentication parameters - resolves #18
1 parent eacea1e commit 059cb62

File tree

3 files changed

+89
-18
lines changed

3 files changed

+89
-18
lines changed

pssh/pssh_client.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ class ParallelSSHClient(object):
4949
def __init__(self, hosts,
5050
user=None, password=None, port=None, pkey=None,
5151
forward_ssh_agent=True, num_retries=DEFAULT_RETRIES, timeout=120,
52-
pool_size=10, proxy_host=None, proxy_port=22,
52+
pool_size=10, proxy_host=None, proxy_port=22, proxy_user=None,
53+
proxy_password=None, proxy_pkey=None,
5354
agent=None, allow_agent=True, host_config=None, channel_timeout=None):
5455
"""
5556
:param hosts: Hosts to connect to
@@ -298,7 +299,9 @@ def __init__(self, hosts,
298299
self.pkey = pkey
299300
self.num_retries = num_retries
300301
self.timeout = timeout
301-
self.proxy_host, self.proxy_port = proxy_host, proxy_port
302+
self.proxy_host, self.proxy_port, self.proxy_user, self.proxy_password, \
303+
self.proxy_pkey = proxy_host, proxy_port, proxy_user, \
304+
proxy_password, proxy_pkey
302305
# To hold host clients
303306
self.host_clients = {}
304307
self.agent = agent
@@ -555,7 +558,11 @@ def _exec_command(self, host, *args, **kwargs):
555558
timeout=self.timeout,
556559
proxy_host=self.proxy_host,
557560
proxy_port=self.proxy_port,
558-
allow_agent=self.allow_agent, agent=self.agent,
561+
proxy_user=self.proxy_user,
562+
proxy_password=self.proxy_password,
563+
proxy_pkey=self.proxy_pkey,
564+
allow_agent=self.allow_agent,
565+
agent=self.agent,
559566
channel_timeout=self.channel_timeout)
560567
return self.host_clients[host].exec_command(*args, **kwargs)
561568

pssh/ssh_client.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,8 @@ def __init__(self, host,
4444
pkey=None, forward_ssh_agent=True,
4545
num_retries=DEFAULT_RETRIES, agent=None,
4646
allow_agent=True, timeout=10, proxy_host=None,
47-
proxy_port=22, channel_timeout=None,
47+
proxy_port=22, proxy_user=None, proxy_password=None,
48+
proxy_pkey=None, channel_timeout=None,
4849
_openssh_config_file=None):
4950
"""Connect to host honouring any user set configuration in ~/.ssh/config \
5051
or /etc/ssh/ssh_config
@@ -115,7 +116,9 @@ def __init__(self, host,
115116
self.num_retries = num_retries
116117
self.timeout = timeout
117118
self.channel_timeout = channel_timeout
118-
self.proxy_host, self.proxy_port = proxy_host, proxy_port
119+
self.proxy_host, self.proxy_port, self.proxy_user, self.proxy_password, \
120+
self.proxy_pkey = proxy_host, proxy_port, proxy_user, \
121+
proxy_password, proxy_pkey
119122
self.proxy_client = None
120123
if self.proxy_host and self.proxy_port:
121124
logger.debug("Proxy configured for destination host %s - Proxy host: %s:%s",
@@ -134,13 +137,14 @@ def _connect_tunnel(self):
134137
"""
135138
self.proxy_client = paramiko.SSHClient()
136139
self.proxy_client.set_missing_host_key_policy(paramiko.MissingHostKeyPolicy())
137-
self._connect(self.proxy_client, self.proxy_host, self.proxy_port)
140+
self._connect(self.proxy_client, self.proxy_host, self.proxy_port,
141+
user=self.proxy_user, password=self.proxy_password,
142+
pkey=self.proxy_pkey)
138143
logger.info("Connecting via SSH proxy %s:%s -> %s:%s", self.proxy_host,
139144
self.proxy_port, self.host, self.port,)
140145
try:
141-
proxy_channel = self.proxy_client.get_transport().\
142-
open_channel('direct-tcpip', (self.host, self.port,),
143-
('127.0.0.1', 0))
146+
proxy_channel = self.proxy_client.get_transport().open_channel(
147+
'direct-tcpip', (self.host, self.port,), ('127.0.0.1', 0))
144148
sleep(0)
145149
return self._connect(self.client, self.host, self.port, sock=proxy_channel)
146150
except ChannelException, ex:
@@ -149,7 +153,8 @@ def _connect_tunnel(self):
149153
self.host, self.port,
150154
str(error_type))
151155

152-
def _connect(self, client, host, port, sock=None, retries=1):
156+
def _connect(self, client, host, port, sock=None, retries=1,
157+
user=None, password=None, pkey=None):
153158
"""Connect to host
154159
155160
:raises: :mod:`pssh.exceptions.AuthenticationException` on authentication error
@@ -158,20 +163,20 @@ def _connect(self, client, host, port, sock=None, retries=1):
158163
:raises: :mod:`pssh.exceptions.SSHException` on other undefined SSH errors
159164
"""
160165
try:
161-
client.connect(host, username=self.user,
162-
password=self.password, port=port,
163-
pkey=self.pkey,
166+
client.connect(host, username=user if user else self.user,
167+
password=password if password else self.password,
168+
port=port, pkey=pkey if pkey else self.pkey,
164169
sock=sock, timeout=self.timeout,
165170
allow_agent=self.allow_agent)
166171
except sock_gaierror, ex:
167172
logger.error("Could not resolve host '%s' - retry %s/%s",
168-
self.host, retries, self.num_retries)
173+
host, retries, self.num_retries)
169174
while retries < self.num_retries:
170175
sleep(5)
171176
return self._connect(client, host, port, sock=sock,
172177
retries=retries+1)
173178
raise UnknownHostException("Unknown host %s - %s - retry %s/%s",
174-
self.host, str(ex.args[1]), retries,
179+
host, str(ex.args[1]), retries,
175180
self.num_retries)
176181
except sock_error, ex:
177182
logger.error("Error connecting to host '%s:%s' - retry %s/%s",

tests/test_pssh_client.py

Lines changed: 62 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -184,7 +184,7 @@ def test_pssh_client_auth_failure(self):
184184
except AuthenticationException:
185185
pass
186186
del client
187-
server.join()
187+
server.kill()
188188

189189
def test_pssh_client_hosts_list_part_failure(self):
190190
"""Test getting output for remainder of host list in the case where one
@@ -230,7 +230,7 @@ def test_pssh_client_ssh_exception(self):
230230
)
231231
self.assertRaises(SSHException, client.run_command, self.fake_cmd)
232232
del client
233-
server.join()
233+
server.kill()
234234

235235
def test_pssh_client_timeout(self):
236236
listen_socket = make_socket(self.host)
@@ -258,7 +258,7 @@ def test_pssh_client_timeout(self):
258258
# msg="Channel timeout %s does not match requested timeout %s" %(
259259
# chan_timeout, client_timeout,))
260260
del client
261-
server.join()
261+
server.kill()
262262

263263
def test_pssh_client_exec_command_password(self):
264264
"""Test password authentication. Embedded server accepts any password
@@ -500,6 +500,65 @@ def test_ssh_proxy(self):
500500
self.server.kill()
501501
proxy_server.kill()
502502

503+
def test_ssh_proxy_auth(self):
504+
"""Test connecting to remote destination via SSH proxy
505+
client -> proxy -> destination
506+
Proxy SSH server accepts no commands and sends no responses, only
507+
proxies to destination. Destination accepts a command as usual."""
508+
proxy_server_socket = make_socket('127.0.0.2')
509+
proxy_server_port = proxy_server_socket.getsockname()[1]
510+
proxy_server = start_server(proxy_server_socket)
511+
proxy_user = 'proxy_user'
512+
proxy_password = 'fake'
513+
gevent.sleep(2)
514+
client = ParallelSSHClient([self.host], port=self.listen_port,
515+
pkey=self.user_key,
516+
proxy_host='127.0.0.2',
517+
proxy_port=proxy_server_port,
518+
proxy_user=proxy_user,
519+
proxy_password='fake',
520+
proxy_pkey=self.user_key,
521+
)
522+
gevent.sleep(2)
523+
output = client.run_command(self.fake_cmd)
524+
stdout = list(output[self.host]['stdout'])
525+
expected_stdout = [self.fake_resp]
526+
self.assertEqual(expected_stdout, stdout,
527+
msg="Got unexpected stdout - %s, expected %s" % (
528+
stdout, expected_stdout,))
529+
self.assertEqual(client.host_clients[self.host].proxy_user, proxy_user)
530+
self.assertEqual(client.host_clients[self.host].proxy_password, proxy_password)
531+
self.assertTrue(client.host_clients[self.host].proxy_pkey)
532+
self.server.kill()
533+
proxy_server.kill()
534+
535+
def test_ssh_proxy_auth_fail(self):
536+
"""Test failures while connecting via proxy"""
537+
listen_socket = make_socket(self.host)
538+
listen_port = listen_socket.getsockname()[1]
539+
self.server.kill()
540+
server = start_server(listen_socket,
541+
fail_auth=True)
542+
proxy_server_socket = make_socket('127.0.0.2')
543+
proxy_server_port = proxy_server_socket.getsockname()[1]
544+
proxy_server = start_server(proxy_server_socket)
545+
proxy_user = 'proxy_user'
546+
proxy_password = 'fake'
547+
gevent.sleep(2)
548+
client = ParallelSSHClient([self.host], port=listen_port,
549+
pkey=self.user_key,
550+
proxy_host='127.0.0.2',
551+
proxy_port=proxy_server_port,
552+
proxy_user=proxy_user,
553+
proxy_password='fake',
554+
proxy_pkey=self.user_key,
555+
)
556+
gevent.sleep(2)
557+
self.assertRaises(AuthenticationException, client.run_command, self.fake_cmd)
558+
del client
559+
server.kill()
560+
proxy_server.kill()
561+
503562
def test_bash_variable_substitution(self):
504563
"""Test bash variables work correctly"""
505564
client = ParallelSSHClient([self.host], port=self.listen_port,

0 commit comments

Comments
 (0)