29.3 远程访问

生成模型:Claude Opus 4.6 (anthropic/claude-opus-4-6) Token 消耗:输入 ~210k tokens,输出 ~6k tokens(本节)


本地部署的 Gateway 默认绑定在 127.0.0.1(loopback),仅允许同一台机器上的客户端连接。但在实际场景中,用户经常需要从笔记本、手机或远程服务器访问家中或办公室的 Gateway。OpenClaw 提供了四种远程访问方案:Tailscale Serve/FunnelSSH 隧道服务发现(mDNS/Bonjour)广域 DNS-SD。这些方案可以组合使用——例如,用 Tailscale 做安全传输层,同时用 Bonjour 做局域网自动发现。

29.3.1 Tailscale Serve / Funnel

Tailscale 简介

衍生解释:Tailscale 是基于 WireGuard 协议的零配置 VPN。它为每台设备分配一个 100.64.0.0/10 网段的 IPv4 地址和一个 <hostname>.<tailnet>.ts.net 形式的 DNS 名称。设备之间的流量经过端到端加密,无需手动配置防火墙或端口转发。Tailscale Serve 允许将本地端口暴露给同一 tailnet 内的其他设备;Tailscale Funnel 则进一步将端口暴露到公网(需要在管理控制台启用权限)。

三种模式

OpenClaw Gateway 的 Tailscale 集成通过配置项 gateway.tailscale.mode 控制,支持三种模式:

模式
可访问范围
认证方式
典型场景

off

仅本地

token/password

默认,不启用 Tailscale

serve

tailnet 内部

Tailscale 身份 + token

家庭/办公室内多设备

funnel

公网

password(强制)

公开 webhook、外部协作

源码实现

Tailscale 的核心逻辑位于 src/gateway/server-tailscale.ts(59 行)和 src/infra/tailscale.ts(496 行)。Gateway 启动时调用 startGatewayTailscaleExposure()

// src/gateway/server-tailscale.ts(简化)
export async function startGatewayTailscaleExposure(params: {
  tailscaleMode: "off" | "serve" | "funnel";
  resetOnExit?: boolean;
  port: number;
  controlUiBasePath?: string;
}) {
  if (params.tailscaleMode === "off") return null;

  // 根据模式调用不同的 Tailscale CLI 命令
  if (params.tailscaleMode === "serve") {
    await enableTailscaleServe(params.port);    // tailscale serve --bg --yes <port>
  } else {
    await enableTailscaleFunnel(params.port);   // tailscale funnel --bg --yes <port>
  }

  // 获取 tailnet 主机名用于日志
  const host = await getTailnetHostname().catch(() => null);
  // 输出: "serve enabled: https://myhost.tail12345.ts.net/ (WS via wss://...)"

  // 返回清理函数(进程退出时调用)
  if (!params.resetOnExit) return null;
  return async () => {
    if (params.tailscaleMode === "serve") {
      await disableTailscaleServe();    // tailscale serve reset
    } else {
      await disableTailscaleFunnel();   // tailscale funnel reset
    }
  };
}

底层的 enableTailscaleServe() / enableTailscaleFunnel() 实际上是执行系统上的 Tailscale CLI:

Tailscale 二进制定位

findTailscaleBinary() 使用四级回退策略定位 tailscale 可执行文件:

  1. PATH 查找which tailscale

  2. macOS 已知路径/Applications/Tailscale.app/Contents/MacOS/Tailscale

  3. find 搜索:在 /Applications 下搜索 Tailscale.app

  4. locate 数据库:使用 locate Tailscale.app 作为最后手段

找到后结果会被缓存,避免重复搜索。

权限提升与 sudo 回退

某些 Tailscale 操作(如 servefunnel)可能需要 root 权限。OpenClaw 实现了自动 sudo 回退机制:

-n(non-interactive)标志确保 sudo 不会阻塞等待密码输入——如果用户没有配置免密 sudo,则直接失败并抛出原始错误。

Tailscale Whois 认证

当 Gateway 运行在 Serve 模式时,Tailscale 的反向代理会注入身份头:

HTTP 头
内容

tailscale-user-login

用户的 Tailscale 登录名

tailscale-user-name

用户的显示名称

tailscale-user-profile-pic

头像 URL

