Appearance
Trusted Proxy 认证
安全敏感功能。 此模式将认证完全委托给你的反向代理。配置错误可能导致你的 Gateway 遭到未授权访问。启用前请仔细阅读本页。
适用场景
以下情况使用 trusted-proxy 认证模式:
- 你在身份感知代理(Pomerium、Caddy + OAuth、nginx + oauth2-proxy、Traefik + forward auth)后面运行 OpenClaw
- 你的代理处理所有认证,并通过 Header 传递用户身份
- 你处于 Kubernetes 或容器环境中,代理是访问 Gateway 的唯一路径
- 你遇到 WebSocket
1008 unauthorized错误,因为浏览器无法在 WS 载荷中传递 token
不适用场景
- 你的代理不认证用户(只是 TLS 终止或负载均衡器)
- 存在绕过代理直接访问 Gateway 的路径(防火墙漏洞、内网直接访问)
- 你不确定代理是否正确剥离/覆盖转发 Header
- 你只需要个人单用户访问(考虑使用 Tailscale Serve + 回环地址,配置更简单)
工作原理
- 你的反向代理认证用户(OAuth、OIDC、SAML 等)
- 代理添加包含已认证用户身份的 Header(例如
x-forwarded-user: nick@example.com) - OpenClaw 验证请求来自受信任的代理 IP(在
gateway.trustedProxies中配置) - OpenClaw 从配置的 Header 中提取用户身份
- 一切检查通过后,请求获得授权
控制台配对行为
当 gateway.auth.mode = "trusted-proxy" 启用且请求通过受信任代理检查时,Control UI WebSocket 会话可以在没有设备配对身份的情况下连接。
影响说明:
- 在此模式下,配对不再是 Control UI 访问的主要关卡。
- 你的反向代理认证策略和
allowUsers成为实际的访问控制机制。 - 请将 Gateway 入口锁定为仅受信任的代理 IP(
gateway.trustedProxies+ 防火墙)。
配置
json5
{
gateway: {
// 同主机代理使用 loopback;远程代理主机使用 lan/custom
bind: "loopback",
// 关键:这里只添加你代理的 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"],
},
},
},
}如果 gateway.bind 是 loopback,请在 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 值:
text
Strict-Transport-Security: max-age=31536000; includeSubDomainsGateway TLS 终止
如果 OpenClaw 自身直接提供 HTTPS(没有 TLS 终止代理),设置:
json5
{
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。
- 仅限回环的本地开发不需要 HSTS。
代理配置示例
Pomerium
Pomerium 通过 x-pomerium-claim-email(或其他声明 Header)传递身份,并在 x-pomerium-jwt-assertion 中传递 JWT。
json5
{
gateway: {
bind: "lan",
trustedProxies: ["10.0.0.1"], // Pomerium 的 IP
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: trueCaddy + OAuth
带 caddy-security 插件的 Caddy 可以认证用户并传递身份 Header。
json5
{
gateway: {
bind: "lan",
trustedProxies: ["127.0.0.1"], // Caddy 的 IP(同主机时)
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"], // nginx/oauth2-proxy 的 IP
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",
},
},
},
}安全检查清单
启用 trusted-proxy 认证前,确认:
- [ ] 代理是唯一路径:Gateway 端口对代理以外的所有访问都有防火墙保护
- [ ] trustedProxies 最小化:只填写你实际的代理 IP,而不是整个子网
- [ ] 代理剥离 Header:你的代理会覆盖(而非追加)客户端的
x-forwarded-*Header - [ ] TLS 终止:你的代理处理 TLS,用户通过 HTTPS 连接
- [ ] 建议设置 allowUsers:限制到已知用户,而非允许任何已认证的人
安全审计
openclaw security audit 会将 trusted-proxy 认证标记为严重等级发现。这是故意的——提醒你正在将安全委托给你的代理配置。
审计会检查:
- 缺少
trustedProxies配置 - 缺少
userHeader配置 - 空的
allowUsers(允许任何已认证用户)
故障排查
"trusted_proxy_untrusted_source"
请求不是来自 gateway.trustedProxies 中的 IP。检查:
- 代理 IP 是否正确?(Docker 容器 IP 可能会变化)
- 你的代理前面是否有负载均衡器?
- 使用
docker inspect或kubectl get pods -o wide找到实际 IP
"trusted_proxy_user_missing"
用户 Header 为空或缺失。检查:
- 你的代理是否配置为传递身份 Header?
- Header 名称是否正确?(大小写不敏感,但拼写很重要)
- 用户是否真的在代理处完成了认证?
"trusted_proxy_missing_header_*"
必需的 Header 不存在。检查:
- 你代理对这些特定 Header 的配置
- 链路中某处是否在剥离 Header
"trusted_proxy_user_not_allowed"
用户已认证但不在 allowUsers 中。要么添加该用户,要么删除允许列表。
WebSocket 仍然失败
确保你的代理:
- 支持 WebSocket 升级(
Upgrade: websocket、Connection: upgrade) - 在 WebSocket 升级请求上也传递身份 Header(不只是 HTTP)
- 对 WebSocket 连接没有单独的认证路径
从 Token 认证迁移
如果你正从 token 认证迁移到 trusted-proxy:
- 配置你的代理认证用户并传递 Header
- 独立测试代理配置(使用 curl 带 Header)
- 用 trusted-proxy 认证更新 OpenClaw 配置
- 重启 Gateway
- 从 Control UI 测试 WebSocket 连接
- 运行
openclaw security audit并审查结果