T3 Connect¶
T3 Connect 使用 Clerk 进行云端身份认证。中继管理环境链接、访问环境所需的凭据以及受管隧道的分配。引导完成后,客户端通过环境的隧道主机名发送应用流量;中继 Worker 不会代理这些 HTTP 或 WebSocket 会话。唯一的例外是自动化 webhook:中继将 /v1/hooks/:endpointKey/:hookId/:token 转发到环境的隧道,以便发送方获得稳定的 URL。endpointKey 是受管隧道名称的 16 位十六进制哈希后缀,而非环境 ID。中继不会在其追踪数据中保留请求体和令牌,并将令牌和签名验证交给环境处理(转发器)。
默认情况下,转发器不存储任何内容。环境可以选择让中继在其离线期间暂存请求(在其链接上设置 hold_webhooks_while_offline)。只有在此情况下,中继才会将原始请求(包括路径中的 hook 令牌)存储到使用 SQLite 存储的 Durable Object 中。每个受管链接对应一个对象,以其端点键为键:每个将该环境链接起来的账户都拥有自己独立的暂存请求、顺序和上限。该对象在其 alarm 触发时通过隧道推送暂存的请求,每个 hook 内按最旧优先的顺序处理,并在环境持续离线时退避重试。若某个 hook 答复忙碌,则在该轮运行中跳过,从而使同一链接上的其他 hook 可以超越它。当隧道重新连接时,环境会要求中继立即向其每个链接投递。请求在以下情况下被删除:环境应答后、24 小时后、链接被取消链接时,或环境关闭暂存功能时——后者会清除它证明过的每个链接。两种清除都是尽力而为:取消链接时,清除失败会记录日志并继续;关闭暂存功能时,会在第一个无法清除的链接处停止,因此这些请求只能等待 24 小时过期。中继仍然从不检查令牌;投递走的是同一条环境路由。每次转发都携带 x-t3-relay-delivery-id,因此,一个在超时前已到达环境、随后又被投递一次的请求只会执行一次(inbox 对象)。
选择 Durable Object 而非 Postgres 或 Queues,是因为暂存的请求是一次写入、一次读取的请求体,最大 1 MiB,并且需要按链接维持顺序、上限和重试时机。Queues 的消息上限为 128 KB,而且无法在其环境离线时扣住某个链接的请求。
Clerk、部署和原生认证设置见 Connect 设置运行手册。
中继是可信代理¶
经过认证的云用户仍需要一条活跃的环境链接。中继会要求该环境铸造一个与客户端 DPoP 密钥绑定的一次性引导凭据。客户端直接与环境交换该凭据,以获得环境会话。中继永远不会收到该会话令牌,而且仅持有引导凭据本身,若无客户端私钥,也无法兑换它。
双方都会对此次交换进行认证。环境只接受有界且带重放防护的中继证明,这些证明须针对其自身身份、已链接用户和所请求的操作。经签名的环境响应将结果绑定到请求 nonce;铸币响应还会将凭据绑定到客户端证明密钥。中继在返回凭据之前会验证这些绑定。这可以防止隧道背后的另一个进程冒充已链接环境。这些检查交汇于环境链接服务与中继连接器。
中继持有铸币请求的签名权。DPoP 保护的是诚实交换免受凭据重用;它并不能让被攻破的中继签名密钥变得无害。修改协议时,请务必明确这一信任假设。
受管隧道只暴露经过验证的 loopback(回环)HTTP 源。链接证明检查会拒绝转发的 authority 头,而中继是从自身受管分配中解析端点,而非调用方提供的 URL。健康检查和铸币请求不得跟随重定向。这些限制确保端点发现不会演变成任意的中继出口,也不会暴露环境主机上的另一个服务。
链接的寿命超过连接器进程¶
CLI 授权、期望的暴露设置以及正在运行的连接器具有不同的生命周期。链接可以在服务器停止时记录意图。启动时会对该意图进行协调。CLI 登出会移除已存储的云凭据并禁用暴露,但不会卸载环境的后台服务。
受管分配属于某个用户/环境对。供给过程会为外部隧道和 DNS 资源设置检查点,以便重试时协调不完整的工作。CLI 管理的链接在正常关闭时释放其隧道,以避免为空闲资源付费,同时保留主机名预留以供下次启动使用。它还会保留分配记录,使环境保持"离线"状态,而不是变成"未授权"状态。
两种情况下,隧道必须在关闭后继续保留。通过客户端安装的链接没有启动供给路径,依赖其存储的连接器令牌。更新交接会立即启动替代服务器,而替换隧道会为每次更新增加路由传播延迟。这两种例外情况归入关闭处理。
释放和取消链接都会在删除外部资源之前认领分配代次。延迟的清理不得删除已被并发重启或重新链接复用的隧道。取消链接会先提交授权撤销,再执行外部拆除,因为一旦数据库出现故障,活跃链接必须仍然可用。拆除失败时会保留足够的状态以便重试。参见受管端点生命周期。
空闲隧道会被回收并恢复¶
Cloudflare 按隧道计费,无论是否有连接器附着;因此,一台已链接环境的笔记本电脑在休眠后会留下一个付费隧道。relay 每五分钟执行一次的维护任务可以回收这些隧道。RELAY_TUNNEL_CLEANUP_MODE 选择 off、dry-run 或 enabled,默认是 off。该模式在部署时读取,因此更改它意味着要重新部署 relay,而不是改一个变量就能生效。
候选对象是同一阶段的隧道:Cloudflare 报告其已断开至少五分钟,或者从未连接且已存在至少一小时。对从未连接的隧道给予更长的宽限期,是为了覆盖仍在进行中的配对。
清理只删除那些主机已注册恢复能力的隧道。未注册恢复能力的分配属于无法替换已删除隧道的主机,因此会保留不动。没有记录隧道 ID、或隧道 ID 不同的分配会被跳过,因为某个 provision 可能拥有它们。完全没有分配行的隧道会被计为 skippedOrphan,并且永远不会被删除:没有行可加锁,因此按名称收养它的重新链接可能与删除操作产生竞态。这些需要手动清理。
每次清扫都有边界:最多十个列表请求、100 次删除、两分钟截止时间,并在遇到 Cloudflare 速率限制时提前停止。每次清扫都会沿着候选列表再前进一个预算,因此持续失败的一批删除不会让排在后面的隧道永远得不到处理。参见 reaper。
主机在启动时通过环境密钥签发的短期签名,将其隧道 ID 和回环 origin 发送出去,从而注册恢复能力。只有在本地主机或端口发生变化时,注册才会触及 Cloudflare;升级后的首次注册中,每个现有分配会各触发一次,因为存储的 origin 为空。首次注册会加入抖动,这样自动更新浪潮不会同时涌向 relay。主机会在连接器配置中存储一个已确认 origin 的标记;后续启动时,只有当该标记与当前配置和端口匹配时,才会在注册之前启动连接器。如果注册在十分钟内无法到达 relay,主机仍然会启动其存储的配置,并在后台持续注册,直到能够协调 origin 为止。如果连接器退出,或 cloudflared 报告隧道被反复拒绝,主机最多每两分钟向 relay 请求一次替换。relay 会在同一分配下进行 provision,因此主机名和 DNS 记录得以保留,客户端也保持其绑定。对分配的每次修改都会递增其 generation;删除操作会以它声明时的 generation 锁定该行,因此在清扫中途重新连接的主机能够赢得竞争。
付费 T3 Chat 套餐提高隧道上限¶
T3 Chat 使用 WorkOS 而不是 Clerk,因此两个账户是通过用户同意关联的,而不是通过共享身份。relay 与 T3 Chat 共享一个密钥(relay 这边是 T3CHAT_LINK_SECRET,T3 Chat 那边是 T3CODE_LINK_SECRET)。relay 签署一个短期令牌,其中标明 Clerk 用户和电子邮件;用户在登录 T3 Chat 后确认该令牌,T3 Chat 存储这一关联。T3 Chat 拥有该关联和套餐,因此 relay 不保留副本。只有当账户为新环境达到其正常上限时,relay 才会询问 T3 Chat;任何失败都会保持正常上限。已经占用计数槽位的环境重启时无需询问,因此降级或 T3 Chat 故障只会阻止新环境。手动覆盖行会替换新环境的默认值,而 T3 Chat 套餐不会提高该值(限制)。
relay 客户端跟随服务端的固定版本¶
主机按照 relayClient.ts 中的 CLOUDFLARED_VERSION 固定运行 cloudflared;在那里更新该版本(版本号、URL 和校验和)就是完整的发布步骤。已链接主机用新的固定版本启动服务器时,会先让连接器继续运行在最新一个比固定版本更旧的受管发行版上,在后台安装被固定的版本,然后在该版本上为同一隧道启动第二个连接器。旧连接器只会在新连接器注册之后才停止,因此这次切换绝不会让主机脱离 relay。如果新连接器启动失败,旧连接器会继续服务,安装会在之后重试。没有 relay 客户端的主机会先安装一个。位于 PATH 上的 cloudflared,或显式指定的 T3CODE_CLOUDFLARED_PATH,属于用户自己的选择,永远不会被替换,因为链接操作在下载前会先询问。
一旦被固定的发行版注册了连接,比该固定版本更旧的受管发行版会被删除,但其中最新的一个除外。那一个以及任何更新的发行版可能属于共享同一个 T3 home 的另一台服务器,例如失败更新后的回滚;没有它,回滚就无法再次运行。
版本来自运行 cloudflared version 的结果,而不是文件夹名称。在 --no-autoupdate 之前的连接器会就地替换自身,因此受管文件夹可能保存着另一个发行版。在固定版本安装期间,这样的二进制文件仍会作为回退运行。每个二进制文件都需要 CLOUDFLARED_MIN_VERSION,即能够接受连接器启动时所带全部标志的最早发行版。每当新增标志时,都应提高该值。
OAuth 陷阱¶
交互式客户端和无头 CLI 使用同一个 Clerk 应用,但使用不同的凭据。relay 同时接受会话模板 JWT 和 CLI OAuth 令牌;如果要求 CLI 使用 JWT 模板,就会拒绝有效登录。CLI 是使用 PKCE 的公共 OAuth 客户端,不存储客户端密钥。
回环 CLI 授权从托管的 /connect 页面开始,这样在进入 Clerk 的 authorize 端点之前,登录已经完成。将未登录的浏览器直接发送到该端点,会在登录重定向期间丢失 authorize 参数。共享流程 会为回环回调保留 PKCE 和 state。
SSH 和无头会话使用 Clerk 的 OAuth 设备授权许可(device authorization grant),因为浏览器通常无法访问远程机器上的监听器。CLI 直接轮询 Clerk 的令牌端点,同时用户在 Clerk 托管的设备页面上批准一个短代码;托管应用不参与其中,也没有重定向 URI 或 PKCE。该许可必须在 CLI OAuth 应用中启用,否则设备端点会在显示任何提示之前返回错误。
本页原文 Markdown:在 AtomGit 查看·内容源自开源项目 t3/t3code