Python 通过不同路径进行 SSH 迭代

Python SSH iteration through different paths

我有一个 JSON object 看起来像这样:

{UID_1:{
    jumpboxes:[jump_ip1, jump_ip2,...],
    hosts: [host_ip1, host_ip2,...],
    ...},
UID_2:{...

跳转箱的身份验证是通过 kerberos(无密码)进行的,主机的身份验证是使用密码进行的,并且主机只能通过跳转主机可见。我不知道 IP 列表中哪些有效,哪些被卡住,或者 non-responding,等等,所以我需要找到第一条可以让我打开 SSH session 的路径。

我能做的是检查 ssh-ing 到跳转主机时的退出代码,如下所示:

jumpip = ''
for i in json[uid][jumpboxes]:
    if os.system('ssh {}@{}'.format(username,i))>0:
        continue
    else:
        jumpip = i
        break

这给了我第一个工作的 jumpbox ip 没有问题,但是有一个密码来建立与第二个主机的 ssh 连接并不那么容易检查退出代码。

有多种方法可以打开隧道 - 使用 os.system() 和使用带有 session 代理的 sshpass(类似于: if os.system('sshpass -p {} ssh -o ProxyCommand="ssh {}@{} nc {} 22" {}@{} -t {}'.format(password, user, jumpip, hosts[j], user, hosts[j], remote_cmd))>0:....(对于上下文,我们假设 sshpass 命令看起来像这样:sshpass -p Password123! -o ProxyCommand="ssh user@jumpbox nc hostip 22" user@hostip -t ll 或在子 shell 中使用 os.system('ssh user@jumpbox -t ping {} -c 5'.format(hosts[j])) 执行 pint,尽管 ping 会 return 退出代码,ICMP 回显回复并不意味着我能够打开隧道(例如守护进程可能被卡住或可能已经崩溃等),或者我可以做一个 try-except-else 块,那尝试使用 pexpect 或 subrpocess.popen 通过跳转箱向远程主机打开 ssh session 并通过管道连接 stdio 从而允许我推送密码,如果未能引发自定义异常,但我无法弄清楚如何从 ssh 客户端获取退出代码,因此我可以检查状态...

这些对我来说都不够稳健,所以我宁愿正确地遍历 IP,对此我愿意征求建议。

一些背景知识 - 隧道将用于启动 nohup-ed 命令,然后将关闭。该脚本使用多处理和池来处理一大堆这些,所以我将启动它们,然后有一个循环来检查它们的状态并检索在主机上执行的远程脚本的结果。我知道 os.system 已被弃用,我应该使用子流程,但这对 use-case 来说不是必需的,所以我不太关心这个。我正在寻找一种聪明的方法来遍历可能的路径,这些路径将采用给定的带有长度为 n 的跳转框的列表和带有长度为 m 和超时 x 秒的主机的列表最多 n*m*x 秒来计算并缩短该时间。

我还使用 pexpect(它使用 paramiko 本身)与远程主机进行交互,一旦找到正确的 IP,我就需要打开隧道。

提前致谢!

Paramiko 的 exit_status_ready 函数会告诉你退出状态。

Return true if the remote process has exited and returned an exit status. You may use this to poll the process status if you don’t want to block in recv_exit_status. Note that the server may not return an exit status in some cases (like bad servers).

查看 pexpect 的源代码,我没有看到它在哪里使用 Paramiko,因此您可能需要将所有 pexpect 代码替换为 Paramiko 代码。 Paramiko 使您可以对建立 SSH 连接的所有底层方面进行大量控制,因此弄清楚它可能有点粗糙,但它确实使您可以对整个过程进行大量控制。

我想通了 - 如果有提示,pexpect 会提供一个退出代码,即我按照

的方式做了一些事情
host = ''
for i in hosts:
    cmd = 'ssh {}@{} -t ssh {}'.format(user,jumpbox, i)
    try:
        p = pexpect.spawn(cmd)
        if p.expect('.*') == 0:
            host = i
            break
    except:
        someException()
if host != '':
    ...

感谢所有的输入。