Gateway 通过 readTailscaleWhoisIdentity() 验证这些头——它调用 tailscale whois --json <ip> 查询连接者的真实 Tailscale 身份,然后与 HTTP 头中的 login 进行交叉比对。验证结果使用 LRU 缓存(成功 TTL 60 秒,失败 TTL 5 秒),避免对每次请求都执行系统调用。

安全约束

运行时配置检查(server-runtime-config.ts)强制执行两条安全规则:

  1. Funnel 要求密码认证funnel 模式下必须设置 gateway.auth.mode = "password",因为 Funnel 暴露到公网,仅靠 token 不够安全。

  2. Serve/Funnel 要求 loopback 绑定:Tailscale 代理通过 127.0.0.1 转发流量,Gateway 不应同时监听外部接口。

29.3.2 SSH 隧道

SSH 隧道是最通用的远程访问方式——不依赖任何第三方服务,只要远程机器开放了 SSH 端口即可。

隧道建立流程

OpenClaw 的 SSH 隧道实现位于 src/infra/ssh-tunnel.ts(214 行),核心函数 startSshPortForward() 完成以下步骤:

  1. 解析目标parseSshTarget() 支持多种格式:user@hostuser@host:porthost:port、甚至带 ssh 前缀。默认端口 22。

  2. 端口分配:优先使用首选端口(通常是 18789),若被占用则分配临时端口。

  3. 启动 SSH 进程:使用 spawn("/usr/bin/ssh", args) 创建隧道。

  4. 等待就绪:轮询 127.0.0.1:本地端口 直到可以建立 TCP 连接。

SSH 参数详解

startSshPortForward() 构造的 SSH 命令包含精心选择的参数:

衍生解释-L localPort:host:remotePort 是 SSH 本地端口转发(Local Port Forwarding)的标准语法。它在本地机器上监听 localPort,将收到的数据通过加密 SSH 通道转发到远程机器上的 host:remotePort。由于 Gateway 只监听 127.0.0.1,这里的 host 也是 127.0.0.1,即远程机器的 loopback。

安全防护

SSH 隧道实现包含两个重要的安全措施:

  1. 主机名注入防御parseSshTarget() 拒绝以 - 开头的主机名,防止恶意输入被解释为 SSH 命令行选项。

  2. -- 参数终止符:在主机名前插入 --,确保即使主机名包含特殊字符也不会被误解析。

生命周期管理

隧道进程的停止函数实现了优雅关闭:先发送 SIGTERM,等待最多 1.5 秒,若进程仍未退出则发送 SIGKILL 强制终止。

配置集成

用户可以在 ~/.openclaw/config.yaml 中配置远程 Gateway 的 SSH 连接参数:

CLI 也支持通过命令行参数 --ssh <target> 直接建立隧道。

29.3.3 服务发现 — mDNS/Bonjour

当 Gateway 和客户端在同一局域网时,用户不应该需要手动输入 IP 地址和端口。OpenClaw 使用 mDNS/Bonjour(也称为 DNS-SD,DNS-based Service Discovery)实现零配置服务发现。

衍生解释:mDNS(Multicast DNS)是一种在局域网内实现 DNS 解析的协议(RFC 6762),无需中心 DNS 服务器。设备通过向 224.0.0.251:5353 发送组播查询来发现其他设备。DNS-SD(DNS-based Service Discovery,RFC 6763)在 mDNS 之上定义了服务注册和发现的标准格式。Apple 的 Bonjour 是 mDNS + DNS-SD 的实现。

广播端——Bonjour Advertiser

Gateway 启动时通过 startGatewayBonjourAdvertiser()src/infra/bonjour.ts)向局域网广播自己的存在。底层使用 @homebridge/ciao 库——这是 Homebridge 项目维护的纯 JavaScript mDNS 实现。

服务注册

注册的 DNS-SD 服务类型为 _openclaw-gw._tcp,TXT 记录包含了客户端连接所需的元数据:

TXT 键
内容
minimal 模式

role

gateway

gatewayPort

端口号

lanHost

hostname.local

displayName

人类可读名称

transport

gateway

gatewayTls

1(如已启用 TLS)

tailnetDns

Tailscale DNS 名称

sshPort

SSH 端口

✗(仅 full 模式)

cliPath

CLI 路径

✗(仅 full 模式)

三种广播模式

mDNS 广播通过 discovery.mdns.mode 配置:

  • minimal(默认):省略 sshPortcliPath,减少信息暴露

  • full:完整广播所有元数据

  • off:完全禁用 mDNS

