Paramiko:绕过NAT路由器进行端口转发

12

配置

  • 本地: 一个本地机器将创建一个ssh连接并在远程盒子上发出命令。
  • 代理: 具有对LOCAL和REMOTE的ssh访问权限的EC-2实例。
  • 远程: 一个远程机器坐落在NAT路由器后面(LOCAL无法访问,但它将打开到PROXY的连接并允许LOCAL进行隧道连接)。

端口转发步骤(通过命令行)

  1. 创建一个SSH连接从REMOTE到PROXY,将REMOTE机器上22号端口的SSH流量转发到PROXY服务器上的8000号端口。

    # 在REMOTE机器上运行
    ssh -N -R 0.0.0.0:8000:localhost:22 PROXY_USER@PROXY_HOSTNAME

  2. 创建一个SSH隧道从LOCAL到PROXY,并将LOCAL:1234的SSH流量转发到PROXY:8000(然后转发到REMOTE:22)。

    # 在LOCAL机器上运行
    ssh -L 1234:localhost:8000 PROXY_USER@PROXY_HOSTNAME

  3. 创建一个从LOCAL到REMOTE(通过PROXY)的转发SSH连接。

    # 在新的终端窗口中从LOCAL机器上运行
    ssh -p 1234 REMOTE_USER@localhost

    # 现在我已经连接到REMOTE机器并可以运行命令了

Paramiko研究

我看了几篇关于使用 Paramiko 进行端口转发的问题,但它们似乎没有涉及到这个具体情况。

我的问题

我该如何使用 Paramiko 运行上述步骤 2 和 3?我想要运行以下命令:

import paramiko

# Create the tunnel connection
tunnel_cli = paramiko.SSHClient()
tunnel_cli.connect(PROXY_HOSTNAME, PROXY_PORT, PROXY_USER)

# Create the forwarded connection and issue commands from LOCAL on the REMOTE box
fwd_cli = paramiko.SSHClient()
fwd_cli.connect('localhost', LOCAL_PORT, REMOTE_USER)
fwd_cli.exec_command('pwd')
2个回答

27

关于Paramiko在“幕后”做什么的详细解释可以在@bitprohet的博客中找到。

假设以上配置,我已经能够工作的代码看起来像这样:

from paramiko import SSHClient

# Set up the proxy (forwarding server) credentials
proxy_hostname = 'your.proxy.hostname'
proxy_username = 'proxy-username'
proxy_port = 22

# Instantiate a client and connect to the proxy server
proxy_client = SSHClient()
proxy_client.load_host_keys('~/.ssh/known_hosts/')
proxy_client.connect(
    proxy_hostname,
    port=proxy_port,
    username=proxy_username,
    key_filename='/path/to/your/private/key/'
)

# Get the client's transport and open a `direct-tcpip` channel passing
# the destination hostname:port and the local hostname:port
transport = proxy_client.get_transport()
dest_addr = ('0.0.0.0', 8000)
local_addr = ('127.0.0.1', 1234)
channel = transport.open_channel("direct-tcpip", dest_addr, local_addr)

# Create a NEW client and pass this channel to it as the `sock` (along with
# whatever credentials you need to auth into your REMOTE box
remote_client = SSHClient()
remote_client.load_host_keys(hosts_file)
remote_client.connect('localhost', port=1234, username='remote_username', sock=channel)

# `remote_client` should now be able to issue commands to the REMOTE box
remote_client.exec_command('pwd')

确实史诗般的。谢谢!! - slayedbylucifer
我认为代码中存在冗余。如果您指定了“sock”,则“hostname”和“port”将被忽略(毕竟它们已经是通道的一部分)。 - Martin Prikryl

1

您是只需要将SSH命令反弹到PROXY上,还是需要转发其他非SSH端口?

如果您只需要SSH连接到REMOTE主机,则Paramiko支持SSH级别的网关(告诉PROXY sshd打开到REMOTE的连接并代表LOCAL转发SSH流量)和ProxyCommand支持(通过本地命令转发所有SSH流量,可以是任何能够与远程主机通信的东西)。

在我看来,听起来像是您需要前者,因为PROXY已经明确有一个正在运行的sshd。如果您查看Fabric的副本并搜索“网关”,则会找到有关Fabric如何使用Paramiko的网关支持的指针(我现在没有时间自己挖掘具体位置。)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接