简答 (TL;DR)
远程(即您计算机上的本地隧道软件)初始化与中继的连接(即 localtunnel.me)充当多路复用代理,当客户端(即 Web 浏览器)连接时,中继通过附加特殊的带有网络信息的标头。
Browser <--\ /--> Device
Browser <---- M-PROXY Service ----> Device
Browser <--/ \--> Device
视频演示
如果你更喜欢视频预览,我刚刚在 UtahJS Conf 2018 上做了一个演讲,其中我也谈到了所有其他潜在的解决方案:SSH Socksv5 代理(你提到的)、VPN、UPnP、DHT、继电器等:
访问能力:访问您的设备,分享您的资料
幻灯片:http ://telebit.cloud/utahjs2018
localtunnel、ngrok 和 Telebit 如何工作(长答案)
我是Telebit的作者,它提供的服务具有与 ngrok、localtunnel 和 libp2p 所提供的功能相似的服务(以及远程/客户端和中继/服务器自己运行的开源代码)。
虽然我不知道 localtunnel 是如何实现的确切内部结构,但我可以解释一下它通常是如何完成的(以及我们是如何做到的),并且很可能与他们的实现方式几乎相同。
你好奇的魔法发生在两个地方:远程套接字和多路复用器。
远程客户端如何访问我的本地主机上的服务器?
1.远程插座
这很简单。当您运行“远程”(telebit、ngrok 和 localtunnel 在这方面的工作方式几乎相同)时,实际上是您的计算机发起了请求。
因此,假设中继(在您的情况下为 localtunnel 代理)使用端口 7777接收来自“远程”(如您的计算机)的流量,并且您的套接字号(计算机上随机选择的源地址)为 1234:
Devices: [Your Computer] (tcp 1234:7777) [Proxy Server]
Software: [Remote] -----------------------> [Relay]
(auth & request 5678)
但是,连接到您的客户端(例如浏览器、netcat 或其他用户代理)实际上也会向中继发起请求。
Devices: [Proxy Service] (tcp 5678) [Client Computer]
Software: [Relay] <------------------------ [netcat]
如果您使用的是 tcp 端口,那么中继服务会保留内部映射,就像 NAT
Internal Relay "Routing Table"
Rule:
Socket remote[src:1234] --- Captures ------> ALL_INCOMING[dst:5678]
Condition:
Incoming client[dst:5678] --- MATCHES -------> ALL_INCOMING[dst:5678]
Therefore:
Incoming client[dst:5678] --- Forwards To ---> remote[src:1234]
两个连接都是“传入”连接,但“南端”的远程连接被授权接收来自另一个传入源的流量(没有某种形式的授权会话,任何人都可以声称使用该端口或地址并劫持您的流量)。
[Client Computer] (tcp 5678) [Proxy Service] (tcp 1234) [Your Computer]
[netcat] --------------> <--[Relay]--> <------------ [remote]
2. 多路复用器
您可能已经注意到上面的描述中有一个严重的缺陷。如果您只是按原样路由流量,您的计算机(远程)一次只能处理一个连接。如果另一个客户端(浏览器、netcat 等)跳到连接上,您的计算机将无法分辨哪些数据包来自哪里。
Browser <--\ /--> Device
Browser <---- M-PROXY Service ----> Device
Browser <--/ \--> Device
本质上,中继(即本地隧道代理)和远程(即您的计算机)所做的就是在客户端要接收的所有数据前面放置一个标头。它需要与 HAProxy 的代理协议非常相似,但也适用于非本地流量。它可能看起来像这样:
<src-address>,<src-port>,<sni>,<dst-port>,<protocol-guess>,<datalen>
例如
172.2.3.4,1234,example.com,443,https,1024
该信息可以在路由的每个数据包之前准确发送或附加到每个数据包。
同样,当远程响应中继时,它必须包含该信息,以便中继知道数据包正在响应哪个客户端。
有关详细信息,请参阅https://www.npmjs.com/package/proxy-packer
旁注/咆哮:端口与 TLS SNI
我最初的解释是使用 tcp 端口,因为它很容易理解。但是,localtunnel、ngrok 和 telebit 都可以使用 tls服务器名称指示符(SNI) 而不是依赖端口号。
[Client Computer] (https 443) [Proxy Service] (wss 443) [Your Computer]
[netcat+openssl] --------------------> <--[Relay]--> <------------ [remote]
(or web browser) (sni:xyz.somerelay.com) (sni:somerelay.com)
中间人与 p2p
你仍然可以通过几种不同的方式来解决这个问题(这就是我想为 Telebit 提供一个无耻的插件的地方,因为如果你喜欢去中心化和点对点,这就是我们大放异彩的地方!)
如果您只使用 tls sni 进行路由(这是我上次检查时 localtunnel 和 ngrok 的默认工作方式),所有流量都会在中继处解密。
Anther 方式需要 ACME/Let's Encrypt 集成(即Greenlock.js),以便流量保持加密,端到端,将 tls 流量路由到客户端而不解密它。这种方法在所有实际用途中都充当对等通道(中继充当 Internet 网络上的另一个不透明“开关”,不知道流量的内容)。
此外,如果远程(例如,通过安全 WebSockets)和客户端都使用 https,那么客户端看起来就像任何其他类型的 https 请求,并且不会受到配置不当的防火墙或其他恶劣/不利的网络条件的阻碍.
现在,基于 libp2p 构建的解决方案也为您提供了虚拟化的对等连接,但它更加间接,并且需要通过不受信任的各方进行路由。我不喜欢这种方式,因为它通常速度较慢,而且我认为它的风险更大。我坚信网络联盟将在长期内战胜匿名化(如 libp2p)。(对于我们的用例,我们需要可以联合的东西——由独立的受信任方运行——这就是为什么我们以我们的方式构建解决方案的原因)