何时使用
- 你在 身份感知代理(Pomerium、Caddy + OAuth、nginx + oauth2-proxy、Traefik + forward auth)后运行 OpenClaw。
- 你的代理处理所有身份验证,并通过标头传递用户身份。
- 你处于 Kubernetes 或容器环境中,代理是通往 Gateway 网关的唯一路径。
- 你遇到 WebSocket
1008 unauthorized错误,因为浏览器无法在 WS 载荷中传递令牌。
何时不要使用
- 你的代理不验证用户身份(只是 TLS 终止器或负载均衡器)。
- 存在任何绕过代理通往 Gateway 网关的路径(防火墙漏洞、内部网络访问)。
- 你不确定代理是否正确剥离/覆盖转发标头。
- 你只需要个人单用户访问(请改为考虑 Tailscale Serve + loopback)。
工作原理
Gateway verifies trusted source
OpenClaw 检查请求是否来自 受信任代理 IP(
gateway.trustedProxies),且不是 Gateway 网关自身的回环或本地接口地址。配置
配置参考
要信任的代理 IP 地址(或 CIDR)数组。来自其他 IP 的请求会被拒绝。
必须是
"trusted-proxy"。包含已验证用户身份的标头名称。
请求受信任时必须存在的额外标头。
用户身份允许列表。为空表示允许所有已验证用户。
对同主机回环反向代理的选择性启用支持。
Control UI 配对行为
当gateway.auth.mode = "trusted-proxy" 处于活动状态且请求通过受信任代理检查时,Control UI WebSocket 会话可以在没有设备配对身份的情况下连接。
范围影响:
- 无设备的 Control UI WebSocket 会话可以连接,但默认不接收任何操作员权限范围。OpenClaw 会将请求的权限范围列表清空为
[],因此未绑定到已批准配对设备/令牌的会话无法自行声明权限。 - 如果 WebSocket 连接成功后方法因
missing scope失败,请使用 HTTPS,以便浏览器可以生成设备身份并完成配对。参见 Control UI 非安全 HTTP。 - 仅限紧急破窗场景:
gateway.controlUi.dangerouslyDisableDeviceAuth=true即使没有设备身份也会保留请求的权限范围。这是严重的安全降级;请尽快恢复。参见 Control UI 非安全 HTTP。
x-openclaw-scopes,OpenClaw 会将会话权限范围限制为请求权限范围与声明权限范围的交集。此标头不会授予权限范围;它只会缩小会话可持有的范围。
影响:
- 在此模式下,配对不再是 Control UI 访问的主要关口。
- 你的反向代理身份验证策略和
allowUsers会成为实际访问控制。 - 仅将 Gateway 网关入口锁定到受信任代理 IP(
gateway.trustedProxies+ 防火墙)。
gateway.controlUi.dangerouslyDisableDeviceAuth 不会向任意 client.mode: "backend" 或 CLI 形态的客户端授予权限范围。自定义自动化应使用设备身份/配对、保留的直接本地 client.id: "gateway-client" 后端辅助路径,或在 HTTP 请求/响应表面更适合时使用 admin HTTP RPC 插件。
操作员权限范围标头
受信任代理身份验证是一种 携带身份 的 HTTP 模式,因此调用方可以选择在 HTTP API 请求上用x-openclaw-scopes 声明操作员权限范围。
注意:WebSocket 权限范围由 Gateway 网关协议握手和设备身份绑定决定。在 Control UI WebSocket 升级请求上,x-openclaw-scopes 只是协商会话权限范围的上限,而不是授予。参见 Control UI 配对行为。
示例:
x-openclaw-scopes: operator.readx-openclaw-scopes: operator.read,operator.writex-openclaw-scopes: operator.admin,operator.write
- 当该标头存在时,OpenClaw 会遵循声明的权限范围集合。
- 当该标头存在但为空时,请求声明 没有 操作员权限范围。
- 当该标头不存在时,普通的携带身份 HTTP API 会回退到标准操作员默认权限范围集合(
operator.admin、operator.read、operator.write、operator.approvals、operator.pairing、operator.talk.secrets)。 - Gateway 网关身份验证 插件 HTTP 路由 默认更窄:当
x-openclaw-scopes不存在时,其运行时权限范围仅回退到operator.write。 - 浏览器来源的 HTTP 请求即使在受信任代理身份验证成功后,仍必须通过
gateway.controlUi.allowedOrigins(或有意启用的 Host 标头回退模式)。
x-openclaw-scopes。
TLS 终止和 HSTS
使用一个 TLS 终止点,并在那里应用 HSTS。- Proxy TLS termination (recommended)
- Gateway TLS termination
当你的反向代理为
https://control.example.com 处理 HTTPS 时,请在该域名的代理上设置 Strict-Transport-Security。- 适合面向互联网的部署。
- 将证书和 HTTP 加固策略保持在一个位置。
- OpenClaw 可以在代理后继续使用回环 HTTP。
推出指南
- 验证流量时,先从较短的最大有效期开始(例如
max-age=300)。 - 只有在信心很高后,才增加到长期值(例如
max-age=31536000)。 - 仅当每个子域都已准备好使用 HTTPS 时,才添加
includeSubDomains。 - 仅当你有意满足完整域名集合的 preload 要求时,才使用 preload。
- 仅回环的本地开发无法从 HSTS 获益。
代理设置示例
Pomerium
Pomerium
Pomerium 在 Pomerium 配置片段:
x-pomerium-claim-email(或其他声明标头)中传递身份,并在 x-pomerium-jwt-assertion 中传递 JWT。Caddy with OAuth
Caddy with OAuth
带有 Caddyfile 片段:
caddy-security 插件的 Caddy 可以验证用户身份并传递身份标头。nginx + oauth2-proxy
nginx + oauth2-proxy
oauth2-proxy 会对用户进行身份验证,并在 nginx 配置片段:
x-auth-request-email 中传递身份信息。Traefik 与转发认证
Traefik 与转发认证
混合令牌配置
如果同时配置了共享令牌(gateway.auth.token 或 OPENCLAW_GATEWAY_TOKEN),Gateway 网关启动会拒绝 trusted-proxy 认证。这两者互斥,因为共享令牌会让同一主机调用方通过一条完全不同于代理已验证身份的路径完成认证,而该模式本来就是为了强制使用代理验证的身份。
如果启动失败并出现类似 gateway auth mode is trusted-proxy, but a shared token is also configured 的错误:
- 使用 trusted-proxy 模式时移除共享令牌,或
- 如果你想使用基于令牌的认证,请将
gateway.auth.mode切换为"token"。
gateway.auth.password / OPENCLAW_GATEWAY_PASSWORD 进行认证。在 trusted-proxy 模式下,令牌回退仍然有意不受支持。
安全检查清单
启用 trusted-proxy 认证之前,请确认:- 代理是唯一路径:Gateway 网关端口已通过防火墙限制为仅你的代理可访问。
- trustedProxies 最小化:只包含你的实际代理 IP,而不是整个子网。
- local loopback 代理来源是有意配置的:除非为同一主机代理显式启用
gateway.auth.trustedProxy.allowLoopback,否则 trusted-proxy 认证会对 local loopback 来源请求失败关闭。 - 代理会剥离标头:你的代理会覆盖(而不是追加)来自客户端的
x-forwarded-*标头。 - TLS 终止:你的代理处理 TLS;用户通过 HTTPS 连接。
- allowedOrigins 显式配置:非 local loopback 的 Control UI 使用显式的
gateway.controlUi.allowedOrigins。 - allowUsers 已设置(推荐):限制为已知用户,而不是允许任何已认证用户。
- 没有混合令牌配置:不要同时设置
gateway.auth.token和gateway.auth.mode: "trusted-proxy"。 - 本地密码回退是私有的:如果你为内部直接调用方配置
gateway.auth.password,请保持 Gateway 网关端口受防火墙保护,避免非代理远程客户端直接访问它。
安全审计
openclaw security audit 会以 critical 严重级别标记 trusted-proxy 认证。这是有意设计的;它是在提醒你已将安全性委托给代理设置。
审计会检查:
- 基础
gateway.trusted_proxy_auth警告/critical 提醒。 - 缺少
trustedProxies配置。 - 缺少
userHeader配置。 - 空的
allowUsers(允许任何已认证用户)。 - 为同一主机代理来源启用了
allowLoopback。
gateway.controlUi.allowedOrigins,以及 Host 标头来源回退。
故障排查
trusted_proxy_untrusted_source
trusted_proxy_untrusted_source
请求不是来自
gateway.trustedProxies 中的 IP。请检查:- 代理 IP 是否正确?(Docker 容器 IP 可能会变化。)
- 你的代理前面是否有负载均衡器?
- 使用
docker inspect或kubectl get pods -o wide查找实际 IP。
trusted_proxy_loopback_source
trusted_proxy_loopback_source
OpenClaw 拒绝了一个 local loopback 来源的 trusted-proxy 请求。检查:
- 代理是否从
127.0.0.1/::1连接? - 你是否正在尝试通过同一主机 local loopback 反向代理使用 trusted-proxy 认证?
- 对于不经过代理的内部同一主机客户端,优先使用令牌/密码认证,或
- 通过非 local loopback 的可信代理地址路由,并将该 IP 保留在
gateway.trustedProxies中,或 - 对于有意配置的同一主机反向代理,设置
gateway.auth.trustedProxy.allowLoopback = true,将 local loopback 地址保留在gateway.trustedProxies中,并确保代理会剥离或覆盖身份标头。
trusted_proxy_local_interface_source / trusted_proxy_local_interface_check_failed
trusted_proxy_local_interface_source / trusted_proxy_local_interface_check_failed
请求的源 IP 匹配了 Gateway 网关主机自身的某个非 local loopback 网络接口地址(而不是代理),这是为了防止在 tailnet 或 Docker 桥接网络中伪造同一主机流量。
..._check_failed 表示接口发现本身出错,因此 OpenClaw 会失败关闭。检查:- Gateway 网关主机本身是否有进程绕过代理直接发送身份标头?
- 代理是否与 Gateway 网关运行在同一网络命名空间中,并且其 IP 也显示为本地接口?
allowLoopback。trusted_proxy_user_missing
trusted_proxy_user_missing
用户标头为空或缺失。请检查:
- 你的代理是否已配置为传递身份标头?
- 标头名称是否正确?(不区分大小写,但拼写很重要)
- 用户是否已在代理处实际完成身份验证?
trusted_proxy_missing_header_*
trusted_proxy_missing_header_*
缺少必需标头。请检查:
- 针对这些特定标头的代理配置。
- 标头是否在链路中的某处被剥离。
trusted_proxy_user_not_allowed
trusted_proxy_user_not_allowed
用户已通过身份验证,但不在
allowUsers 中。请添加该用户,或移除允许列表。trusted_proxy_no_proxies_configured / trusted_proxy_config_missing
trusted_proxy_no_proxies_configured / trusted_proxy_config_missing
gateway.auth.mode 是 "trusted-proxy",但 gateway.trustedProxies 为空,或 gateway.auth.trustedProxy 本身缺失。在两者都设置之前,所有请求都会被拒绝。trusted_proxy_origin_not_allowed
trusted_proxy_origin_not_allowed
Trusted-proxy 认证成功,但浏览器
Origin 标头未通过 Control UI 来源检查。检查:gateway.controlUi.allowedOrigins包含精确的浏览器来源。- 除非你有意允许全部来源,否则不要依赖通配符来源。
- 如果你有意使用 Host 标头回退模式,请确认已刻意设置
gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback=true。
连接成功,但方法报告缺少权限范围
连接成功,但方法报告缺少权限范围
WebSocket 已连接,但
chat.history、sessions.list 或
models.list 失败,并显示 missing scope: operator.read。常见原因:- 无设备的 Control UI 会话:trusted-proxy 认证可以在没有设备身份的情况下允许 WebSocket 连接,但 OpenClaw 会按设计清除无设备会话上的权限范围。
- 自定义后端客户端:
gateway.controlUi.dangerouslyDisableDeviceAuth仅作用于 Control UI 范围,不会向任意后端或 CLI 形态的 WebSocket 客户端授予权限范围。 - 过窄的
x-openclaw-scopes:如果你的代理在 Control UI WebSocket 升级请求中注入此标头,会话权限范围会被限制为该集合。空标头值会导致没有任何权限范围。
- 对于 Control UI,使用 HTTPS,以便浏览器可以生成设备身份并完成配对。
- 对于自定义自动化,使用设备身份/配对、保留的直接本地
gateway-client后端辅助路径,或 admin HTTP RPC。 - 仅将
gateway.controlUi.dangerouslyDisableDeviceAuth: true用作临时的 Control UI 应急通道。
WebSocket 仍然失败
WebSocket 仍然失败
确保你的代理:
- 支持 WebSocket 升级(
Upgrade: websocket、Connection: upgrade)。 - 在 WebSocket 升级请求中传递身份标头(而不仅是 HTTP)。
- 没有为 WebSocket 连接设置单独的认证路径。