背景 链接到标题

之前写过一篇 OpenClaw web 工具原理和代理的配置,介绍了在 docker-compose 中设置 HTTP_PROXY/HTTPS_PROXY 环境变量让 web_fetch、web_search 走代理的方法。按那篇文章配置后,一台部署节点工作正常,另一台同样配置却不行——web_fetch 始终无法访问 Google。

现象 链接到标题

两台机器,OpenClaw 2026.6.5,docker-compose 中设置了相同的代理环境变量:

environment:
  HTTP_PROXY: http://proxy-server:7890
  HTTPS_PROXY: http://proxy-server:7890
  NO_PROXY: localhost,127.0.0.1,192.168.0.0/24,10.0.0.0/8,::1

运行时调用 web_fetch("https://www.google.com")节点 A 正常返回,节点 B 失败

排查过程 链接到标题

1. 容器内 curl 测试 链接到标题

两台机器的容器内手动测试代理连通性,结果一致:

# 直连 Google
curl -s --connect-timeout 5 https://www.google.com
# → 200 OK

# 走代理访问 Google
curl -s --connect-timeout 10 -x http://proxy-server:7890 https://www.google.com
# → 200 OK

curl 能走代理,说明代理服务器本身没有问题。

2. Node.js fetch 测试 链接到标题

上篇文章 提到,web_fetch 底层用的是 Node.js 内置的 globalThis.fetch,它会读取 HTTP_PROXY 环境变量。但在两台容器内用 Node.js 测试:

// 设置 EnvHttpProxyAgent 为全局 dispatcher
const { EnvHttpProxyAgent, setGlobalDispatcher } = require("undici");
const agent = new EnvHttpProxyAgent({
  httpProxy: "http://proxy-server:7890",
  httpsProxy: "http://proxy-server:7890"
});
setGlobalDispatcher(agent);

// 测试 fetch
await fetch("https://www.google.com", { signal: AbortSignal.timeout(10000) });
// → 两台机器都 200 OK

直接测试也能通。问题不在代理连接本身。

3. 发现 SSRF 拦截日志 链接到标题

检查节点 B 的 OpenClaw 日志发现关键信息:

[security] blocked URL fetch (url-fetch) targetOrigin=https://www.google.com
reason=Blocked: resolves to private/internal/special-use IP address

OpenClaw 内置了 SSRF(Server-Side Request Forgery)防护层,web_fetch 调用时会先解析目标域名的 IP,检查是否属于内网/特殊用途地址。如果解析到疑似内网的 IP,直接拦截。

4. DNS 污染 链接到标题

检查容器内的 DNS 解析结果:

www.google.com → 69.171.235.22, 2001::1

返回的不是 Google 的真实 IP(69.171.235.22 实际属于 Meta),这是国内典型的 DNS 污染现象。OpenClaw 的 SSRF 防护在严格模式下会对所有解析出的 IP 逐一检查,被污染的解析结果触发了拦截。

5. 配置对比 链接到标题

对比两台机器的 OpenClaw 配置文件 openclaw.json节点 A 多了一项关键配置

{
  "tools": {
    "web": {
      "fetch": {
        "useTrustedEnvProxy": true
      }
    }
  }
}

节点 B 完全没有 tools 配置段。

根因分析 链接到标题

OpenClaw 2026.6.5 的 fetch-guard(SSRF 防护层)根据配置走不同的代码路径:

flowchart TD A["web_fetch 调用"] --> B{"useTrustedEnvProxy?"} B -->|"true"| C["TRUSTED_ENV_PROXY 模式"] B -->|"false/未设置"| D["STRICT 模式"] C --> E["只检查 hostname allowlist"] C --> F["创建 EnvHttpProxyAgent
让代理处理 DNS"] C --> G["请求 → 代理 → Shadowsocks
→ 远端 DNS 解析 → Google"] D --> H["本地 DNS 解析"] D --> I["逐 IP 检查
是否有私有/特殊地址"] I --> J["被污染的 IP
→ 拦截"] style A fill:#e3f2fd,stroke:#1565c0 style G fill:#c8e6c9,stroke:#2e7d32 style J fill:#ffcdd2,stroke:#c62828
  • useTrustedEnvProxy: true:SSRF 防护跳过本地 DNS 解析,信任代理服务器处理 DNS 和外网访问。代理(如 Shadowsocks)在远端完成 DNS 解析,拿到的是 Google 的真实 IP,自然不会被拦截。
  • 未设置此选项:走严格模式,本地 DNS 解析 → 被污染 → SSRF 拦截。

这解释了「同一份 docker-compose 代理配置,一台能通一台不能」的原因:代理配置本身没问题,差别在于 OpenClaw 的应用层配置。

修复 链接到标题

在节点 B 的 ~/.openclaw/openclaw.json 中添加:

{
  "tools": {
    "web": {
      "fetch": {
        "useTrustedEnvProxy": true
      }
    }
  }
}

重启 gateway 后生效。修改验证通过 DNS 污染仍然存在,但 web_fetch 能正常访问 Google。

总结 链接到标题

  • OpenClaw 2026.6.5 的 web_fetch 走 SSRF 防护层,严格模式下会做本地 DNS 解析 + IP 检查
  • 在国内网络环境下 DNS 污染导致被拦截,需要开启 useTrustedEnvProxy: true 让代理处理 DNS
  • 配置生效后即使用户本地 DNS 返回异常 IP,也不影响 web_fetch 正常访问外网
  • SSRF 防护是为防止攻击者通过 web_fetch 扫描内网,生产环境建议按需使用此选项