Skip to content

将 OpenClaw Gateway 的认证委托给身份感知反向代理(Pomerium、Caddy、nginx+OAuth)时使用 trusted-proxy 模式。关键操作:配置 gateway.trustedProxies 为代理实际 IP,设置 userHeader 接收用户身份,确保代理是访问 Gateway 的唯一路径。注意默认拒绝 loopback 源请求,如需同主机代理需显式启用 allowLoopback。启动后可用 openclaw security audit 审计配置安全性。

OpenClaw Trusted Proxy 认证配置:解决 WebSocket 1008 错误

安全敏感功能。 此模式将认证完全委托给你的反向代理。配置错误可能导致 Gateway 遭到未授权访问。启用前请仔细阅读。

什么场景用 Trusted Proxy

在以下情况使用 trusted-proxy 认证模式:

  • 你在身份感知代理(Pomerium、Caddy + OAuth、nginx + oauth2-proxy、Traefik + forward auth)后面运行 OpenClaw
  • 代理处理所有认证,并通过 Header 传递用户身份
  • 你处于 Kubernetes 或容器环境,代理是访问 Gateway 的唯一路径
  • 你遇到 WebSocket 1008 unauthorized 错误——浏览器无法在 WS 载荷中传递 token

什么场景不用 Trusted Proxy

  • 你的代理不认证用户(只做 TLS 终止或负载均衡)
  • 存在绕过代理直接访问 Gateway 的路径(防火墙漏洞、内网直连)
  • 你不确定代理是否正确剥离/覆盖转发的 Header
  • 仅需个人单用户访问——建议使用 Tailscale Serve + 回环地址,配置更简单

