65.9K
CodeProject 正在变化。 阅读更多。
Home

Node.js 实现反向隧道/端口转发

starIconstarIconstarIconstarIconstarIcon

5.00/5 (7投票s)

2017年7月8日

CPOL

6分钟阅读

viewsIcon

37581

使用 Node.js 应用进行反向端口转发/隧道连接到没有公网 IP 的 PC

引言

一个应用程序,允许您转发位于没有公网 IP 的网络内的某些机器的特定端口。

背景

我时不时需要通过 RDP、SSH、代理等方式连接到私有网络内的几台机器。可惜,TeamViewer、Hamachi、SSH 隧道、VPN 等工具都被禁用了……

所以我决定构建一个 Node.js 应用来满足我的需求。

Using the Code

本地运行

我将从运行应用程序和进行一些简单的尝试所需的最少操作开始。

先决条件:Node.js (测试过 8 版本),git 客户端

  1. git clone https://github.com/mgrybyk/node-tunnel.git
  2. cd node-tunnel
  3. npm install

现在,让我们创建一个最小化的配置文件。我将提供两个示例:一个用于有SSH的人,另一个用于有浏览器的人。 :)

创建一个 .env 文件(完整的文件名是 ".env",不是扩展名!),内容如下:
注意:".env" 文件应在项目根文件夹中创建,本例中是:node-tunnel

用于SSH(您可以根据需要更改主机/端口到任何其他值)

N_T_AGENT_DATA_HOST=localhost
N_T_AGENT_DATA_PORT=22
N_T_CLIENT_PORT=2222

用于http(您可以根据需要更改主机/端口到任何其他值;由于证书等问题,网站很可能无法正常工作,但这足以作为示例)

N_T_AGENT_DATA_HOST=inplainsite.org
N_T_AGENT_DATA_PORT=80
N_T_CLIENT_PORT=8000

我们已经准备好启动它了!启动三个终端窗口,因为我们需要启动 3 个 node 实例,然后运行:

node server
node agent
node client

如果您选择了SSH方式,请连接到 localhost:2222 -> ssh -p 2222 localhost
如果您选择了http方式,请打开浏览器访问 localhost:8000

是的!这是一个最小化的工作示例。您的所有流量都通过 client->server->agent 并返回。

实际案例,两台 PC

在这个例子中,我们面临这样的情况。您有一台拥有公网 IP 的 PC,另一台没有。目标 - 通过 SSH/RDP/其他方式连接到它。

让我们先在每台机器上克隆仓库并安装模块。

现在,转到远程 PC 并创建一个 .env 文件。在此示例中,我使用了 RDP 端口,您可以随意更改为任何其他端口。

N_T_SERVER_HOST=ip of machine with public ip
N_T_SERVER_PORT=1337
N_T_SERVER_PORTS_FROM=1338
N_T_SERVER_PORTS_TO=1340

N_T_AGENT_DATA_HOST=localhost
N_T_AGENT_DATA_PORT=3389

现在,在此处启动 agent:node agent

切换到本地 PC,并创建一个 .env 文件

N_T_SERVER_HOST=localhost
N_T_SERVER_PORT=1337
N_T_SERVER_PORTS_FROM=1338
N_T_SERVER_PORTS_TO=1340

N_T_CLIENT_PORT=8000

现在,在此处启动 server 和 client

node server
node client

完成后,您可以通过 RPD 客户端连接到 localhost:8000,这将打开到远程 PC 的连接。

实际案例,两台 PC 和一台服务器

这看起来很棒,但如果您的本地 PC 没有公网 IP 怎么办?您必须通过一台拥有公网 IP 的 PC 来转发所有流量。如果您没有这样的 PC,您可以在 AWS 上创建一个免费的容器。

我们开始吧!照常,在每台机器上安装 node.js,克隆仓库并安装模块。

转到拥有公网 IP 的 PC(服务器),并在那里创建一个 .env 文件

N_T_SERVER_HOST=localhost
N_T_SERVER_PORT=1337
N_T_SERVER_PORTS_FROM=1338
N_T_SERVER_PORTS_TO=1340

很好,运行服务器:node server

在远程 PC 上,创建一个 .env 文件。在此示例中,我使用了 SSH 端口,但您可以将主机/端口更改为任何您想要的值。此外,我将为 agent 指定一个名称(应与 client 的名称匹配)。

N_T_SERVER_HOST=ip of machine with public ip
N_T_SERVER_PORT=1337

N_T_AGENT_DATA_HOST=localhost
N_T_AGENT_DATA_PORT=22
N_T_AGENT_NAME=test-ssh

准备启动 agent:node agent

