在需要远程连接到私有网络中的多台机器时,可能会遇到一些障碍,比如TeamViewer、Hamachi、SSH隧道、VPN等工具被封锁。为了解决这个问题,决定构建一个Node.js应用程序来满足需求。
首先,会介绍运行应用程序所需的最少操作步骤,并对其进行一些探索。
需要Node.js(测试版本为8),以及git客户端。
git clone https://github.com/mgrybyk/node-tunnel.git
cd node-tunnel
npm install
接下来,需要创建一个最小的配置文件。将提供两个示例:一个用于有SSH的用户,另一个用于使用浏览器的用户。
注意:".env"文件应该在项目根目录下创建,即node-tunnel目录下。
N_T_AGENT_DATA_HOST=localhost
N_T_AGENT_DATA_PORT=22
N_T_CLIENT_PORT=2222
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
连接到localhost:2222:
ssh -p 2222 localhost
打开浏览器并访问localhost:8000。
就是这样!这是一个最小工作示例。所有的流量都通过客户端->服务器->代理并返回。
在这个例子中,有两台PC,一台有公网IP,另一台没有。目标是使用SSH/RDP等方式连接到它。
现在,在每台机器上克隆仓库并安装模块。
在这个例子中,使用的是RDP端口,可以根据需要更改为其他端口。
N_T_SERVER_HOST=有公网IP的机器的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
node agent
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
node server
node client
一旦完成,可以使用RDP客户端连接到localhost:8000,这将打开到远程PC的连接。
看起来不错,但如果本地PC没有公网IP怎么办?需要通过有公网IP的PC转发所有流量。如果没有这样的PC,可以在AWS上创建一个免费的容器。
在每台机器上安装Node.js,克隆仓库并安装模块。
N_T_SERVER_HOST=localhost
N_T_SERVER_PORT=1337
N_T_SERVER_PORTS_FROM=1338
N_T_SERVER_PORTS_TO=1340
node server
在这个例子中,使用的是SSH端口,但可以更改为其他端口。同时,会给代理起一个名字(这应该与客户端的名字匹配)。
N_T_SERVER_HOST=有公网IP的机器的IP
N_T_SERVER_PORT=1337
N_T_AGENT_DATA_HOST=localhost
N_T_AGENT_DATA_PORT=22
N_T_AGENT_NAME=test-ssh
node agent
客户端的名字应该与代理的名字匹配。
N_T_SERVER_HOST=有公网IP的机器的IP
N_T_SERVER_PORT=1337
N_T_CLIENT_PORT=8000
N_T_CLIENT_NAME=test-ssh
node client
现在,可以打开SSH连接到localhost:8000,这将打开到远程PC的SSH连接。
通过服务器机器创建了一个隧道,就像之前做的那样。所有的数据都按照以下方式传输:
SSH客户端 -> 客户端 -> 服务器 -> 代理 -> SSH服务器
然后返回SSH服务器 -> 代理 -> 服务器 -> 客户端 -> SSH客户端
代理和客户端之间没有直接连接。
一个服务器可以通过多个代理和客户端。例如:
可以运行一个代理来处理SSH,另一个来处理RDP。请注意,每个代理都应该有一个名字(N_T_AGENT_NAME)。
每个代理可以与多个客户端一起工作,所以可以在机器上运行客户端,其他人可以连接到特定的代理。不要忘记指定客户端应该使用哪个代理,通过提供名字(N_T_CLIENT_NAME)。
如果需要运行多个代理/客户端/服务器,可以创建多个.env文件,例如:
.env.rdp
.env.ssh
.env.test
有了这样的.env.*文件,可以通过传递.env文件名作为参数来启动服务器/客户端/代理:
node server .env.rdp
node agent .env.ssh
node client .env.test
注意:".env"文件应该在项目根目录下创建,即node-tunnel目录下。
核心内容在这里:
Net允许创建基于流的TCP服务器/客户端和流管道。
为了将数据从一个套接字转发到另一个套接字并返回,简单地将它们像这样管道:
JavaScript agentSocket.pipe(clientSocket) clientSocket.pipe(agentSocket)
让看看这个例子:
在SSH的例子中,发生了以下事情:
SSH客户端连接到客户端(在某个端口上监听)
客户端将所有数据转发到服务器
服务器知道它需要将数据转发到特定的代理
代理打开到SSH服务器的连接,并将数据从服务器转发到它
SSH服务器的响应返回到代理,服务器,客户端,最后到达SSH客户端。
让试着解释应用程序的每个部分的作用。
默认情况下,服务器监听客户端和代理连接。
一旦新的代理连接 - 服务器为其创建一个专用服务器。
一旦新的客户端连接 - 服务器通知它有一个专用服务器用于代理及其端口。
客户端和代理的名字应该匹配才能开始数据转发,不允许有同名的代理。
可能有多个代理,每个代理可能有多个客户端(可能有多个同名的客户端)。
代理的专用服务器的行为是这样的:
在客户端服务器的新连接上,打开到代理专用服务器的新连接。
一旦客户端连接 - 向代理发送通知,以便它可以打开到服务器的连接。
与此同时:客户端套接字现在存储并等待代理套接字。
一旦代理连接 - 专用服务器将代理连接到客户端并返回,然后删除客户端和代理套接字的数据侦听器。
专用服务器向客户端发送通知,说管道已创建,准备好了。
客户端创建服务器并在.env文件中提供的端口上监听传入连接。
在新连接上,客户端开始将数据转发到服务器(见服务器部分的详细信息)。
代理等待有关客户端连接的通知。当发生这种情况时 - 代理连接到.env文件中指定的host:port,并将数据转发到服务器。
加密!目前数据没有加密,对于SSH,RDP来说这不是问题,但对于纯文本协议来说是个问题。
目前,只有服务消息是加密的。
当然,修复一些缺陷,清理代码,提高稳定性。
如果有任何想法 - 请随时分享。
感谢阅读!
希望这在某种程度上是有趣的,可以理解的,甚至可能是有用的。
在处理管道时有用的模块可能是though2。