def secure_copy(user, host, src, dest, key_filename=None, allow_agent=True):
keys = _load_keys(key_filename, allow_agent)
pkey = keys[0]
ssh = paramiko.SSHClient()
proxy = None
ssh_config_file = os.path.expanduser("~/.ssh/config")
if os.path.exists(ssh_config_file):
conf = paramiko.SSHConfig()
with open(ssh_config_file) as f:
conf.parse(f)
host_config = conf.lookup(host)
if 'proxycommand' in host_config:
proxy = paramiko.ProxyCommand(host_config['proxycommand'])
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host, username=user, pkey=pkey, sock=proxy)
scp = SCPClient(ssh.get_transport())
scp.get(src, dest)
scp.close()
python类ProxyCommand()的实例源码
def dummytest():
"""
Code posted on the github issue regarding the SSH Banner Error on the paramiko github.
https://github.com/paramiko/paramiko/issues/673
"""
import os
import paramiko
import logging
logging.basicConfig(level=logging.DEBUG)
# Loading ssh configuration to get the IP and user of the desired host (here 'bastion')
cfg = paramiko.SSHConfig()
with open(os.path.expanduser("~/.ssh/config")) as f:
cfg.parse(f)
host_cfg = cfg.lookup('bastion')
sock = paramiko.ProxyCommand("ssh {}@{} nc 10.8.9.160 22".format(host_cfg.get('user'), host_cfg.get('hostname')))
sock.settimeout(30)
# Sock stays open until a client tries to use it (or the program exits)
# Client Setup
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
# Connect and execute command
# The following line hangs for 15 seconds (increase with banner_timeout argument) and raises an SSHError
client.connect("10.8.9.160", username='root', sock=sock)
(stdin, stdout, stderr) = client.exec_command("echo 'Hello World !'")
for line in stdout.readlines():
print(line)
client.close()
def paramiko_connect(host):
client = paramiko.SSHClient()
client._policy = paramiko.WarningPolicy()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_config = paramiko.SSHConfig()
user_config_file = os.path.expanduser("~/.ssh/config")
try:
with open(user_config_file) as f:
ssh_config.parse(f)
except FileNotFoundError:
print("{} file could not be found. Aborting.".format(user_config_file))
sys.exit(1)
cfg = {'hostname': options['hostname'], 'username': options["username"]}
user_config = ssh_config.lookup(cfg['hostname'])
for k in ('hostname', 'username', 'port'):
if k in user_config:
cfg[k] = user_config[k]
if 'proxycommand' in user_config:
cfg['sock'] = paramiko.ProxyCommand(user_config['proxycommand'])
return client.connect(**cfg)
def connect(self):
"""Connect to ssh server."""
if not self.closed:
raise RuntimeError('SSH is already opened')
sock = paramiko.ProxyCommand(self._proxy_cmd) \
if self._proxy_cmd else None
self._ssh = paramiko.SSHClient()
self._ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self._ssh.connect(
self._host,
self._port,
pkey=self._pkey,
timeout=self._timeout,
banner_timeout=self._timeout,
username=self._username,
password=self._password,
sock=sock)
def through(self, host, user='root'):
"""
Defines a proxy command to rebound on the host.
This method is actually unsafe to use and will cause an SSH Banner Error. This library can't work through a
proxy
:param string host: Either a valid name stored in ~/.ssh/config or a valid IP address.
:param string user: A valid user for the host. Default : 'root'.
:return: This instance. Allows to chain methods.
:rtype: CloudApp
"""
ssh_conf_file = os.path.expanduser("~/.ssh/config")
try:
ssh_conf = paramiko.SSHConfig()
with open(ssh_conf_file) as f:
ssh_conf.parse(f)
if host in ssh_conf.get_hostnames():
host_conf = ssh_conf.lookup(host)
user = host_conf.get('user', 'root')
host = host_conf.get('hostname')
else:
print("Could not find host {} in {}. Using raw hostname.".format(host, ssh_conf_file))
except FileNotFoundError as e:
print("Could not load {} : {}. Using raw hostname.".format(ssh_conf_file, e))
# "ssh -W %h:%p {}@{}" doesn't create an entry in the logs. "ssh {}@{} nc %h %p"
# Actually connects to name (causing an entry in the logs)
print("ssh {}@{} nc {} 22".format(user, host, self.instance.private_ip_address))
self.ssh_sock = paramiko.ProxyCommand("ssh {}@{} nc {} 22".format(user, host, self.instance.private_ip_address))
return self
def _connect(self):
if 'proxycommand' in self.config:
proxy = paramiko.ProxyCommand(self.config['proxycommand'])
# TODO: check this code, needed?
#subprocess.check_output(
# [os.environ['SHELL'], '-c',
# 'echo %s' % self.config['proxycommand']]
#).strip()
else:
proxy = None
# Connect to server
# Compression will not speedup picture transfers, but will help for
# the initial remote hash download and for files that are compressible.
# noinspection PyTypeChecker
self.client.connect(
self.config.get('hostname', self.hostname),
username=self.user or self.config.get('user', None),
password=self.password,
port=self.config.get('port', SSH_PORT),
sock=proxy,
compress=True)
transport = self.client.get_transport()
# https://github.com/paramiko/paramiko/issues/175
transport.window_size = 2147483647
# 512MB -> 4GB, this is a security degradation
transport.packetizer.REKEY_BYTES = pow(2, 32)
self.sftp = self.client.open_sftp()
def ssh_connection(self):
"""Reusable :obj:`paramiko.client.SSHClient`."""
if hasattr(self, "_ssh_connection"):
return self._ssh_connection
# TODO configurable
ssh_config_file = os.path.join(os.path.expanduser("~"), ".ssh", "config")
client = paramiko.SSHClient()
client._policy = paramiko.WarningPolicy()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_config = paramiko.SSHConfig()
if os.path.exists(ssh_config_file):
with open(ssh_config_file) as f:
ssh_config.parse(f)
parameters = {
"hostname": self.host,
"username": self.username,
"password": self.password,
"port": self.ssh_port,
}
user_config = ssh_config.lookup(self.host)
for k in ('hostname', 'username', 'port'):
if k in user_config:
parameters[k] = user_config[k]
if 'proxycommand' in user_config:
parameters['sock'] = paramiko.ProxyCommand(user_config['proxycommand'])
# TODO configurable
# if ssh_key_file:
# parameters['key_filename'] = ssh_key_file
if 'identityfile' in user_config:
parameters['key_filename'] = user_config['identityfile']
client.connect(**parameters)
self._ssh_connection = client
return client
def _parse_proxy_command(self, port=22):
proxy_command = None
# Parse ansible_ssh_common_args, specifically looking for ProxyCommand
ssh_args = [
getattr(self._play_context, 'ssh_extra_args', ''),
getattr(self._play_context, 'ssh_common_args', ''),
getattr(self._play_context, 'ssh_args', ''),
]
if ssh_args is not None:
args = self._split_ssh_args(' '.join(ssh_args))
for i, arg in enumerate(args):
if arg.lower() == 'proxycommand':
# _split_ssh_args split ProxyCommand from the command itself
proxy_command = args[i + 1]
else:
# ProxyCommand and the command itself are a single string
match = SETTINGS_REGEX.match(arg)
if match:
if match.group(1).lower() == 'proxycommand':
proxy_command = match.group(2)
if proxy_command:
break
proxy_command = proxy_command or C.PARAMIKO_PROXY_COMMAND
sock_kwarg = {}
if proxy_command:
replacers = {
'%h': self._play_context.remote_addr,
'%p': port,
'%r': self._play_context.remote_user
}
for find, replace in replacers.items():
proxy_command = proxy_command.replace(find, str(replace))
try:
sock_kwarg = {'sock': paramiko.ProxyCommand(proxy_command)}
display.vvv("CONFIGURE PROXY COMMAND FOR CONNECTION: %s" % proxy_command, host=self._play_context.remote_addr)
except AttributeError:
display.warning('Paramiko ProxyCommand support unavailable. '
'Please upgrade to Paramiko 1.9.0 or newer. '
'Not using configured ProxyCommand')
return sock_kwarg
def ssh(host, forward_agent=False, sudoable=False, max_attempts=1, max_timeout=5):
"""Manages a SSH connection to the desired host.
Will leverage your ssh config at ~/.ssh/config if available
:param host: the server to connect to
:type host: str
:param forward_agent: forward the local agents
:type forward_agent: bool
:param sudoable: allow sudo commands
:type sudoable: bool
:param max_attempts: the maximum attempts to connect to the desired host
:type max_attempts: int
:param max_timeout: the maximum timeout in seconds to sleep between attempts
:type max_timeout: int
:returns a SSH connection to the desired host
:rtype: Connection
:raises MaxConnectionAttemptsError: Exceeded the maximum attempts
to establish the SSH connection.
"""
with closing(SSHClient()) as client:
client.set_missing_host_key_policy(AutoAddPolicy())
cfg = {
"hostname": host,
"timeout": max_timeout,
}
ssh_config = SSHConfig()
user_config_file = os.path.expanduser("~/.ssh/config")
if os.path.exists(user_config_file):
with open(user_config_file) as f:
ssh_config.parse(f)
host_config = ssh_config.lookup(host)
if "user" in host_config:
cfg["username"] = host_config["user"]
if "proxycommand" in host_config:
cfg["sock"] = ProxyCommand(host_config["proxycommand"])
if "identityfile" in host_config:
cfg['key_filename'] = host_config['identityfile']
if "port" in host_config:
cfg["port"] = int(host_config["port"])
attempts = 0
while attempts < max_attempts:
try:
attempts += 1
client.connect(**cfg)
break
except socket.error:
if attempts < max_attempts:
time.sleep(max_timeout)
else:
raise MaxConnectionAttemptsError(
"Exceeded max attempts to connect to host: {0}".format(max_attempts)
)
yield Connection(client, forward_agent, sudoable)
def connect(self):
"""Finish building the client and connects to the target server, returning a paramiko client object"""
assert self._client_class
assert self._hostname is not None, 'destination hostname was not specified'
client = self._client_class()
if self._missing_host_key_policy:
client.set_missing_host_key_policy(self._missing_host_key_policy())
config_data = self._config.lookup(self._hostname)
ssh_kwargs = {
'timeout': self._timeout,
'banner_timeout': self._banner_timeout,
'port': self._port
}
# unless one is explicitely specified with .user(), get our username from configuration, defaulting to $USER
if self._username is None:
ssh_kwargs['username'] = config_data.get('user', os.getenv('USER'))
else:
ssh_kwargs['username'] = self._username
if self._password is not None:
ssh_kwargs['password'] = self._password
if 'proxycommand' in config_data:
ssh_kwargs['sock'] = paramiko.ProxyCommand(config_data['proxycommand'])
elif self._proxy_command is not None:
ssh_kwargs['sock'] = paramiko.ProxyCommand(self._proxy_command)
if config_data.get('identity_file') is not None:
ssh_kwargs['key_filename'] = config_data.get('identity_file')
# unless explicitely specified with .allow_agent, allow agent by default unless identitiesonly is yes in our config
if self._allow_agent is None:
ssh_kwargs['allow_agent'] = config_data.get('identitiesonly', 'no') != 'yes'
else:
ssh_kwargs['allow_agent'] = self._allow_agent
if self._sock is not None:
ssh_kwargs['sock'] = self._sock
logging.debug('Connecting to %s with options %s', config_data['hostname'], ssh_kwargs)
client.connect(config_data['hostname'], **ssh_kwargs)
return client
def _parse_proxy_command(self, port=22):
proxy_command = None
# Parse ansible_ssh_common_args, specifically looking for ProxyCommand
ssh_args = [
getattr(self._play_context, 'ssh_extra_args', '') or '',
getattr(self._play_context, 'ssh_common_args', '') or '',
getattr(self._play_context, 'ssh_args', '') or '',
]
if ssh_args is not None:
args = self._split_ssh_args(' '.join(ssh_args))
for i, arg in enumerate(args):
if arg.lower() == 'proxycommand':
# _split_ssh_args split ProxyCommand from the command itself
proxy_command = args[i + 1]
else:
# ProxyCommand and the command itself are a single string
match = SETTINGS_REGEX.match(arg)
if match:
if match.group(1).lower() == 'proxycommand':
proxy_command = match.group(2)
if proxy_command:
break
proxy_command = proxy_command or C.PARAMIKO_PROXY_COMMAND
sock_kwarg = {}
if proxy_command:
replacers = {
'%h': self._play_context.remote_addr,
'%p': port,
'%r': self._play_context.remote_user
}
for find, replace in replacers.items():
proxy_command = proxy_command.replace(find, str(replace))
try:
sock_kwarg = {'sock': paramiko.ProxyCommand(proxy_command)}
display.vvv("CONFIGURE PROXY COMMAND FOR CONNECTION: %s" % proxy_command, host=self._play_context.remote_addr)
except AttributeError:
display.warning('Paramiko ProxyCommand support unavailable. '
'Please upgrade to Paramiko 1.9.0 or newer. '
'Not using configured ProxyCommand')
return sock_kwarg