繁体   English   中英

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

[英]How can I implement port forwarding in a Paramiko server?

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

我正在尝试在自定义 SSH 服务器上实现 direct-tcpip, check_channel_direct_tcpip_requestServerInterface class 中提供了 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 请求?

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

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

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

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

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

回到示例服务器代码。 您的服务器 class 需要将传输作为参数,这与示例代码不同:

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM