受信任代理認證

安全敏感功能。 此模式將認證完全委派給你的反向代理。設定錯誤可能讓你的 Gateway 暴露在未授權存取之下。啟用前請仔細閱讀本頁。

何時使用

在以下情況使用 trusted-proxy 認證模式:

  • 你在一個身分感知代理(Pomerium、Caddy + OAuth、nginx + oauth2-proxy、Traefik + forward auth)後面運行 OpenClaw
  • 你的代理已處理所有認證並透過 header 傳遞使用者身分
  • 你在 Kubernetes 或容器環境中,代理是唯一能到達 Gateway 的路徑
  • 你遇到 WebSocket 1008 unauthorized 錯誤,因為瀏覽器無法在 WS payload 中傳遞 token

何時不該使用

  • 如果你的代理不認證使用者(只做 TLS 終止或負載平衡)
  • 如果有任何路徑可以繞過代理到達 Gateway(防火牆漏洞、內部網路存取)
  • 如果你不確定代理是否正確地剝除/覆寫轉送 header
  • 如果你只需要個人單使用者存取(考慮 Tailscale Serve + loopback,設定更簡單)

運作原理

  1. 你的反向代理認證使用者(OAuth、OIDC、SAML 等)
  2. 代理加入一個帶有已認證使用者身分的 header(例如 x-forwarded-user: [email protected]
  3. OpenClaw 檢查請求是否來自受信任的代理 IP(在 gateway.trustedProxies 中設定)
  4. OpenClaw 從設定的 header 中提取使用者身分
  5. 如果一切正確,請求即被授權

Control UI 配對行為

gateway.auth.mode = "trusted-proxy" 生效且請求通過受信任代理檢查時,Control UI WebSocket session 可以不需要裝置配對身分就能連線。

影響:

  • 在此模式下,配對不再是 Control UI 存取的主要閘門。
  • 你的反向代理認證策略和 allowUsers 成為有效的存取控制。
  • 確保 gateway 入口僅限受信任的代理 IP(gateway.trustedProxies + 防火牆)。

設定

{
  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: ["[email protected]", "[email protected]"],
      },
    },
  },
}

如果 gateway.bindloopback,在 gateway.trustedProxies 中加入 loopback 代理位址(127.0.0.1::1 或等效的 loopback 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 可以在代理後面保持 loopback HTTP。

Header 值範例:

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

Gateway 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。
  • Loopback-only 的本機開發不需要 HSTS。

代理設定範例

Pomerium

Pomerium 透過 x-pomerium-claim-email(或其他 claim header)傳遞身分,並在 x-pomerium-jwt-assertion 中包含 JWT。

{
  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 設定片段:

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

Caddy with OAuth

Caddy 搭配 caddy-security 外掛可以認證使用者並傳遞身分 header。

{
  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 傳遞身分。

{
  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 with Forward Auth

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

安全檢查清單

啟用受信任代理認證前,確認以下各項:

  • 代理是唯一路徑:Gateway 連接埠被防火牆限制,只有你的代理能存取
  • 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 升級請求(不只是 HTTP)中也傳遞身分 header
  • 沒有為 WebSocket 連線設定不同的認證路徑

從 Token 認證遷移

如果你從 token 認證遷移到受信任代理:

  1. 設定你的代理來認證使用者並傳遞 header
  2. 獨立測試代理設定(用 curl 帶 header)
  3. 更新 OpenClaw 設定為受信任代理認證
  4. 重新啟動 Gateway
  5. 從 Control UI 測試 WebSocket 連線
  6. 執行 openclaw security audit 並檢視發現項目

相關文件