我想构建一个类似于 ngrok 的服务,并试图了解 ngrok 的隧道服务子域管理背后的架构。据我所知,ngrok 为每个隧道提供了唯一的子域(如 abcde.ngrok.io),但我对他们如何大规模管理这些子域感到困惑。
具体来说:
- 每个隧道都有单独的 IP 地址吗?如果是这样,那么 ngrok 怎么会有如此多的 IP?他们如何为其 IP 动态添加新的 DNS 记录?
- 如果不同的子域由同一个 IP 地址管理,如果它们都具有相同的 IP 地址,ngrok 如何理解我连接到的是 a.ngrok.io,而不是 b.ngrok.io?我知道,有像 SNI 这样的技术,但如果我建立没有 TLS 加密的 TCP 隧道,它如何工作?
主要问题是:有没有办法在规模较小的项目中实施类似的系统?关键组件是什么?
任何关于实现此类系统的潜在架构或最佳实践的见解都将不胜感激!谢谢!
不,隧道地址在用户之间共享。
首先,通配符子域名。DNS 支持名为 的记录,它将自动覆盖任何未特别定义的
*.example.com
(单级)子域名。.example.com
第二:与 HTTP URL 类似,DNS 记录不需要添加到某个地方,因为没有子域名的中央数据库;相反,该信息仅由 Ngrok 自己的名称服务器提供。因此,就像 HTTP webapp 可以通过代码动态响应各种 URL 一样,可以编写一个动态响应各种子域名的 DNS 服务器。
对于普通的 TCP 隧道,他们实际上并不知道这一点。据我了解,他们的系统只使用 TCP 端口号来区分隧道,因为您实际上永远不会获得整个子域 - 您只会在该 IP 地址上获得一个 TCP 端口。
取决于规模。在非常小的规模下(例如可能只有 0-3 个用户),即使是标准 SSH 服务器也可以提供相同类型的隧道
ssh -R
(Ngrok 甚至为其自定义后端提供相同类型的 SSH 样式界面)。这种服务的基本代码有点像常规的 TCP 代理(rinetd、haproxy、nginx)——打开一个监听套接字,对于每个接收到的连接,建立一个传出连接,并进行 poll() 循环,将数据从一个套接字复制到另一个套接字。
但是,为了使其像 Ngrok 的服务一样工作(整个想法是 Ngrok 无法连接到后端;后端必须连接到 Ngrok),您需要对其进行更改,以便代理在两个不同的侦听器上侦听“客户端”连接和“代理”连接,并以类似的方式将它们配对。(类似于工作方式
socat tcp-l:1234 tcp-l:2345
。)这将仅限于一次一个客户端,因此下一步将是更改“代理-代理”连接,以便它可以多路复用来自多个客户端的数据(类似于 SSH 或 QUIC 如何在 TCP 连接内有几个不同的数据“流”) - 并使“代理”对它们进行解复用。
例如,代理现在接受一个“代理”连接,并且对于每个收到的“客户端”连接,它都会向代理发送一个命令,如“客户端 #5 已连接 - 请打开与后端的新连接”和“通过后端连接 #5 发送此数据块”,等等。(这也非常类似于
ssh -R
工作原理。)其余的只是使代理更加灵活,例如,每当它收到“代理”连接时,让它自动分配一个新的“客户端”监听器,实现 NAT 的保持活动检查等。