如何在Paramiko服务器中实现端口转发?

2024-03-28 16:33:13 发布

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

当您以ssh user@host -L <local port>:<remote host>:<remote port>的身份运行SSH,然后尝试通过本地端口连接时,会发生“directtcpip”请求(通常称为端口转发)

我正在尝试在自定义SSH服务器上实现direct tcpip,Paramiko在ServerInterface类中提供了check_channel_direct_tcpip_request,以检查是否应允许“direct tcpip”请求,其实现方式如下:

class Server(paramiko.ServerInterface):
    # ...
    def check_channel_direct_tcpip_request(self, chanid, origin, destination):
        return paramiko.OPEN_SUCCEEDED

但是,当我使用前面提到的SSH命令并通过本地端口连接时,什么也不会发生,可能是因为我需要自己实现连接处理

在阅读文档时,还发现只有在返回OPEN_SUCCEDED后才打开通道

在为请求返回OPEN_SUCCEEDED之后,如何处理直接tcpip请求


Tags: 端口hostparamikoremoteportrequestcheckchannel
1条回答
网友
1楼 · 发布于 2024-03-28 16:33:13

您确实需要设置自己的连接处理程序。这是一个冗长的回答,解释了我采取的步骤——如果您的服务器代码已经运行,您将不需要其中的一些步骤。整个正在运行的服务器示例在这里:https://controlc.com/25439153

我使用这里的Paramiko示例服务器代码https://github.com/paramiko/paramiko/blob/master/demos/demo_server.py作为基础,并在此基础上植入了一些套接字代码。这没有任何错误处理、线程相关细节或任何其他“适当”的内容,但它允许您使用端口转发器

这也有很多您不需要的东西,因为我不想开始整理虚拟示例代码。对此表示歉意

首先,我们需要转发器工具。这将创建一个线程来运行“隧道”转发器。这也回答了你的问题,你从哪里得到你的频道。您可以accept()从传输中删除它,但需要在转发器线程中执行此操作。正如您在OP中所述,它还没有出现在check_channel_direct_tcpip_request()函数中,但它最终将对线程可用

def tunnel(sock, chan, chunk_size=1024):
    while True:
        r, w, x = select.select([sock, chan], [], [])

        if sock in r:
            data = sock.recv(chunk_size)
            if len(data) == 0:
                break
            chan.send(data)

        if chan in r:
            data = chan.recv(chunk_size)
            if len(data) == 0:
                break
            sock.send(data)

    chan.close()
    sock.close()


class ForwardClient(threading.Thread):
    daemon = True
    # chanid = 0

    def __init__(self, address, transport, chanid):
        threading.Thread.__init__(self)

        self.socket = socket.create_connection(address)
        self.transport = transport
        self.chanid = chanid

    def run(self):
        while True:
            chan = self.transport.accept(10)
            if chan == None:
                continue

            print("Got new channel (id: %i).", chan.get_id())

            if chan.get_id() == self.chanid:
                break

        peer = self.socket.getpeername()
        try:
            tunnel(self.socket, chan)
        except:
            pass

回到示例服务器代码。与示例代码中不同,您的服务器类需要将transport作为参数:

class Server(paramiko.ServerInterface):
    # 'data' is the output of base64.b64encode(key)
    # (using the "user_rsa_key" files)
    data = (
    b"AAAAB3NzaC1yc2EAAAABIwAAAIEAyO4it3fHlmGZWJaGrfeHOVY7RWO3P9M7hp"
    b"fAu7jJ2d7eothvfeuoRFtJwhUmZDluRdFyhFY/hFAh76PJKGAusIqIQKlkJxMC"
    b"KDqIexkgHAfID/6mqvmnSJf0b5W8v5h2pI/stOSwTQ+pxVhwJ9ctYDhRSlF0iT"
    b"UWT10hcuO4Ks8="
    )
    good_pub_key = paramiko.RSAKey(data=decodebytes(data))

    def __init__(self, transport):
        self.transport = transport
        self.event = threading.Event()

然后您将覆盖相关方法并在那里创建转发器:

def check_channel_direct_tcpip_request(self, chanid, origin, destination):
    print(chanid, origin, destination)
    f = ForwardClient(destination, self.transport, chanid)
    f.start()

    return paramiko.OPEN_SUCCEEDED

您需要将传输参数添加到服务器类的创建中:

t.add_server_key(host_key)
server = Server(t)

此示例服务器要求您在名为test_RSA.key的目录中拥有一个RSA私钥。在那里创建任何RSA密钥,您不需要它,但我没有费心从代码中删除它的使用

然后,您可以运行服务器(在端口2200上运行)并发出

ssh -p 2200 -L 2300:www.google.com:80  robey@localhost

(密码为foo)

现在当你尝试

telnet localhost 2300

在那里输入一些东西,你会得到谷歌的回复

相关问题 更多 >