Watchdog 机制

网络接口变化(如 WiFi 重连、睡眠唤醒)可能导致 mDNS 广播失效。Bonjour Advertiser 内置了一个 60 秒间隔的 watchdog,它检测服务状态是否仍为 announced,若不是则尝试重新广播:

发现端——Beacon Discovery

客户端通过 discoverGatewayBeacons()src/infra/bonjour-discovery.ts,604 行)发现 Gateway。该函数根据平台选择不同的发现工具:

平台
工具
命令

macOS

dns-sd(系统内置)

dns-sd -B _openclaw-gw._tcp local.

Linux

avahi-browse

avahi-browse -rt _openclaw-gw._tcp

发现流程:

  1. 浏览:先列出指定域内所有 _openclaw-gw._tcp 实例

  2. 解析:对每个发现的实例查询其完整信息(主机、端口、TXT 记录)

  3. 解码:处理 DNS-SD 特有的八进制转义(如中文名称)

返回的 GatewayBonjourBeacon 对象包含了客户端连接所需的全部信息,macOS 应用和 iOS 应用的配对流程就是基于此实现的(参见第 28 章)。

29.3.4 广域 DNS-SD(Wide-Area Discovery)

标准 mDNS 只在局域网内有效——组播包不会穿越路由器。OpenClaw 支持广域 DNS-SD(也称 Unicast DNS-SD),通过常规 DNS 记录(非组播)实现跨网络的服务发现。

工作原理

广域 DNS-SD 的核心思想是将 mDNS 记录写入一个真实的 DNS 域:

Zone 文件生成

startGatewayDiscovery()src/gateway/server-discovery-runtime.ts)在启动时检查是否启用了广域发现:

writeWideAreaGatewayZone()src/infra/widearea-dns.ts,200 行)生成标准的 BIND 格式 zone 文件,并使用内容哈希(FNV-1a)实现幂等写入——只有记录实际发生变化时才更新文件和递增 SOA serial。

发现端的广域回退

客户端发现时,如果标准 mDNS 没有在广域域(如 openclaw.internal.)发现实例,会触发 discoverWideAreaViaTailnetDns() 回退:

  1. 通过 tailscale status --json 获取 tailnet 中所有节点的 IPv4 地址

  2. 以 6 路并发向每个 IP 发送 dig PTR 查询

  3. 找到能响应的节点后,继续查询 SRV 和 TXT 记录

  4. 组装成与 mDNS 相同格式的 GatewayBonjourBeacon

这种方式结合了 Tailscale 的设备列表和 DNS-SD 的标准协议,无需搭建中心 DNS 服务器即可实现跨网络发现。

配置示例

对应的环境变量:

环境变量
作用

OPENCLAW_DISABLE_BONJOUR=1

完全禁用 Bonjour

OPENCLAW_MDNS_HOSTNAME

自定义 mDNS 主机名

OPENCLAW_WIDE_AREA_DOMAIN

广域发现域名

OPENCLAW_TAILNET_DNS

手动指定 Tailscale DNS 名称

OPENCLAW_SSH_PORT

mDNS TXT 中的 SSH 端口(full 模式)

OPENCLAW_CLI_PATH

手动指定 CLI 路径


本节小结

  1. Tailscale Serve/Funnel 提供零配置的安全远程访问。Serve 模式限 tailnet 内部,Funnel 暴露到公网(强制密码认证 + loopback 绑定)。底层通过调用 Tailscale CLI 实现,支持 sudo 回退和 Whois 身份验证。

  2. SSH 隧道 是最通用的方案。startSshPortForward() 自动分配本地端口、设置安全参数(心跳、超时、防注入)、轮询等待隧道就绪,并提供优雅关闭机制。

  3. mDNS/Bonjour 实现局域网零配置发现。Gateway 通过 @homebridge/ciao 广播 _openclaw-gw._tcp 服务,客户端通过 dns-sd(macOS)或 avahi-browse(Linux)发现。Watchdog 确保睡眠唤醒后自动恢复。

  4. 广域 DNS-SD 突破局域网限制,通过生成标准 DNS zone 文件 + Tailscale 节点扫描实现跨网络发现,无需搭建中心化 DNS 基础设施。

  5. 这四种方案可灵活组合:Tailscale 做传输层 + Bonjour 做发现层是最常见的搭配。

Last updated