与Paramiko嵌套的SSH会话

时间:2009-12-16 01:28:07

标签: python ssh paramiko

我正在重写我写入Python的Bash脚本。该脚本的关键是

ssh -t first.com "ssh second.com very_remote_command"

我遇到了使用paramiko进行嵌套身份验证的问题。我无法找到任何处理我的确切情况的示例,但我能够在远程主机上找到 sudo 的示例。

The first method写入stdin

ssh.connect('127.0.0.1', username='jesse', password='lol')
stdin, stdout, stderr = ssh.exec_command("sudo dmesg")
stdin.write('lol\n')
stdin.flush()

The second创建了一个频道并使用类似套接字的发送 recv

我能够使用 stdin.write 来使用 sudo ,但它不能与远程主机上的 ssh 一起使用。

import paramiko

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('first.com', username='luser', password='secret')
stdin, stdout, stderr = ssh.exec_command('ssh luser@second.com')
stdin.write('secret')
stdin.flush()
print '---- out ----'
print stdout.readlines()
print '---- error ----'
print stderr.readlines()

ssh.close()

... ...打印

---- out ----
[]
---- error ----
['Pseudo-terminal will not be allocated because stdin is not a terminal.\r\n', 'Permission denied, please try again.\r\n', 'Permission denied, please try again.\r\n', 'Permission denied (publickey,password,keyboard-interactive).\r\n']

伪终端错误提醒我原始命令中的-t标志,所以我使用Channel切换到第二种方法。而不是ssh.exec_command和更高版本,我有:

t = ssh.get_transport()
chan = t.open_session()
chan.get_pty()
print '---- send ssh cmd ----'
print chan.send('ssh luser@second.com')
print '---- recv ----'
print chan.recv(9999)
chan = t.open_session()
print '---- send password ----'
print chan.send('secret')
print '---- recv ----'
print chan.recv(9999)

...但它打印'----发送ssh cmd ----'并挂起,直到我终止进程。

我是Python新手,对网络也不了解。在第一种情况下,为什么发送密码可以使用 sudo 而不能使用 ssh ?提示有何不同? paramiko甚至是正确的库吗?

5 个答案:

答案 0 :(得分:26)

我设法找到了解决方案,但它需要一些手动工作。如果有人有更好的解决方案,请告诉我。

ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('first.com', username='luser', password='secret')

chan = ssh.invoke_shell()

# Ssh and wait for the password prompt.
chan.send('ssh second.com\n')
buff = ''
while not buff.endswith('\'s password: '):
    resp = chan.recv(9999)
    buff += resp

# Send the password and wait for a prompt.
chan.send('secret\n')
buff = ''
while not buff.endswith('some-prompt$ '):
    resp = chan.recv(9999)
    buff += resp

# Execute whatever command and wait for a prompt again.
chan.send('ls\n')
buff = ''
while not buff.endswith('some-prompt$ '):
    resp = chan.recv(9999)
    buff += resp

# Now buff has the data I need.
print 'buff', buff

ssh.close()

需要注意的是,而不是这个

t = ssh.get_transport()
chan = t.open_session()
chan.get_pty()

......你想要这个

chan = ssh.invoke_shell()

它让我想起当我小时候写一个TradeWars脚本并放弃编码十年的时候。 :)

答案 1 :(得分:15)

这是一个仅使用paramiko(和端口转发)的小例子:

import paramiko as ssh

class SSHTool():
    def __init__(self, host, user, auth,
                 via=None, via_user=None, via_auth=None):
        if via:
            t0 = ssh.Transport(via)
            t0.start_client()
            t0.auth_password(via_user, via_auth)
            # setup forwarding from 127.0.0.1:<free_random_port> to |host|
            channel = t0.open_channel('direct-tcpip', host, ('127.0.0.1', 0))
            self.transport = ssh.Transport(channel)
        else:
            self.transport = ssh.Transport(host)
        self.transport.start_client()
        self.transport.auth_password(user, auth)

    def run(self, cmd):
        ch = self.transport.open_session()
        ch.set_combine_stderr(True)
        ch.exec_command(cmd)
        retcode = ch.recv_exit_status()
        buf = ''
        while ch.recv_ready():
            buf += ch.recv(1024)
        return (buf, retcode)

# The example below is equivalent to
# $ ssh 10.10.10.10 ssh 192.168.1.1 uname -a
# The code above works as if these 2 commands were executed:
# $ ssh -L <free_random_port>:192.168.1.1:22 10.10.10.10
# $ ssh 127.0.0.1:<free_random_port> uname -a
host = ('192.168.1.1', 22)
via_host = ('10.10.10.10', 22)

ssht = SSHTool(host, 'user1', 'pass1',
    via=via_host, via_user='user2', via_auth='pass2')

print ssht.run('uname -a')

答案 2 :(得分:7)

您可以使用其他ssh连接的频道创建ssh连接。有关详细信息,请参阅here

答案 3 :(得分:1)

对于现成的解决方案,请查看pxpect项目中的pxssh。查看sshls.py和ssh_tunnel.py示例。

http://www.noah.org/wiki/Pexpect

答案 4 :(得分:0)

Sinas的答案效果很好,但并没有为我提供很长命令的所有输出。但是,使用chan.makefile()可以检索所有输出。

以下适用于需要tty并且还提示输入sudo密码的系统

ssh = paramiko.SSHClient()
ssh.load_system_host_keys()
ssh.set_missing_host_key_policy(paramiko.WarningPolicy())
ssh.connect("10.10.10.1", 22, "user", "password")
chan=ssh.get_transport().open_session()
chan.get_pty()
f = chan.makefile()
chan.exec_command("sudo dmesg")
chan.send("password\n")
print f.read()
ssh.close()