Paramiko:将 ssh 会话嵌套到另一台机器,同时保留 paramiko 功能(ProxyJump)
Paramiko: nest ssh session to another machine while preserving paramiko functionality (ProxyJump)
我正在尝试使用 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 功能
我可以使用以下代码部分实现此目的:
cli = paramiko.SSHClient()
cli.set_missing_host_key_policy(paramiko.AutoAddPolicy())
proxy = paramiko.ProxyCommand('ssh user@1.1.1.1 nc 2.2.2.2 22')
cli.connect(hostname='2.2.2.2', username='user', password='pass', sock=proxy)
问题是,因为 ProxyCommand
使用 subprocess.Popen
到 运行 给定的命令,它要求我从用户输入中输入密码 "ad-hoc" (另外,它需要 MyLocalMachine
上的 OS 安装 ssh
- 但情况并非总是如此)。
由于 ProxyCommand
的方法(recv
、send
)是对适当 POpen
方法的简单绑定,我想知道是否可以欺骗paramiko 客户端使用另一个客户端的会话作为代理?
最好post将此作为建议的答案,您可以执行以下操作:
代码未经过测试也不会工作,因为它非常不完整。我建议查看这个惊人的图作为参考 http://www.revsys.com/writings/quicktips/ssh-tunnel.html
来自中间机器
"ssh -f user@anothermachine -L 2000:localhost:22 -N"
来自本地计算机:
paramiko.connect(middlemachine, 2000)
更新 15.05.18:添加了缺失的代码(copy-paste 上帝对我不利)。
TL;DR:我设法使用简单的 exec_command
调用和伪装成 sock
. 的 class 来做到这一点
总结:
- 此解决方案不使用除 22 以外的任何其他端口。如果您可以通过嵌套 ssh 客户端手动连接到计算机 - 它会起作用。它不需要任何端口转发或配置更改。
- 无需提示输入密码即可运行(一切都是自动的)
- 它在保留 paramiko 功能的同时嵌套 ssh 会话。
- 您可以根据需要多次嵌套会话
- 它需要在代理主机上安装 netcat (
nc
) - 尽管任何可以提供基本 netcat 功能(在套接字和 [=58= 之间移动数据) ]) 会起作用。
所以,这里是解决方案:
伪装者
下面的代码定义了一个class,可以用来代替paramiko.ProxyCommand
。它提供标准 socket
对象的所有方法。这个 class 的 init 方法通常采用 exec_command()
的三元组 returns:
注意:我对它进行了广泛的测试,但你不应该认为任何事情都是理所当然的。这是一个黑客。
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()
用法
下面展示了我是如何使用上面的class来解决我的问题的:
# Connecting to MiddleMachine and executing netcat
mid_cli = paramiko.SSHClient()
mid_cli.set_missing_host_key_policy(paramiko.AutoAddPolicy())
mid_cli.connect(hostname='1.1.1.1', username='user', password='pass')
io_tupple = mid_cli.exec_command('nc 2.2.2.2 22')
# Instantiate the 'masquerader' class
proxy = ParaProxy(*io_tupple)
# Connecting to AnotherMachine and executing... anything...
end_cli = paramiko.SSHClient()
end_cli.set_missing_host_key_policy(paramiko.AutoAddPolicy())
end_cli.connect(hostname='2.2.2.2', username='user', password='pass', sock=proxy)
end_cli.exec_command('echo THANK GOD FINALLY')
瞧瞧。
我正在尝试使用 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 功能
我可以使用以下代码部分实现此目的:
cli = paramiko.SSHClient()
cli.set_missing_host_key_policy(paramiko.AutoAddPolicy())
proxy = paramiko.ProxyCommand('ssh user@1.1.1.1 nc 2.2.2.2 22')
cli.connect(hostname='2.2.2.2', username='user', password='pass', sock=proxy)
问题是,因为 ProxyCommand
使用 subprocess.Popen
到 运行 给定的命令,它要求我从用户输入中输入密码 "ad-hoc" (另外,它需要 MyLocalMachine
上的 OS 安装 ssh
- 但情况并非总是如此)。
由于 ProxyCommand
的方法(recv
、send
)是对适当 POpen
方法的简单绑定,我想知道是否可以欺骗paramiko 客户端使用另一个客户端的会话作为代理?
最好post将此作为建议的答案,您可以执行以下操作:
代码未经过测试也不会工作,因为它非常不完整。我建议查看这个惊人的图作为参考 http://www.revsys.com/writings/quicktips/ssh-tunnel.html
来自中间机器
"ssh -f user@anothermachine -L 2000:localhost:22 -N"
来自本地计算机:
paramiko.connect(middlemachine, 2000)
更新 15.05.18:添加了缺失的代码(copy-paste 上帝对我不利)。
TL;DR:我设法使用简单的 exec_command
调用和伪装成 sock
. 的 class 来做到这一点
总结:
- 此解决方案不使用除 22 以外的任何其他端口。如果您可以通过嵌套 ssh 客户端手动连接到计算机 - 它会起作用。它不需要任何端口转发或配置更改。
- 无需提示输入密码即可运行(一切都是自动的)
- 它在保留 paramiko 功能的同时嵌套 ssh 会话。
- 您可以根据需要多次嵌套会话
- 它需要在代理主机上安装 netcat (
nc
) - 尽管任何可以提供基本 netcat 功能(在套接字和 [=58= 之间移动数据) ]) 会起作用。
所以,这里是解决方案:
伪装者
下面的代码定义了一个class,可以用来代替paramiko.ProxyCommand
。它提供标准 socket
对象的所有方法。这个 class 的 init 方法通常采用 exec_command()
的三元组 returns:
注意:我对它进行了广泛的测试,但你不应该认为任何事情都是理所当然的。这是一个黑客。
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()
用法
下面展示了我是如何使用上面的class来解决我的问题的:
# Connecting to MiddleMachine and executing netcat
mid_cli = paramiko.SSHClient()
mid_cli.set_missing_host_key_policy(paramiko.AutoAddPolicy())
mid_cli.connect(hostname='1.1.1.1', username='user', password='pass')
io_tupple = mid_cli.exec_command('nc 2.2.2.2 22')
# Instantiate the 'masquerader' class
proxy = ParaProxy(*io_tupple)
# Connecting to AnotherMachine and executing... anything...
end_cli = paramiko.SSHClient()
end_cli.set_missing_host_key_policy(paramiko.AutoAddPolicy())
end_cli.connect(hostname='2.2.2.2', username='user', password='pass', sock=proxy)
end_cli.exec_command('echo THANK GOD FINALLY')
瞧瞧。