生成模型 :Claude Opus 4.6 (anthropic/claude-opus-4-6) Token 消耗 :输入 ~210k tokens,输出 ~6k tokens(本节)
本地部署的 Gateway 默认绑定在 127.0.0.1(loopback),仅允许同一台机器上的客户端连接。但在实际场景中,用户经常需要从笔记本、手机或远程服务器访问家中或办公室的 Gateway。OpenClaw 提供了四种远程访问方案:Tailscale Serve/Funnel 、SSH 隧道 、服务发现(mDNS/Bonjour) 和广域 DNS-SD 。这些方案可以组合使用——例如,用 Tailscale 做安全传输层,同时用 Bonjour 做局域网自动发现。
29.3.1 Tailscale Serve / Funnel
衍生解释 :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 控制,支持三种模式:
Tailscale 的核心逻辑位于 src/gateway/server-tailscale.ts(59 行)和 src/infra/tailscale.ts(496 行)。Gateway 启动时调用 startGatewayTailscaleExposure():
Copy // 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 可执行文件:
macOS 已知路径 :/Applications/Tailscale.app/Contents/MacOS/Tailscale
find 搜索 :在 /Applications 下搜索 Tailscale.app
locate 数据库 :使用 locate Tailscale.app 作为最后手段
找到后结果会被缓存,避免重复搜索。
某些 Tailscale 操作(如 serve、funnel)可能需要 root 权限。OpenClaw 实现了自动 sudo 回退机制:
-n(non-interactive)标志确保 sudo 不会阻塞等待密码输入——如果用户没有配置免密 sudo,则直接失败并抛出原始错误。
Tailscale Whois 认证
当 Gateway 运行在 Serve 模式时,Tailscale 的反向代理会注入身份头:
tailscale-user-profile-pic
Gateway 通过 readTailscaleWhoisIdentity() 验证这些头——它调用 tailscale whois --json <ip> 查询连接者的真实 Tailscale 身份,然后与 HTTP 头中的 login 进行交叉比对。验证结果使用 LRU 缓存(成功 TTL 60 秒,失败 TTL 5 秒),避免对每次请求都执行系统调用。
运行时配置检查(server-runtime-config.ts)强制执行两条安全规则:
Funnel 要求密码认证 :funnel 模式下必须设置 gateway.auth.mode = "password",因为 Funnel 暴露到公网,仅靠 token 不够安全。
Serve/Funnel 要求 loopback 绑定 :Tailscale 代理通过 127.0.0.1 转发流量,Gateway 不应同时监听外部接口。
SSH 隧道是最通用的远程访问方式——不依赖任何第三方服务,只要远程机器开放了 SSH 端口即可。
OpenClaw 的 SSH 隧道实现位于 src/infra/ssh-tunnel.ts(214 行),核心函数 startSshPortForward() 完成以下步骤:
解析目标 :parseSshTarget() 支持多种格式:user@host、user@host:port、host:port、甚至带 ssh 前缀。默认端口 22。
端口分配 :优先使用首选端口(通常是 18789),若被占用则分配临时端口。
启动 SSH 进程 :使用 spawn("/usr/bin/ssh", args) 创建隧道。
等待就绪 :轮询 127.0.0.1:本地端口 直到可以建立 TCP 连接。
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 隧道实现包含两个重要的安全措施:
主机名注入防御 :parseSshTarget() 拒绝以 - 开头的主机名,防止恶意输入被解释为 SSH 命令行选项。
-- 参数终止符 :在主机名前插入 --,确保即使主机名包含特殊字符也不会被误解析。
隧道进程的停止函数实现了优雅关闭:先发送 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 记录包含了客户端连接所需的元数据:
mDNS 广播通过 discovery.mdns.mode 配置:
minimal (默认):省略 sshPort 和 cliPath,减少信息暴露
网络接口变化(如 WiFi 重连、睡眠唤醒)可能导致 mDNS 广播失效。Bonjour Advertiser 内置了一个 60 秒间隔的 watchdog,它检测服务状态是否仍为 announced,若不是则尝试重新广播:
发现端——Beacon Discovery
客户端通过 discoverGatewayBeacons()(src/infra/bonjour-discovery.ts,604 行)发现 Gateway。该函数根据平台选择不同的发现工具:
dns-sd -B _openclaw-gw._tcp local.
avahi-browse -rt _openclaw-gw._tcp
发现流程:
浏览 :先列出指定域内所有 _openclaw-gw._tcp 实例
解析 :对每个发现的实例查询其完整信息(主机、端口、TXT 记录)
解码 :处理 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 域:
startGatewayDiscovery()(src/gateway/server-discovery-runtime.ts)在启动时检查是否启用了广域发现:
writeWideAreaGatewayZone()(src/infra/widearea-dns.ts,200 行)生成标准的 BIND 格式 zone 文件,并使用内容哈希 (FNV-1a)实现幂等写入——只有记录实际发生变化时才更新文件和递增 SOA serial。
客户端发现时,如果标准 mDNS 没有在广域域(如 openclaw.internal.)发现实例,会触发 discoverWideAreaViaTailnetDns() 回退:
通过 tailscale status --json 获取 tailnet 中所有节点的 IPv4 地址
以 6 路并发向每个 IP 发送 dig PTR 查询
找到能响应的节点后,继续查询 SRV 和 TXT 记录
组装成与 mDNS 相同格式的 GatewayBonjourBeacon
这种方式结合了 Tailscale 的设备列表和 DNS-SD 的标准协议,无需搭建中心 DNS 服务器即可实现跨网络发现。
对应的环境变量:
OPENCLAW_DISABLE_BONJOUR=1
OPENCLAW_WIDE_AREA_DOMAIN
mDNS TXT 中的 SSH 端口(full 模式)
Tailscale Serve/Funnel 提供零配置的安全远程访问。Serve 模式限 tailnet 内部,Funnel 暴露到公网(强制密码认证 + loopback 绑定)。底层通过调用 Tailscale CLI 实现,支持 sudo 回退和 Whois 身份验证。
SSH 隧道 是最通用的方案。startSshPortForward() 自动分配本地端口、设置安全参数(心跳、超时、防注入)、轮询等待隧道就绪,并提供优雅关闭机制。
mDNS/Bonjour 实现局域网零配置发现。Gateway 通过 @homebridge/ciao 广播 _openclaw-gw._tcp 服务,客户端通过 dns-sd(macOS)或 avahi-browse(Linux)发现。Watchdog 确保睡眠唤醒后自动恢复。
广域 DNS-SD 突破局域网限制,通过生成标准 DNS zone 文件 + Tailscale 节点扫描实现跨网络发现,无需搭建中心化 DNS 基础设施。
这四种方案可灵活组合:Tailscale 做传输层 + Bonjour 做发现层是最常见的搭配。