工作原理

  1. 反向代理认证用户(OAuth、OIDC、SAML 等)
  2. 代理添加包含已认证用户身份的 Header(例如 x-forwarded-user: nick@example.com
  3. OpenClaw 验证请求来自受信任的代理 IP(在 gateway.trustedProxies 中配置)
  4. OpenClaw 从配置的 Header 中提取用户身份
  5. 一切检查通过,请求获得授权

控制台配对行为变化

gateway.auth.mode = "trusted-proxy" 且请求通过受信任代理检查后,Control UI WebSocket 会话可以不依赖设备配对身份直接连接。此时配对不再是主要访问关卡,反向代理的认证策略和 allowUsers 成为实际控制。务必通过 gateway.trustedProxies + 防火墙将 Gateway 入口锁定为仅受信任代理 IP。

怎么配置 Trusted Proxy 认证

json5
{
  gateway: {
    // Trusted-proxy 认证默认拒绝 loopback 源请求
    bind: "lan",

    // 关键:只添加你的代理 IP
    trustedProxies: ["10.0.0.1", "172.17.0.1"],

    auth: {
      mode: "trusted-proxy",
      trustedProxy: {
        // 必填:包含已认证用户身份的 Header 名称
        userHeader: "x-forwarded-user",

        // 可选:必须存在的额外 Header(用于代理验证)
        requiredHeaders: ["x-forwarded-proto", "x-forwarded-host"],

        // 可选:用户身份允许列表,空表示允许所有已认证用户
        allowUsers: ["nick@example.com", "admin@company.org"],

        // 可选:是否允许同主机 loopback 反向代理(默认 false)
        allowLoopback: false,
      },
    },
  },
}

重要运行时规则

  • Trusted-proxy 认证会拒绝来自 loopback 源的请求(127.0.0.1::1、loopback CIDR)。同主机 loopback 反向代理不满足条件,除非显式设置 allowLoopback: true 并包含 loopback 地址到 trustedProxies
  • 启用 allowLoopback 后,Gateway 信任本地进程发出的身份 Header,因此必须保证 Gateway 仍被防火墙屏蔽远程访问,且本地代理会覆盖客户端提供的身份 Header。
  • 不经反向代理的内部 Gateway 客户端应使用 gateway.auth.password / OPENCLAW_GATEWAY_PASSWORD,而不是 trusted-proxy 身份 Header。
  • 非 loopback 的 Control UI 部署仍需显式配置 gateway.controlUi.allowedOrigins
  • 注意:若请求到达 loopback 但携带了 ForwardedX-Forwarded-*X-Real-IP 等 Header,这些证据会阻止本地直连的密码回退和设备身份门控。此时若 allowLoopback 已启用,trusted-proxy 仍可接受请求,但 requiredHeadersallowUsers 继续生效。

配置参数参考

字段必填说明
gateway.trustedProxies受信任代理 IP 数组,来自其他 IP 的请求被拒绝
gateway.auth.mode必须为 "trusted-proxy"
gateway.auth.trustedProxy.userHeader包含已认证用户身份的 Header 名称
gateway.auth.trustedProxy.requiredHeaders必须存在的额外 Header(代理验证)
gateway.auth.trustedProxy.allowUsers用户身份允许列表,空表示允许所有已认证用户
gateway.auth.trustedProxy.allowLoopback允许同主机 loopback 反向代理(默认 false

仅当本地反向代理是预期信任边界时才启用 allowLoopback。任何能连接 Gateway 的本地进程都可能尝试发送身份 Header,所以需确保直接 Gateway 访问私有、且代理要求必须携带 x-forwarded-proto 等代理拥有的 Header。

TLS 终止和 HSTS 怎么配置

使用一个 TLS 终止点,并在该点应用 HSTS。

推荐:代理 TLS 终止

当反向代理处理 HTTPS(如 https://control.example.com)时,在代理侧设置 Strict-Transport-Security。证书和 HTTP 加固策略集中一处,OpenClaw 可保留在回环 HTTP。

text
Strict-Transport-Security: max-age=31536000; includeSubDomains

Gateway 自身 TLS 终止

若 OpenClaw 直接提供 HTTPS,设置:

json5
{
  gateway: {
    tls: { enabled: true },
    http: {
      securityHeaders: {
        strictTransportSecurity: "max-age=31536000; includeSubDomains",
      },
    },
  },
}

strictTransportSecurity 接受字符串值,或 false 显式禁用。

推出建议

  • 先用短 max-age 验证(如 max-age=300
  • 确认无误后改为长期值(如 max-age=31536000
  • 仅当所有子域都已 HTTPS 时才加 includeSubDomains
  • 仅当整个域集符合 preload 要求时使用预加载
  • 仅回环本地开发无需 HSTS

代理配置示例

Pomerium

Pomerium 通过 x-pomerium-claim-email 传递身份,JWT 在 x-pomerium-jwt-assertion

json5
{
  gateway: {
    bind: "lan",
    trustedProxies: ["10.0.0.1"],
    auth: {
      mode: "trusted-proxy",
      trustedProxy: {
        userHeader: "x-pomerium-claim-email",
        requiredHeaders: ["x-pomerium-jwt-assertion"],
      },
    },
  },
}

Pomerium 路由配置:

yaml
routes:
  - from: https://openclaw.example.com
    to: http://openclaw-gateway:18789
    policy:
      - allow:
          or:
            - email:
                is: nick@example.com
    pass_identity_headers: true

Caddy + OAuth

使用 caddy-security 插件。

json5
{
  gateway: {
    bind: "lan",
    trustedProxies: ["10.0.0.1"],
    auth: {
      mode: "trusted-proxy",
      trustedProxy: {
        userHeader: "x-forwarded-user",
      },
    },
  },
}

Caddyfile 片段:

openclaw.example.com {
    authenticate with oauth2_provider
    authorize with policy1

    reverse_proxy openclaw:18789 {
        header_up X-Forwarded-User {http.auth.user.email}
    }
}

nginx + oauth2-proxy

oauth2-proxy 使用 x-auth-request-email 传递身份。

json5
{
  gateway: {
    bind: "lan",
    trustedProxies: ["10.0.0.1"],
    auth: {
      mode: "trusted-proxy",
      trustedProxy: {
        userHeader: "x-auth-request-email",
      },
    },
  },
}

nginx 配置:

nginx
location / {
    auth_request /oauth2/auth;
    auth_request_set $user $upstream_http_x_auth_request_email;

    proxy_pass http://openclaw:18789;
    proxy_set_header X-Auth-Request-Email $user;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

Traefik + Forward Auth

json5
{
  gateway: {
    bind: "lan",
    trustedProxies: ["172.17.0.1"], // Traefik 容器 IP
    auth: {
      mode: "trusted-proxy",
      trustedProxy: {
        userHeader: "x-forwarded-user",
      },
    },
  },
}

为什么启动报 "mixed_trusted_proxy_token" 错误

OpenClaw 拒绝同时启用 gateway.auth.tokenOPENCLAW_GATEWAY_TOKENtrusted-proxy 模式。混合配置会导致 loopback 请求走错认证路径。解决办法:

  • 使用 trusted-proxy 模式时删除共享 token
  • 如需 token 认证,将 gateway.auth.mode 改为 "token"

Loopback 请求仍会失败关闭,不会静默通过。内部调用者可改用 gateway.auth.password

Operator 作用域 Header(x-openclaw-scopes)

Trusted-proxy 模式是携带身份的 HTTP 模式,调用方可选声明 operator 作用域:

  • x-openclaw-scopes: operator.read
  • x-openclaw-scopes: operator.read,operator.write
  • x-openclaw-scopes: operator.admin,operator.write

行为:

  • Header 存在时,OpenClaw 遵守声明的 scope
  • Header 存在但为空:声明 operator 作用域
  • Header 不存在:标准 API 回退到默认 operator 作用域;插件 HTTP 路由则回退到 operator.write
  • 浏览器 HTTP 请求仍需通过 gateway.controlUi.allowedOrigins 检查

实用规则:想缩小权限时显式传递 x-openclaw-scopes

安全检查清单

启用前确认:

  • [ ] 代理是唯一路径:Gateway 端口只允许代理访问
  • [ ] trustedProxies 最小化:只填实际代理 IP,不是整个子网
  • [ ] Loopback 代理源已确认:除非显式启用 allowLoopback,否则 loopback 会被拒绝
  • [ ] 代理剥离 Header:代理覆盖(而非追加)客户端的 x-forwarded-*
  • [ ] TLS 终止:代理处理 TLS,用户通过 HTTPS 连接
  • [ ] allowedOrigins 显式:非 loopback Control UI 使用明确来源
  • [ ] 设置 allowUsers(推荐):限制到已知用户
  • [ ] 无混合 token 配置:不同时设置 token 和 trusted-proxy
  • [ ] 本地密码回退私有:若配置了 gateway.auth.password,确保 Gateway 端口对远程防火墙

安全审计

openclaw security audit 会将 trusted-proxy 模式标记为严重等级(有意的提醒)。审计检查:

  • 基础 gateway.trusted_proxy_auth 提醒
  • 缺少 trustedProxies
  • 缺少 userHeader
  • allowUsers 为空(允许任何已认证用户)
  • allowLoopback 启用
  • 暴露的 Control UI 入口缺 origin 策略

故障排查

"trusted_proxy_untrusted_source" 报错

请求 IP 不在 gateway.trustedProxies 中。检查:

  • 代理 IP 是否正确?(Docker 容器 IP 会变化)
  • 代理前是否有负载均衡?
  • 使用 docker inspectkubectl get pods -o wide 查找实际 IP

"trusted_proxy_loopback_source" 怎么解决

同主机 loopback 反向代理被拒绝。解决方法:

  • 改用 token/password 认证;或
  • 通过非 loopback 受信任代理地址路由;或
  • 若有意使用同主机代理,设置 allowLoopback: true 并将 loopback 地址加入 trustedProxies,同时确保代理覆盖身份 Header

"trusted_proxy_user_missing" 报错

用户 Header 为空或缺失。检查:

  • 代理是否配置了传递身份 Header?
  • Header 名称拼写是否正确(大小写不敏感但拼写重要)?
  • 用户是否在代理端实际认证成功?

"trusted_proxy_missing_header_*" 怎么办

必需 Header 不存在。检查代理配置中是否添加了这些 Header,以及链路中是否有地方剥离。

"trusted_proxy_user_not_allowed"

用户已认证但不在 allowUsers 中。要么添加该用户,要么清空 allowUsers。

"trusted_proxy_origin_not_allowed"

Trusted-proxy 认证通过,但浏览器 Origin 未通过 Control UI 检查。检查:

  • gateway.controlUi.allowedOrigins 是否包含确切浏览器 Origin
  • 是否意外使用了通配符
  • 若使用 Host-header 回退模式,必须显式设置 gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback=true

WebSocket 仍然失败

确保代理:

  • 支持 WebSocket 升级(Upgrade: websocketConnection: upgrade
  • 在 WebSocket 升级请求上也传递身份 Header(不只是 HTTP)
  • 没有为 WebSocket 使用独立的认证路径

从 Token 认证迁移

  1. 配置代理认证用户并传递 Header
  2. 独立测试代理(curl 带上 Header)
  3. 更新 OpenClaw 配置为 trusted-proxy 模式
  4. 重启 Gateway
  5. 从 Control UI 测试 WebSocket
  6. 运行 openclaw security audit 并审查结果

常见问题

为什么 WebSocket 1008 unauthorized 错误一直出现?

浏览器无法在 WebSocket 升级时传递 token,导致 token 认证失败。改用 trusted-proxy 模式后,代理通过身份 Header 认证用户,OpenClaw 验证代理 IP 并提取身份,即可解决此错误。

怎么配置 nginx + oauth2-proxy 实现 trusted-proxy?

在 OpenClaw 配置中设置 gateway.trustedProxies 为 nginx/oauth2-proxy 的 IP, userHeaderx-auth-request-email。nginx 配置中使用 auth_request 并设置 proxy_set_header X-Auth-Request-Email,同时开启 WebSocket 升级(proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade")。

启动报 "mixed_trusted_proxy_token" 怎么办?

说明同时设置了 gateway.auth.tokengateway.auth.mode: "trusted-proxy"。移除共享 token 或改用 mode: "token"。trusted-proxy 模式不支持 token 回退。

相关文档