Paramiko:将ssh会话嵌套到另一台计算机,同时保留Paramiko功能(ProxyJump)

2024-06-03 04:24:02 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试使用paramiko通过netcat弹出SSH会话:

 MyLocalMachine ----||----> MiddleMachine --(netcat)--> AnotherMachine
 ('localhost')  (firewall)   ('1.1.1.1')                 ('2.2.2.2')
  • 没有从MyLocalMachine到的直接连接 AnotherMachine
  • MiddleMachine上的SSH服务器将不接受任何打开连接到AnotherMachine的直接tcpip通道的尝试
  • 我不能使用SSH密钥。我只能通过给定的用户名和密码连接。在
  • 我不能使用sshpass
  • 我不能使用PExpect
  • 我想自动连接
  • 我想保留所有的paramiko功能

我可以部分使用以下代码来实现:

^{pr2}$

问题是,因为ProxyCommand正在使用subprocess.Popen来运行给定的命令,所以它要求我从用户输入中给密码“ad-hoc”(另外,它要求MyLocalMachine上的操作系统必须安装ssh,但情况并非总是如此)。在

由于ProxyCommand的方法(recvsend)是对适当的POpen方法的简单绑定,我想知道是否可以诱使paramiko客户机使用另一个客户机的会话作为代理?在


Tags: 方法服务器localhostparamiko密码客户机密钥ssh
2条回答

更新15.05.18:添加了丢失的代码(复制粘贴神对我不好)。

TL;DR:我使用简单的exec_command调用和一个假装为sock的类来完成这项工作。

总结一下:

  • 此解决方案不使用22以外的任何端口。如果您可以通过嵌套ssh客户端手动连接到机器,那么它就可以工作了。它不需要任何端口转发或配置更改。在
  • 它不提示输入密码(一切都是自动的)
  • 它嵌套ssh会话,同时保留paramiko功能。在
  • 您可以任意多次嵌套会话
  • 它需要在代理主机上安装netcat(nc),尽管任何可以提供基本netcat功能(在套接字和stdin/stdout之间移动数据)的功能都可以。在

解决方法是:

化妆师

下面的代码定义了一个可以用来代替paramiko.ProxyCommand的类。它提供标准socket对象所做的所有方法。此类的init方法采用exec_command()通常返回的3元组:

注意:我已经对它进行了广泛的测试,但你不应该把任何事情都当作理所当然。这是一次黑客攻击。

import paramiko
import time
import socket     
from select import select                                                       


class ParaProxy(paramiko.proxy.ProxyCommand):                      
    def __init__(self, stdin, stdout, stderr):                             
        self.stdin = stdin                                                 
        self.stdout = stdout                                               
        self.stderr = stderr
        self.timeout = None
        self.channel = stdin.channel                                               

    def send(self, content):                                               
        try:                                                               
            self.stdin.write(content)                                      
        except IOError as exc:                                             
            raise socket.error("Error: {}".format(exc))                                                    
        return len(content)                                                

    def recv(self, size):                                                  
        try:
            buffer = b''
            start = time.time()

            while len(buffer) < size:
                select_timeout = self._calculate_remaining_time(start)
                ready, _, _ = select([self.stdout.channel], [], [],
                                     select_timeout)
                if ready and self.stdout.channel is ready[0]:
                      buffer += self.stdout.read(size - len(buffer))

        except socket.timeout:
            if not buffer:
                raise

        except IOError as e:
            return ""

        return buffer

    def _calculate_remaining_time(self, start):
        if self.timeout is not None:
            elapsed = time.time() - start
            if elapsed >= self.timeout:
                raise socket.timeout()
            return self.timeout - elapsed
        return None                                   

    def close(self):                                                       
        self.stdin.close()                                                 
        self.stdout.close()                                                
        self.stderr.close()
        self.channel.close()                                                                                                                            

用法

以下是我如何使用上述类来解决我的问题:

^{pr2}$

等等。在

最好将此作为建议答案发布,您可以执行以下操作:

代码没有经过测试,也不会工作,因为它非常不完整。我建议你检查一下这个神奇的图腾供参考http://www.revsys.com/writings/quicktips/ssh-tunnel.html

从中间的机器

"ssh -f user@anothermachine -L 2000:localhost:22 -N"

从本地计算机:

^{pr2}$

相关问题 更多 >