切换到客户端 PC 并创建一个 .env 文件。客户端名称应与 agent 名称匹配。

N_T_SERVER_HOST=ip of machine with public ip
N_T_SERVER_PORT=1337

N_T_CLIENT_PORT=8000
N_T_CLIENT_NAME=test-ssh

最后,启动 client:node client

太棒了。现在我们可以打开到 localhost:8000 的 SSH 连接,这将打开到远程 PC 的 SSH 连接。

我们已经通过服务器机器创建了隧道,正如我们之前所做的那样。所有数据都如下传输:

ssh 客户端 -> client -> server -> agent -> ssh 服务器

然后返回:ssh 服务器 -> agent -> server -> client -> ssh 客户端

agent 和 client 之间没有直接连接。

更多 Agents 和 Clients

多个 agents 和 clients 可以通过一个 server 运行。例如:
您可以运行一个 agent 来处理 SSH,另一个来处理 RDP。请注意,每个 agent 都应该有一个名称 (N_T_AGENT_NAME)。

每个 agent 可以与多个 client 一起工作,因此您可以运行一个 client 在您的机器上,其他 client 可以连接到特定的 agent。不要忘记通过提供名称 (N_T_CLIENT_NAME) 来指定 client 应该使用哪个 agent。

多个 .env 文件

如果您需要运行多个 agents/clients/servers,您可以创建多个 .env 文件,例如:

.env.rdp
.env.ssh
.env.test

有了这些.env.*文件,您可以通过将 .env 文件名作为参数来启动 server/client/agent。

node server .env.rdp
node agent .env.ssh
node client .env.test

注意:".env" 文件应在项目根文件夹中创建,本例中是:node-tunnel。

它是如何工作的?

核心部分在此:Net,它允许创建基于流的 TCP 服务器/客户端和流 管道
为了将数据从一个 socket 转发到另一个 socket 并返回,我简单地像这样管道它们:

agentSocket.pipe(clientSocket)
clientSocket.pipe(agentSocket)

让我们来看这个例子:

在 SSH 的例子中,发生以下情况:

  1. SSH 客户端连接到 client(它正在监听某个端口)
  2. Client 将所有数据转发给 server
  3. Server 知道需要将数据转发给特定的 agent
  4. Agent 打开到 SSH 服务器的连接,并将数据从 server 转发给它

    来自 SSH 服务器的响应会返回到 agent、server、client,最终到达 SSH 客户端。

让我尝试解释应用程序的每个部分的作用。

服务器

  • 默认情况下,server 监听 clients 和 agents 的连接。
  • 一旦新的 agent 连接,server 就会为它创建一个专用服务器。
  • 一旦新的 client 连接,server 会通知它有一个专用的 agent 服务器及其端口。
  • 为了开始数据转发,client 和 agent 的名称必须匹配,不允许存在同名的 agent。
  • 可以存在多个 agents,并且可以存在每个 agent 的多个 client(可以存在大量同名的 client)。

专用的 agent 服务器行为如下:

  1. 当 client 服务器有新连接时,会打开一个到 agent 专用服务器的新连接。
  2. 一旦 client 连接,就会向 agent 发送通知,以便它可以连接到服务器。
    与此同时:client socket 被存储起来,等待 agent socket。
  3. 一旦 agent 连接,专用服务器会将 agent 和 client 相互管道,并移除 client 和 agent sockets 的数据监听器。
  4. 专用服务器向 client 发送通知,表明管道已创建,我们已准备就绪。
  5. 完成!

客户端

Client 创建一个服务器,并在 .env 文件中提供的端口上监听传入的连接。

新连接时,client 开始将数据转发给 server(详见 server 部分)。

Agent

Agent 等待关于 client 连接的通知。当发生这种情况时,agent 会连接到 .env 文件中指定的 host:port 并将其转发给 server。

下一步?

加密!目前数据未加密,对于 SSH、RDP 来说这不是问题,但对于明文协议来说是个问题。
目前只有服务消息被加密。

当然,修复一些缺陷,清理代码,提高稳定性。

如果您有任何想法,请随时分享。 :)

总结

感谢阅读!

我希望这或多或少有趣,或多或少易于理解,甚至可能有用。 :)

关注点

处理管道时,一个有用的模块可能是 though2
使用此模块,您可以将数据通过您的 worker 进行处理,并执行诸如:日志记录、错误处理或更改数据,例如将某个关键字(ebay)更改为另一个关键字(amazon),或者将所有 0 更改为 1。 :)

历史

  • 2017 年 7 月 8 日:初始版本,修复了一些拼写错误
© . All rights reserved.