SSH 没有 Host 头
Source: Hacker News
我们在 SSH 上遇到了一个挑战。每台虚拟机都有一个标准的 URL,既可用于 HTTPS 也可用于 SSH,例如 undefined-behavior.exe.xyz。正如你可以在浏览器中输入域名(TLS 和身份验证会自动处理),你也可以运行:
ssh undefined-behavior.exe.xyz
来获取该虚拟机的 shell。
如果为每台机器分配独立的 IP 地址,这个实现非常直接,但 exe.dev 为你提供的是一个平价订阅下的多台虚拟机。
我们无法为每台机器分配一个 IPv4 地址,因为这会把订阅费用抬得很高。我们也不能只使用 IPv6,因为那样会导致部分互联网用户无法通过网页访问虚拟机。因此我们必须在虚拟机之间共享 IPv4 地址。
对于 Web,这个问题早已解决。许多站点可以共享同一个 IP 地址。浏览器会在 HTTP 请求中通过 Host 头部发送它访问的域名。exe.dev 的代理根据该头部将请求转发到相应的虚拟机。
而 SSH 并没有类似的 Host 头部。如果我们在虚拟机之间复用 IPv4 地址,就没有办法把 SSH 连接路由到正确的虚拟机。
我们的解决方案:SSH IP 共享
我们不是为所有虚拟机使用同一个 IP,而是准备了一组公共 IPv4 地址。每台虚拟机都会被分配一个 相对于其所有者唯一的地址。
因此,你会看到类似下面的 A 记录:
$ dig undefined-behavior.exe.xyz
; > DiG 9.10.6 > undefined-behavior.exe.xyz
...
;; ANSWER SECTION:
undefined-behavior.exe.xyz. 230 IN CNAME s003.exe.xyz.
s003.exe.xyz. 230 IN A 16.145.102.7
相对于其所有者 意味着虽然 s003 所代表的 IP 被许多虚拟机使用,但在同一个用户下只会对应一台虚拟机。
这就是我们路由 SSH 连接所需的全部额外信息。当 SSH 连接时,它会提供公钥并通过特定的 IP 地址进入。公钥告诉我们用户,而 {user, IP} 元组唯一标识了他们要连接的虚拟机。示意图如下:
构建能够实现此功能的代理需要跨系统的通信:
- 创建虚拟机时必须根据所属用户(或在不久的将来,所属团队)仔细分配 IP。
- 我们的 SSH 代理必须能够判断请求到达的本地 IP——在裸金属环境中这很容易,但在公共云环境中,公共 IP 被 NAT 到私有 VPC 地址时就更为困难。
所有这些都需要定制的管理软件,因此我们无法将其推荐为想要在单一 IP 上复用 VM SSH 访问的通用方案。不过,统一且可预测的域名行为对我们非常重要,所以我们为 exe.dev 实现了这一方案。