返回资讯中心
外部精选
软件工程
#开发者实时流

Retriever 通过 Tailscale 隧道让云端浏览器代理使用用户家庭 IP

来源标题:Run cloud agents from your home IP through a Tailscale tunnel

Retriever 推出 Tailscale Tunnels,让云端浏览器代理的流量经由用户本地设备(如 Mac mini 或树莓派)出口,使网站看到的是用户家庭 IP 而非数据中心 IP。该功能于 10 月上线,重点在于隔离不可信浏览器与家庭内网,防止恶意页面访问路由器管理页、NAS 等设备。官方称路由边界已实施并测试,但仍有一个缺口尚未解决。

Hacker News 最新arjunchint2026/11/7 · 今天30 分钟阅读

以下正文由 AI 翻译自 Hacker News 最新(原文版权归原站所有),可能存在偏差,重要内容请以原文为准。

Retriever 在云端运行浏览器 agent。每次运行都会在一个容器里拿到一个全新的 Chrome,运行结束后我们就把它丢弃。这套做法一直可行,直到任务需要用到你的某个账号。这时从网站的角度看,你刚刚从一个外州的数据中心登录了。

十月我们推出了 Tailscale Tunnels:云端运行可以把流量通过你自己网络里的一台电脑(比如 Mac mini 或树莓派)发出去,这样网站看到的就是你的 IP。把数据包送到那里是最简单的部分,因为 Tailscale 的用户态模式本身就支持。真正的工作在于另一个问题:

如何把一个不可信的浏览器接入你的家庭 IP,同时又不让它进入你的家庭网络?

我们把运行中的浏览器视为敌对对象。它会加载任务引导它访问的任何页面,而一个读取这些页面的模型会决定下一步做什么。如果它的流量从你的 Mac mini 出去,那么一个恶意页面首先会尝试的就是你路由器的管理页面、你的 NAS,以及你 tailnet 上的其他所有设备。这篇文章讲的是路由如何工作、我们在哪里强制执行这条边界、我们测试了什么,以及我们尚未填补的缺口。

本文讨论的问题,用一段短视频说明。

为什么 IP 重要,以及它的边界在哪里

持有账号的网站会根据许多信号给每个会话打分,网络就是其中之一。一个在家中连接上创建的会话,一小时后却从云服务商的地址段出现,看起来就像是被盗的 cookie。住宅代理能给你一个住宅地址,但仍然不是你账号所认识的那个地址。

通过你自己的连接进行路由只能修正这一个信号,其他信号一概不管。云端浏览器仍然与你的笔记本电脑是不同的设备,网站关于自动化的规则依然适用。我们在接近结尾处会回到这一点,因为这是该方法最大的局限。

Chrome 把每个请求(包括 localhost)都发送到 127.0.0.1:3128 上的一个出口网关,这是每次云端运行本来就在用的一个小型 Node 代理,用于我们的托管代理。网关判断某个目的地是否被允许,然后把 CONNECT 转发给该运行的上游。对于 Tailscale 路由,这个上游是为本次运行以用户态模式启动的 tailscaled。它已加入你的 tailnet,选定了你的出口节点,并在一个随机的 loopback 端口上暴露 HTTP 代理。在它与你的出口节点之间,流量走 WireGuard,NAT 穿透成功时是点对点直连,不成功时则通过 Tailscale DERP 中继。

一个容器一次运行一个 Chrome 和一个浏览器 lease,但同一时刻它还可以服务其他工作,比如分离运行、抓取和预热。所以隧道绑定的是 lease,而不是容器。一个槽位最多持有一个 tailscaled,以该运行的请求 ID 和拥有该路由的用户为键。绑定另一个请求会先停掉旧节点,绑定与释放是串行化的。在 lease 绑定之前,该路由的上游是一个占位符:

每次运行都会得到自己的一个原版 tailscaled:

用户态网络意味着没有 TUN 设备,也不改动容器的路由表。从容器进入你 tailnet 的唯一通道就是那一个 loopback 端口,所以不会有其他进程意外地把流量送进你的网络。每个节点都有自己的 0700 目录、控制 socket 和端口,并把状态保存在内存中。--no-logs-no-support 会关闭 tailscaled 向 Tailscale 上传日志。

节点以其主机名(rtrvr-run-<hash>)、标签和 --accept-routes=false 加入,所以你其他设备通告的子网路由永远不会被安装。密钥放在一个 0600 文件中,并在 finally 中删除。然后我们选择出口节点,并轮询 tailscale status --json,直到它报告同一个 peer 在线:

保存的出口节点先按 node ID 匹配,再按 Tailscale IP,再按主机名。两台同名机器视为不匹配:运行不会去猜该从你的哪台电脑出口。最后,节点执行 tailscale logout,这会立即从你的 tailnet 中移除一个临时节点,然后它被杀死,其目录被删除。

OAuth 客户端。你创建一个带有 Auth Keys (write) 作用域和 tag:rtrvr 的 Tailscale OAuth 客户端,我们加密存储它的 secret。每次运行时,我们用该 secret 换取一个 token,并通过 POST /api/v2/tailnet/-/keys 精确铸造一个 key:

tailscaled 只会看到那个一次性、五分钟有效的 key,永远看不到客户端 secret。每次运行都是自己的节点,所以 OAuth 路由可以并行运行。

使用 Tailscale 登录。大多数用户不会去创建 OAuth 客户端并编辑策略文件,所以我们加了一个无需 key 的模式。我们用一个状态文件启动 tailscaled,不带 key 运行 tailscale up,并向你展示来自 tailscale status --json 的授权 URL。一旦你批准,该节点就成为你 tailnet 中一个名为 rtrvr-cloud-<hash> 的普通设备。我们存储它的状态文件(其中持有该设备的密钥),用 AES-256-GCM 加密,附加认证数据中写明用户和路由,因此复制到另一个用户或路由上的 blob 无法解密。每次运行都会把该状态恢复到全新的 tailscaled 中,运行 tailscale up --reset,在结束时保存任何续期后的密钥,然后停止而不登出,因为登出会移除你的设备。

那是一个设备,而两个使用相同节点密钥的 tailscaled 进程会争抢它。所以登录路由有一个 lease,在 tailscaled 启动之前通过 Firestore 事务获取。除非续期,它会在 10 分钟后失效,持有者每 3 分钟续期一次。如果续期停滞,看门狗会在 lease 可能失效前 30 秒停掉隧道。每次获取都会得到一个 fencing token,状态回写时会检查它,因此丢失了 lease 的持有者无法覆盖更新的密钥。lease 只在 tailscaled 退出后才释放,所以两个守护进程永远不会重叠。

(原文较长,此处译出前 6000 字,完整内容请点「打开原文」。)

正文由 探How 从来源站点 RSS 同步,内容未经改写;遇到排版缺失或需要图片、视频时请以原文为准。

打开原文
去社区讨论 →