可信代理认证

⚠️ 安全敏感功能。 此模式将认证完全委托给你的反向代理。配置不当可能导致网关暴露给未授权访问。启用前请仔细阅读本页。

何时使用

在以下场景使用 trusted-proxy 认证模式:

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

何时不要使用

  • 代理不做用户认证(只是 TLS 终结或负载均衡器)
  • 存在绕过代理直达网关的路径(防火墙漏洞、内网直接访问)
  • 不确定代理是否正确剥离/覆盖转发 header
  • 只需个人单用户访问(考虑 Tailscale Serve + 回环,更简单)

工作原理

  1. 反向代理对用户做认证(OAuth、OIDC、SAML 等)
  2. 代理在请求中添加已认证的用户身份 header(例如 x-forwarded-user: [email protected]
  3. OpenClaw 检查请求是否来自可信代理 IP(在 gateway.trustedProxies 中配置)
  4. OpenClaw 从配置的 header 中提取用户身份
  5. 全部检查通过后,请求获得授权

控制 UI 配对行为

gateway.auth.mode = "trusted-proxy" 生效且请求通过了可信代理检查时,控制 UI WebSocket 会话可以不经过设备配对身份验证就连接。

影响:

  • 此模式下配对不再是控制 UI 访问的主要门控。
  • 反向代理认证策略和 allowUsers 成为实际的访问控制。
  • 确保网关入口仅限可信代理 IP(gateway.trustedProxies + 防火墙)。

配置

{
  gateway: {
    // Use loopback for same-host proxy setups; use lan/custom for remote proxy hosts
    bind: "loopback",

    // CRITICAL: Only add your proxy's IP(s) here
    trustedProxies: ["10.0.0.1", "172.17.0.1"],

    auth: {
      mode: "trusted-proxy",
      trustedProxy: {
        // Header containing authenticated user identity (required)
        userHeader: "x-forwarded-user",

        // Optional: headers that MUST be present (proxy verification)
        requiredHeaders: ["x-forwarded-proto", "x-forwarded-host"],

        // Optional: restrict to specific users (empty = allow all)
        allowUsers: ["[email protected]", "[email protected]"],
      },
    },
  },
}

如果 gateway.bindloopback,需要在 gateway.trustedProxies 中包含回环代理地址(127.0.0.1::1 或等效的回环 CIDR)。

配置参考

字段必填说明
gateway.trustedProxies要信任的代理 IP 地址数组。来自其他 IP 的请求会被拒绝。
gateway.auth.mode必须设为 "trusted-proxy"
gateway.auth.trustedProxy.userHeader包含已认证用户身份的 header 名称
gateway.auth.trustedProxy.requiredHeaders额外的必须存在的 header,用于验证请求确实经过了代理
gateway.auth.trustedProxy.allowUsers用户身份白名单。为空表示允许所有已认证用户。

TLS 终结与 HSTS

使用一个 TLS 终结点,在那里配置 HSTS。

推荐模式:代理 TLS 终结

反向代理为 https://control.example.com 处理 HTTPS 时,在代理上为该域名设置 Strict-Transport-Security

  • 适合面向互联网的部署。
  • 证书和 HTTP 安全加固策略集中在一处。
  • OpenClaw 可以在代理后面保持回环 HTTP。

示例 header 值:

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

网关 TLS 终结

如果 OpenClaw 自身直接提供 HTTPS(无 TLS 终结代理),设置:

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

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

上线建议

  • 先用短 max age(例如 max-age=300)验证流量。
  • 确认无误后再增加到长期值(例如 max-age=31536000)。
  • 只有在所有子域名都支持 HTTPS 时才加 includeSubDomains
  • 只有在你清楚你的完整域名集符合 preload 要求时才使用 preload。
  • 回环本地开发不需要 HSTS。

代理配置示例

Pomerium

Pomerium 在 x-pomerium-claim-email(或其他 claim header)中传递身份,在 x-pomerium-jwt-assertion 中传递 JWT。

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

Pomerium 配置片段:

routes:
  - from: https://openclaw.example.com
    to: http://openclaw-gateway:18789
    policy:
      - allow:
          or:
            - email:
                is: [email protected]
    pass_identity_headers: true

Caddy + OAuth

Caddy 配合 caddy-security 插件可以认证用户并传递身份 header。

{
  gateway: {
    bind: "lan",
    trustedProxies: ["127.0.0.1"], // Caddy's IP (if on same host)
    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 中传递身份。

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

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

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

安全检查清单

启用可信代理认证之前,逐项确认:

  • 代理是唯一路径:网关端口的防火墙只允许代理访问
  • trustedProxies 最小化:只填实际的代理 IP,不要填整个子网
  • 代理剥离 header:代理覆盖(而非追加)来自客户端的 x-forwarded-* header
  • TLS 终结:代理处理 TLS;用户通过 HTTPS 连接
  • 设置了 allowUsers(推荐):限制为已知用户,而非允许所有已认证用户

安全审计

openclaw security audit 会以 critical 严重级别标记可信代理认证。这是刻意的——提醒你安全性依赖于代理配置。

审计检查项:

  • 缺少 trustedProxies 配置
  • 缺少 userHeader 配置
  • allowUsers 为空(允许任何已认证用户)

故障排查

”trusted_proxy_untrusted_source”

请求不是来自 gateway.trustedProxies 中的 IP。检查:

  • 代理 IP 是否正确?(Docker 容器 IP 可能会变)
  • 代理前面是否还有负载均衡器?
  • docker inspectkubectl get pods -o wide 查看实际 IP

”trusted_proxy_user_missing”

用户 header 为空或不存在。检查:

  • 代理是否配置了传递身份 header?
  • header 名称是否正确?(不区分大小写,但拼写要对)
  • 用户在代理层面是否真的已认证?

“trustedproxy_missing_header*”

必需的 header 不存在。检查:

  • 代理对这些特定 header 的配置
  • header 是否在传输链中被某处剥离

”trusted_proxy_user_not_allowed”

用户已认证但不在 allowUsers 中。把用户添加到白名单,或移除白名单。

WebSocket 仍然失败

确保你的代理:

  • 支持 WebSocket 升级(Upgrade: websocketConnection: upgrade
  • 在 WebSocket 升级请求上也传递身份 header(不仅仅是 HTTP)
  • WebSocket 连接没有走不同的认证路径

从 Token 认证迁移

如果你要从 token 认证切换到 trusted-proxy:

  1. 配置代理认证用户并传递 header
  2. 独立测试代理配置(用 curl 带 header 测试)
  3. 更新 OpenClaw 配置为 trusted-proxy 认证
  4. 重启网关
  5. 测试控制 UI 的 WebSocket 连接
  6. 运行 openclaw security audit 并审查发现项

相关文档