Load key files manually before making SSH connections
Change-Id: I63828b940d9bedcfff854f6732a78c09828666af
This commit is contained in:
parent
592eccd0ef
commit
f526f4eae7
|
@ -18,8 +18,8 @@ from __future__ import absolute_import
|
||||||
import collections
|
import collections
|
||||||
import contextlib
|
import contextlib
|
||||||
import getpass
|
import getpass
|
||||||
|
import io
|
||||||
import os
|
import os
|
||||||
import socket
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
import threading
|
import threading
|
||||||
|
@ -579,6 +579,21 @@ def ssh_client(host, port=None, username=None, proxy_jump=None,
|
||||||
**connect_parameters)
|
**connect_parameters)
|
||||||
|
|
||||||
|
|
||||||
|
def load_private_keys(key_filenames: typing.List[str]):
|
||||||
|
pkeys = []
|
||||||
|
for filename in key_filenames:
|
||||||
|
if os.path.exists(filename):
|
||||||
|
try:
|
||||||
|
with io.open(filename, 'rt') as fd:
|
||||||
|
pkey = paramiko.RSAKey.from_private_key(fd)
|
||||||
|
except Exception:
|
||||||
|
LOG.error('Unable to get RSAKey private key from file: '
|
||||||
|
f'{filename}', exc_info=1)
|
||||||
|
else:
|
||||||
|
pkeys.append(pkey)
|
||||||
|
return pkeys
|
||||||
|
|
||||||
|
|
||||||
def ssh_connect(hostname, username=None, port=None, connection_interval=None,
|
def ssh_connect(hostname, username=None, port=None, connection_interval=None,
|
||||||
connection_attempts=None, connection_timeout=None,
|
connection_attempts=None, connection_timeout=None,
|
||||||
proxy_command=None, proxy_client=None, key_filename=None,
|
proxy_command=None, proxy_client=None, key_filename=None,
|
||||||
|
@ -587,13 +602,9 @@ def ssh_connect(hostname, username=None, port=None, connection_interval=None,
|
||||||
client.set_missing_host_key_policy(paramiko.WarningPolicy())
|
client.set_missing_host_key_policy(paramiko.WarningPolicy())
|
||||||
login = _command.ssh_login(hostname=hostname, username=username, port=port)
|
login = _command.ssh_login(hostname=hostname, username=username, port=port)
|
||||||
|
|
||||||
if key_filename:
|
assert isinstance(key_filename, list)
|
||||||
# Ensures we try enough times to try all keys
|
pkeys = load_private_keys(key_filename)
|
||||||
tobiko.check_valid_type(key_filename, list)
|
auth_failed: typing.Optional[Exception] = None
|
||||||
connection_attempts = max(connection_attempts or 1,
|
|
||||||
len(key_filename),
|
|
||||||
1)
|
|
||||||
|
|
||||||
for attempt in tobiko.retry(count=connection_attempts,
|
for attempt in tobiko.retry(count=connection_attempts,
|
||||||
timeout=connection_timeout,
|
timeout=connection_timeout,
|
||||||
interval=connection_interval,
|
interval=connection_interval,
|
||||||
|
@ -603,8 +614,8 @@ def ssh_connect(hostname, username=None, port=None, connection_interval=None,
|
||||||
LOG.debug(f"Logging in to '{login}'...\n"
|
LOG.debug(f"Logging in to '{login}'...\n"
|
||||||
f" - parameters: {parameters}\n"
|
f" - parameters: {parameters}\n"
|
||||||
f" - attempt: {attempt.details}\n")
|
f" - attempt: {attempt.details}\n")
|
||||||
|
for pkey in pkeys + [None]:
|
||||||
try:
|
succeeded = False
|
||||||
proxy_sock = ssh_proxy_sock(
|
proxy_sock = ssh_proxy_sock(
|
||||||
hostname=hostname,
|
hostname=hostname,
|
||||||
port=port,
|
port=port,
|
||||||
|
@ -613,29 +624,35 @@ def ssh_connect(hostname, username=None, port=None, connection_interval=None,
|
||||||
timeout=connection_timeout,
|
timeout=connection_timeout,
|
||||||
connection_attempts=1,
|
connection_attempts=1,
|
||||||
connection_interval=connection_interval)
|
connection_interval=connection_interval)
|
||||||
client.connect(hostname=hostname,
|
try:
|
||||||
username=username,
|
client.connect(hostname=hostname,
|
||||||
port=port,
|
username=username,
|
||||||
sock=proxy_sock,
|
port=port,
|
||||||
key_filename=key_filename,
|
sock=proxy_sock,
|
||||||
**parameters)
|
pkey=pkey,
|
||||||
except ValueError as ex:
|
**parameters)
|
||||||
attempt.check_limits()
|
except paramiko.ssh_exception.AuthenticationException as ex:
|
||||||
if (str(ex) == 'q must be exactly 160, 224, or 256 bits long' and
|
if auth_failed is not None:
|
||||||
len(key_filename) > 1):
|
ex.__cause__ = auth_failed
|
||||||
# Must try without the first key
|
auth_failed = ex
|
||||||
LOG.debug(f"Retry connecting with the next key: {ex}")
|
|
||||||
key_filename = key_filename[1:]
|
|
||||||
continue
|
continue
|
||||||
|
except Exception as ex:
|
||||||
|
LOG.debug(f"Error logging in to '{login}': {ex}", exc_info=1)
|
||||||
|
attempt.check_limits()
|
||||||
|
break
|
||||||
else:
|
else:
|
||||||
raise
|
LOG.debug(f"Successfully logged in to '{login}'")
|
||||||
except (EOFError, socket.error, socket.timeout,
|
succeeded = True
|
||||||
paramiko.SSHException) as ex:
|
return client, proxy_sock
|
||||||
attempt.check_limits()
|
finally:
|
||||||
LOG.debug(f"Error logging in to '{login}': {ex}")
|
if not succeeded:
|
||||||
|
try:
|
||||||
|
proxy_sock.close()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
else:
|
else:
|
||||||
LOG.debug(f"Successfully logged in to '{login}'")
|
if isinstance(auth_failed, Exception):
|
||||||
return client, proxy_sock
|
raise auth_failed
|
||||||
|
|
||||||
|
|
||||||
def ssh_proxy_sock(hostname=None, port=None, command=None, client=None,
|
def ssh_proxy_sock(hostname=None, port=None, command=None, client=None,
|
||||||
|
|
Loading…
Reference in New Issue