I am trying to ssh tunnel from PC --> server1 ---> server2 ----> switch1
This is possible through a regular terminal with a simple: ssh switch1 it references my ssh_config which reads:
Host server1
user bill
Hostname server1
ForwardAgent yes
IdentityFile ~/.ssh/id_rsa
ProxyCommand none
Host server2
user bill
Hostname server2
IdentityFile ~/.ssh/id_rsa
ProxyCommand ssh server1 /usr/bin/nc %h %p
Host switch1
user bill
Hostname %h.omniture.com
ProxyCommand ssh server2 /usr/bin/nc %h %p
Not a problem from regular terminal. But trying to build a python script to do it has proven to be difficult.
Script is as follows:
import paramiko
import subprocess
import getpass
import os
def ssh_command(ip, user, passwd, command):
client = paramiko.SSHClient()
client.load_host_keys('/Users/bill/.ssh/known_hosts')
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
config = paramiko.SSHConfig()
if os.path.exists('/etc/ssh/ssh_config'):
config.parse(open('/etc/ssh/ssh_config'))
if os.path.exists(os.path.expanduser('~/.ssh/config')):
config.parse(open(os.path.expanduser('~/.ssh/config')))
host = config.lookup(ip)
if 'proxycommand' in host:
proxy = paramiko.ProxyCommand(
subprocess.check_output(
[os.environ['SHELL'], '-c', 'echo %s' %
host['proxycommand']]
).strip()
)
else:
proxy = None
client.connect(host['hostname'], username='bill',
password=getpass.getpass(), sock=proxy)
ssh_session = client.get_transport().open_session()
if ssh_session.active:
ssh_session.exec_command(command)
print ssh_session.recv(1024)
return
ssh_command('sw-a-102.sin2', 'bill', getpass.getpass(), 'show ver')
The error I get is:
No handlers could be found for logger "paramiko.transport"
Traceback (most recent call last):
File "/Users/bill/git/tools/python/dns-check/proxy-test.py", line 34, in <module>
ssh_command('switch1', 'bill', getpass.getpass(), 'show ver')
File "/Users/bill/git/tools/python/dns-check/proxy-test.py", line 28, in ssh_command
client.connect(host['hostname'], username='bill', password=getpass.getpass(), sock=proxy)
File "/Library/Python/2.7/site-packages/paramiko/client.py", line 265, in connect
t.start_client()
File "/Library/Python/2.7/site-packages/paramiko/transport.py", line 406, in start_client
raise e
paramiko.ssh_exception.ProxyCommandFailure: ('ssh server2 /usr/bin/nc switch1 22', 'Broken pipe')
If I can get this to work using paramiko that would be great, if someone knows a better way, that would be good too. Thank you for your time.
There is a better way, still with Paramiko but without ProxyCommand configuration.
Reason being paramiko's proxy command support is buggy and prone to race conditions, which is the cause of the error above.
OTOH, SSH has native support for tunneling in the protocol itself and does not require external tools to achieve it.
import paramiko
# Make clients
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.MissingHostKeyPolicy())
server2_client = paramiko.SSHClient()
server2_client.set_missing_host_key_policy(paramiko.MissingHostKeyPolicy())
switch1_client = paramiko.SSHClient()
switch1_client.set_missing_host_key_policy(paramiko.MissingHostKeyPolicy())
# Connect to server1 normally
client.connect('server1')
# Make channel server1 -> server2
server2_chan = client.get_transport().open_channel('direct-tcpip', ('<server2 ip>', 22,), ('127.0.0.1', 0))
# Connect to server2 via server1's channel
server2_client.connect('<server2 ip>', sock=server1_chan)
# Make channel server2 -> switch1
switch1_chan = server2_client.get_transport().open_channel('direct-tcpip', ('<switch1 ip>', 22,), ('127.0.0.1', 0))
# Finally connect to switch1 via server2 channel
switch1_client.connect('switch1', sock=server2_chan)
switch1_client.exec_command(<command>)
Substitute server and switch names with their IP addresses.
See also a parallel SSH client based on paramiko with support for native SSH tunneling.
Related
I am attempting to use paramiko with iproxy to ssh to an iOS host, after Catalina seemingly killed my other method.
Firstly I forward local port to device port
iproxy 2222 22 -u <UUIDxxxxxx>
This can be seen on netstat that its listening
tcp6 0 0 ::1.2222 *.* LISTEN
tcp4 0 0 127.0.0.1.2222 *.* LISTEN
However with paramiko, I always get a NoValidConnections error
File "/usr/local/lib/python2.7/site-packages/paramiko/client.py", line 368, in connect raise NoValidConnectionsError(errors)
paramiko.ssh_exception.NoValidConnectionsError: [Errno None] Unable to connect to port 2222 on 127.0.0.1
Current python code is:
sshIP = "127.0.0.1"
sshPort = 2222
ssh_client =paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(hostname=sshIP,username='root',password=sshPass,port=sshPort)
stdin,stdout,stderr = ssh_client.exec_command("sw_vers")
prodver = stdout.readlines()
SSHing manually from cli works fine.
####IPROXY OUTPUT
New connection for 2222->22, fd = 5
waiting for connection
Requesting connecion to USB device handle 571 (serial: UUIDXXXXX), port 22
# CLI SSH
$ ssh root#127.0.0.1 -p 2222
Warning: Permanently added '[127.0.0.1]:2222' (RSA) to the list of known hosts.
root#127.0.0.1's password:
Last login: Tue May 25 11:41:30 2021 from 127.0.0.1
iPhone-Black:~ root#
Not too sure what was the overall fix, but I added a time.sleep(3) after calling iproxy to ensure that it had started successfully before calling paramiko.
I also changed the order of the paramiko options, and added port= after hostname= as per paramiko documentation.
connect(hostname, port=port, username=None, password=None,
Both of these seem to have helped and connections are now made correctly.
ssh_client =paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect(hostname=sshIP,port=sshPort,username='root',password=sshPass)
in the terminal, the following works just fine:
> ssh me#host -p 22
but the following python code gives me a TimeoutError:
from paramiko import SSHClient
client = SSHClient()
client.connect(hostname='host', port=22, username='me', look_for_keys=True)
Any idea why that would be the case? The connection uses RSA keys for authentication, and I'm over a VPN. Seems like I'm missing some context I need to tell paramiko about, but I can't figure out what it is.
I usually connect to a remote server by adding an explicit link to my private key:
HOST = 'hostname'
USER = 'username'
PKEY = '/path/to/rsa_private_key'
PKEY_PASS = 'xxx'
k = paramiko.RSAKey.from_private_key_file(PKEY, PKEY_PASS)
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
print "connecting"
ssh.connect(HOST, username=USER, pkey=k)
connect_status = 'connected' if ssh else 'failed to connect'
print connect_status
I need to connect to SFTP server using proxy command.
I know how to connect to SFTP server directly:
paramiko's sshclient with sftp
I can open an SSH connection via proxy command using this code:
import paramiko
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
target_host = 'sftp.XXXXX.co'
target_port = 22
proxy = paramiko.proxy.ProxyCommand('/usr/bin/nc --proxy proxy_host:8080 %s %d' % (target_host, target_port) )
client.connect(hostname=target_host,username='username', port=target_port, password='XXXXXXXX', sock=proxy)
But I need to create SFTPClient, not SSHClient. But I do not know how to pass the ProxyCommand to the SFTPClient.
To connect to SFTP server using a "custom socket", do:
proxy = paramiko.proxy.ProxyCommand(...)
transport = paramiko.Transport(proxy)
transport.connect(username = username, password = password)
sftp = paramiko.SFTPClient.from_transport(transport)
I am trying to use paramiko to connect to a remote host. However, in order to connect, I need to specify a bind address, which you can do with OpenSSH via:
ssh -o BindAddress=x.x.x.x user#host
I've been searching high and low for an equivalent option in the paramiko SSHClient docs, but I can't seem to find it. It seems like this would be a standard option to have. Can someone point me in the right direction? Do I need to create a separate socket connection and use that?
From ssh_config manual:
BindAddress
Use the specified address on the local machine as the source address of the connection.
You might provide a socket to client's connect function, so you might bind the source address in the socket.
Example:
import socket
import paramiko
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('192.168.223.21', 0)) # set source address
sock.connect(('192.168.223.23', 22)) # connect to the destination address
client = paramiko.SSHClient()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect('192.168.223.23',
username=username,
password=password,
sock=sock) # pass socket to Paramiko
Documentation: http://docs.paramiko.org/en/2.4/api/client.html).
connect(hostname, port=22, username=None, password=None, pkey=None,
key_filename=None, timeout=None, allow_agent=True, look_for_keys=True,
compress=False, sock=None, gss_auth=False, gss_kex=False,
gss_deleg_creds=True, gss_host=None, banner_timeout=None,
auth_timeout=None, gss_trust_dns=True, passphrase=None)
sock (socket) – an open socket or socket-like object (such as a
Channel) to use for communication to the target host
I am trying to connect via SSH to a computer tunneling through another computer using paramiko in Python, but I am having some strange issues.
My config file in /.ssh/config looks like this:
Host remoteA
HostName 169.254.1.1
User root
IdentityFile ~/.ssh/id_dss.openssh.remoteA
ForwardX11 no
StrictHostKeyChecking no
ForwardAgent yes
UserKnownHostsFile /dev/null
Host remoteB
User root
IdentityFile ~/.ssh/id_dss.openssh.remoteB
ForwardX11 no
StrictHostKeyChecking no
UserKnownHostsFile /dev/null
ProxyCommand ssh -W 169.254.1.2:22 remoteA
And my python code like this:
from paramiko import SSHClient,SSHConfig,SSHException
import getpass
import paramiko
def getSSHConnection(hostName):
config = SSHConfig()
user = getpass.getuser()
config.parse(open('C:/Users/' + user +'/.ssh/config'))
host=config.lookup(hostName)
# setup SSH client
client = SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
#Check for proxy settings
try:
print host ['proxycommand']
proxy = paramiko.ProxyCommand(host['proxycommand'])
except:
proxy = None
#Setup the SSH connection
try:
if (proxy is None):
client.connect(host['hostname'],22,username=host['user'],key_filename=host['identityfile'])
else:
client.connect(host['hostname'],22,username=host['user'], key_filename=host['identityfile'],sock=proxy)
except SSHException,ex:
print ex
return client
ssh_client = getSSHConnection('remoteA')
# run a command
print "\nRun a command"
cmd = 'ps aux'
stdin,stdout,stderr = ssh_client.exec_command(cmd)
print stdout.read()
ssh_client = getSSHConnection('remoteB')
# run a command
print "\nRun a command"
cmd = 'ps aux'
stdin,stdout,stderr = ssh_client.exec_command(cmd)
print stdout.read()
First when connecting to remoteA it works perfectly,but then when connecting to remoteB it crashes in the step: proxy = paramiko.ProxyCommand(host['proxycommand']).
File "C:\Python27\lib\site-packages\paramiko\client.py",line 291,in connect
for (family,socktype,proto,canonname,sockaddr) in socket.getaddrinfo(hostname,port, socket.AF_UNSPEC,socket.SOCK_STREAM):
socket.gaierror: [Errno 11004] getaddrinfo failed
Within Cygwin,I am able to connect using the command ssh remoteB so I know the config file should be ok.
If it makes any difference,I am running this on Windows 7.
I think you can stick to your first solution. Just you need to change your proxy command and make sure your jump host has got nc installed
I use a proxy command like this
proxy = paramiko.ProxyCommand("ssh -o StrictHostKeyChecking=no jumphostIP nc targethostIP 22")
I found this WiKi very useful
http://en.wikibooks.org/wiki/OpenSSH/Cookbook/Proxies_and_Jump_Hosts
Just use actual values instead of %h and %p as I have done.
So instead of using ProxyCommand I used port forwarding to solve my issue.
def getForwardedSSHPort(self, tunnnelHostName):
forwarderClient = self.getSSHConnection(tunnnelHostName, None)
transport = forwarderClient.get_transport()
dest_addr = ('169.254.1.2', 22)
local_addr = ('127.0.0.1', 10022)
channel = transport.open_channel('direct-tcpip', dest_addr, local_addr)
remoteClient = self.getSSHConnection( tunnnelHostName, channel)