Gateway
受信任的代理驗證
何時使用
在以下情況使用 trusted-proxy 驗證模式:
- 你在具備身分識別能力的代理(Pomerium、Caddy + OAuth、nginx + oauth2-proxy、Traefik + forward auth)後方執行 OpenClaw。
- 你的代理會處理所有驗證,並透過標頭傳遞使用者身分。
- 你處於 Kubernetes 或容器環境,且代理是通往 Gateway 的唯一路徑。
- 你遇到 WebSocket
1008 unauthorized錯誤,因為瀏覽器無法在 WS payload 中傳遞 token。
何時不要使用
- 如果你的代理不會驗證使用者(只是 TLS 終止器或負載平衡器)。
- 如果有任何路徑可繞過代理存取 Gateway(防火牆漏洞、內部網路存取)。
- 如果你不確定代理是否正確移除或覆寫轉送標頭。
- 如果你只需要個人單一使用者存取(可考慮使用 Tailscale Serve + loopback 以簡化設定)。
運作方式
代理驗證使用者
你的反向代理會驗證使用者(OAuth、OIDC、SAML 等)。
代理加入身分標頭
代理會加入包含已驗證使用者身分的標頭(例如 x-forwarded-user: [email protected])。
Gateway 驗證信任來源
OpenClaw 會檢查請求是否來自受信任的代理 IP(於 gateway.trustedProxies 中設定)。
Gateway 擷取身分
OpenClaw 會從設定的標頭擷取使用者身分。
授權
如果所有檢查都通過,請求就會獲得授權。
Control UI 配對行為
當 gateway.auth.mode = "trusted-proxy" 啟用且請求通過 trusted-proxy 檢查時,Control UI WebSocket 工作階段可以在沒有裝置配對身分的情況下連線。
影響:
- 在此模式中,配對不再是 Control UI 存取的主要閘門。
- 你的反向代理驗證政策與
allowUsers會成為實際的存取控制。 - 僅讓 Gateway 入口鎖定為受信任的代理 IP(
gateway.trustedProxies+ 防火牆)。
設定
{
gateway: {
// Trusted-proxy auth expects requests from a non-loopback trusted proxy source by default
bind: "lan",
// 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]"],
// Optional: allow a same-host loopback proxy after explicit opt-in
allowLoopback: false,
},
},
},
}
設定參考
gateway.trustedProxiesstring[]required要信任的代理 IP 位址陣列。來自其他 IP 的請求會被拒絕。
gateway.auth.modestringrequired必須是 "trusted-proxy"。
gateway.auth.trustedProxy.userHeaderstringrequired包含已驗證使用者身分的標頭名稱。
gateway.auth.trustedProxy.requiredHeadersstring[]請求要被信任時必須存在的其他標頭。
gateway.auth.trustedProxy.allowUsersstring[]使用者身分允許清單。空白表示允許所有已驗證使用者。
gateway.auth.trustedProxy.allowLoopbackboolean選擇啟用對 same-host loopback 反向代理的支援。預設為 false。
TLS 終止與 HSTS
使用單一 TLS 終止點,並在該處套用 HSTS。
代理 TLS 終止(建議)
當你的反向代理為 https://control.example.com 處理 HTTPS 時,請在代理上為該網域設定 Strict-Transport-Security。
- 適合面向網際網路的部署。
- 將憑證與 HTTP 強化政策集中在同一處。
- OpenClaw 可以留在代理後方的 loopback HTTP。
範例標頭值:
Strict-Transport-Security: max-age=31536000; includeSubDomains
Gateway TLS 終止
如果 OpenClaw 本身直接提供 HTTPS(沒有 TLS 終止代理),請設定:
{
gateway: {
tls: { enabled: true },
http: {
securityHeaders: {
strictTransportSecurity: "max-age=31536000; includeSubDomains",
},
},
},
}
strictTransportSecurity 接受字串標頭值,或使用 false 明確停用。
推出指引
- 驗證流量時,先從較短的 max age 開始(例如
max-age=300)。 - 只有在信心足夠高後,才提高到長效值(例如
max-age=31536000)。 - 只有在每個子網域都已準備好 HTTPS 時,才加入
includeSubDomains。 - 只有在你有意為完整網域集合滿足 preload 要求時,才使用 preload。
- 僅限 loopback 的本機開發不會受益於 HSTS。
代理設定範例
Pomerium
Pomerium 會在 x-pomerium-claim-email(或其他 claim 標頭)中傳遞身分,並在 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
使用 OAuth 的 Caddy
搭配 caddy-security Plugin 的 Caddy 可以驗證使用者並傳遞身分標頭。
{
gateway: {
bind: "lan",
trustedProxies: ["10.0.0.1"], // Caddy/sidecar proxy 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 中傳遞身分。
{
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";
}
使用 forward auth 的 Traefik
{
gateway: {
bind: "lan",
trustedProxies: ["172.17.0.1"], // Traefik container IP
auth: {
mode: "trusted-proxy",
trustedProxy: {
userHeader: "x-forwarded-user",
},
},
},
}
混合 token 設定
OpenClaw 會拒絕同時啟用 gateway.auth.token(或 OPENCLAW_GATEWAY_TOKEN)與 trusted-proxy 模式的模糊設定。混合 token 設定可能導致 loopback 請求在錯誤的驗證路徑上悄悄通過驗證。
如果你在啟動時看到 mixed_trusted_proxy_token 錯誤:
- 使用 trusted-proxy 模式時,移除共享 token,或
- 如果你打算使用 token 型驗證,將
gateway.auth.mode切換為"token"。
Loopback trusted-proxy 身分標頭仍會失敗關閉:same-host 呼叫者不會被悄悄驗證為代理使用者。繞過代理的內部 OpenClaw 呼叫者可以改用 gateway.auth.password / OPENCLAW_GATEWAY_PASSWORD 驗證。Token fallback 在 trusted-proxy 模式中仍刻意不支援。
操作者範圍標頭
Trusted-proxy 驗證是一種帶有身分的 HTTP 模式,因此呼叫者可以選擇使用 x-openclaw-scopes 宣告操作者範圍。
範例:
x-openclaw-scopes: operator.readx-openclaw-scopes: operator.read,operator.writex-openclaw-scopes: operator.admin,operator.write
行為:
- 當標頭存在時,OpenClaw 會採用宣告的 scope 集合。
- 當標頭存在但為空時,請求宣告沒有操作者範圍。
- 當標頭不存在時,一般帶有身分的 HTTP API 會回退到標準操作者預設 scope 集合。
- Gateway 驗證的 Plugin HTTP 路由預設較窄:當
x-openclaw-scopes不存在時,其執行階段 scope 會回退到operator.write。 - 來自瀏覽器來源的 HTTP 請求,即使 trusted-proxy 驗證成功,仍必須通過
gateway.controlUi.allowedOrigins(或刻意使用 Host-header fallback 模式)。
實務規則:當你希望 trusted-proxy 請求比預設值更窄,或 gateway-auth Plugin 路由需要比 write scope 更強的權限時,請明確傳送 x-openclaw-scopes。
安全檢查清單
啟用 trusted-proxy 驗證前,請確認:
- [ ] Proxy 是唯一路徑:除了你的 Proxy 之外,Gateway 連接埠已對所有來源封鎖。
- [ ] trustedProxies 最小化:只包含你的實際 Proxy IP,而不是整個子網路。
- [ ] Loopback Proxy 來源是刻意設定:除非為同主機 Proxy 明確啟用
gateway.auth.trustedProxy.allowLoopback,否則 trusted-proxy 驗證會對 loopback 來源請求安全失敗。 - [ ] Proxy 會移除標頭:你的 Proxy 會覆寫(而不是附加)來自用戶端的
x-forwarded-*標頭。 - [ ] TLS 終止:你的 Proxy 會處理 TLS;使用者透過 HTTPS 連線。
- [ ] allowedOrigins 是明確的:非 loopback 控制 UI 使用明確的
gateway.controlUi.allowedOrigins。 - [ ] allowUsers 已設定(建議):限制為已知使用者,而不是允許任何已驗證的人。
- [ ] 沒有混用 Token 設定:不要同時設定
gateway.auth.token和gateway.auth.mode: "trusted-proxy"。 - [ ] 本機密碼備援保持私密:如果你為內部直接呼叫者設定
gateway.auth.password,請保持 Gateway 連接埠受防火牆保護,讓非 Proxy 的遠端用戶端無法直接連到它。
安全性稽核
openclaw security audit 會將 trusted-proxy 驗證標記為 critical 嚴重性發現。這是刻意設計的,提醒你正在把安全性委派給你的 Proxy 設定。
稽核會檢查:
- 基礎
gateway.trusted_proxy_auth警告/critical 提醒 - 缺少
trustedProxies設定 - 缺少
userHeader設定 - 空的
allowUsers(允許任何已驗證使用者) - 為同主機 Proxy 來源啟用的
allowLoopback - 暴露的控制 UI 表面上有萬用字元或缺少瀏覽器來源政策
疑難排解
trusted_proxy_untrusted_source
請求不是來自 gateway.trustedProxies 中的 IP。請檢查:
- Proxy IP 正確嗎?(Docker 容器 IP 可能會變更。)
- 你的 Proxy 前方是否有負載平衡器?
- 使用
docker inspect或kubectl get pods -o wide找出實際 IP。
trusted_proxy_loopback_source
OpenClaw 拒絕了一個 loopback 來源的 trusted-proxy 請求。
檢查:
- Proxy 是否從
127.0.0.1/::1連線? - 你是否嘗試搭配同主機 loopback 反向 Proxy 使用 trusted-proxy 驗證?
修正:
- 對於不經過 Proxy 的內部同主機用戶端,優先使用 Token/密碼驗證,或
- 透過非 loopback 的受信任 Proxy 位址路由,並將該 IP 保留在
gateway.trustedProxies中,或 - 對於刻意設定的同主機反向 Proxy,設定
gateway.auth.trustedProxy.allowLoopback = true,將 loopback 位址保留在gateway.trustedProxies中,並確認 Proxy 會移除或覆寫身分標頭。
trusted_proxy_user_missing
使用者標頭是空的或不存在。請檢查:
- 你的 Proxy 是否設定為傳遞身分標頭?
- 標頭名稱正確嗎?(不區分大小寫,但拼字很重要)
- 使用者是否真的已在 Proxy 驗證?
trusted_proxy_missing_header_*
必要標頭不存在。請檢查:
- 你的 Proxy 對這些特定標頭的設定。
- 標頭是否在鏈中的某處被移除。
trusted_proxy_user_not_allowed
使用者已驗證,但不在 allowUsers 中。請新增該使用者,或移除允許清單。
trusted_proxy_origin_not_allowed
trusted-proxy 驗證成功,但瀏覽器 Origin 標頭未通過控制 UI 來源檢查。
檢查:
gateway.controlUi.allowedOrigins包含確切的瀏覽器來源。- 除非你刻意想要允許全部行為,否則不要依賴萬用字元來源。
- 如果你刻意使用 Host 標頭備援模式,請確認已刻意設定
gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback=true。
WebSocket still failing
確認你的 Proxy:
- 支援 WebSocket 升級(
Upgrade: websocket、Connection: upgrade)。 - 會在 WebSocket 升級請求上傳遞身分標頭(不只是 HTTP)。
- 沒有為 WebSocket 連線設定獨立的驗證路徑。
從 Token 驗證遷移
如果你要從 Token 驗證移轉到 trusted-proxy:
Configure the proxy
設定你的 Proxy 以驗證使用者並傳遞標頭。
Test the proxy independently
獨立測試 Proxy 設定(使用帶有標頭的 curl)。
Update OpenClaw config
使用 trusted-proxy 驗證更新 OpenClaw 設定。
Restart the Gateway
重新啟動 Gateway。
Test WebSocket
從控制 UI 測試 WebSocket 連線。
Audit
執行 openclaw security audit 並檢閱發現。