Gateway

Довірена автентифікація через проксі

Коли використовувати

Використовуйте режим автентифікації trusted-proxy, коли:

  • Ви запускаєте OpenClaw за проксі з урахуванням ідентичності (Pomerium, Caddy + OAuth, nginx + oauth2-proxy, Traefik + forward auth).
  • Ваш проксі обробляє всю автентифікацію та передає ідентичність користувача через заголовки.
  • Ви працюєте в Kubernetes або контейнерному середовищі, де проксі є єдиним шляхом до Gateway.
  • Ви отримуєте помилки WebSocket 1008 unauthorized, бо браузери не можуть передавати токени в корисному навантаженні WS.

Коли НЕ використовувати

  • Якщо ваш проксі не автентифікує користувачів (лише TLS-термінатор або балансувальник навантаження).
  • Якщо існує будь-який шлях до Gateway в обхід проксі (прогалини у firewall, доступ з внутрішньої мережі).
  • Якщо ви не впевнені, чи ваш проксі коректно видаляє/перезаписує переслані заголовки.
  • Якщо вам потрібен лише персональний однокористувацький доступ (розгляньте Tailscale Serve + loopback для простішого налаштування).

Як це працює

  • Проксі автентифікує користувача

    Ваш зворотний проксі автентифікує користувачів (OAuth, OIDC, SAML тощо).

  • Проксі додає заголовок ідентичності

    Проксі додає заголовок з ідентичністю автентифікованого користувача (наприклад, x-forwarded-user: nick@example.com).

  • Gateway перевіряє довірене джерело

    OpenClaw перевіряє, що запит надійшов від довіреної IP-адреси проксі (налаштовано в gateway.trustedProxies).

  • Gateway витягує ідентичність

    OpenClaw витягує ідентичність користувача з налаштованого заголовка.

  • Авторизація

    Якщо всі перевірки успішні, запит авторизується.

  • Поведінка сполучення Control UI

    Коли активний gateway.auth.mode = "trusted-proxy" і запит проходить перевірки trusted-proxy, WebSocket-сеанси Control UI можуть підключатися без ідентичності сполученого пристрою.

    Наслідки для області дії:

    • WebSocket-сеанси Control UI без пристрою підключаються, але типово не отримують операторських областей дії. OpenClaw очищає список запитаних областей дії до [], щоб сеанс, не прив'язаний до схваленого сполученого пристрою/токена, не міг самостійно оголошувати дозволи.
    • Якщо методи завершуються помилкою missing scope після успішного WebSocket-підключення, використовуйте HTTPS, щоб браузер міг згенерувати ідентичність пристрою та завершити сполучення. Див. небезпечний HTTP Control UI.
    • Лише аварійний обхід: gateway.controlUi.dangerouslyDisableDeviceAuth=true зберігає запитані області дії навіть без ідентичності пристрою. Це суттєве зниження рівня безпеки; швидко скасуйте. Див. небезпечний HTTP Control UI.

    Обмеження областей дії зворотним проксі:

    • Якщо ваш проксі надсилає x-openclaw-scopes у запиті оновлення WebSocket для Control UI, OpenClaw обмежує області дії сеансу перетином запитаних і оголошених областей дії. Цей заголовок не надає областей дії; він лише звужує те, що може мати сеанс.

    Наслідки:

    • Сполучення більше не є основним бар'єром для доступу до Control UI в цьому режимі.
    • Ваша політика автентифікації зворотного проксі та allowUsers стають фактичним контролем доступу.
    • Тримайте вхідний доступ до gateway обмеженим лише довіреними IP-адресами проксі (gateway.trustedProxies + firewall).

    Власні WebSocket-клієнти не є сеансами Control UI. gateway.controlUi.dangerouslyDisableDeviceAuth не надає областей дії довільним клієнтам client.mode: "backend" або клієнтам у стилі CLI. Власна автоматизація має використовувати ідентичність/сполучення пристрою, зарезервований прямий локальний допоміжний шлях backend client.id: "gateway-client" або Plugin admin HTTP RPC, коли поверхня HTTP-запит/відповідь підходить краще.

    Конфігурація

    json5
    {  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: ["nick@example.com", "admin@company.org"],         // 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 для https://control.example.com, задайте Strict-Transport-Security на проксі для цього домену.

    • Добре підходить для розгортань, доступних з інтернету.
    • Тримає політику сертифікатів і HTTP-захисту в одному місці.
    • OpenClaw може залишатися на loopback HTTP за проксі.

    Приклад значення заголовка:

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

    TLS-термінація на Gateway

    Якщо OpenClaw сам напряму обслуговує HTTPS (без проксі, що термінує TLS), задайте:

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

    strictTransportSecurity приймає рядкове значення заголовка або false для явного вимкнення.

    Рекомендації з розгортання

    • Спершу почніть із короткого max age (наприклад, max-age=300), поки перевіряєте трафік.
    • Збільшуйте до довготривалих значень (наприклад, max-age=31536000) лише після високої впевненості.
    • Додавайте includeSubDomains лише якщо кожен піддомен готовий до HTTPS.
    • Використовуйте preload лише якщо ви свідомо відповідаєте вимогам preload для повного набору доменів.
    • Локальна розробка лише через loopback не отримує користі від HSTS.

    Приклади налаштування проксі

    Pomerium

    Pomerium передає ідентичність у x-pomerium-claim-email (або інших claim-заголовках) і JWT у x-pomerium-jwt-assertion.

    json5
    {  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:

    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 з Plugin caddy-security може автентифікувати користувачів і передавати заголовки ідентичності.

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

    Фрагмент Caddyfile:

    Code
    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 container IP    auth: {      mode: "trusted-proxy",      trustedProxy: {        userHeader: "x-forwarded-user",      },    },  },}

    Змішана конфігурація токенів

    OpenClaw відхиляє неоднозначні конфігурації, де одночасно активні gateway.auth.token (або OPENCLAW_GATEWAY_TOKEN) і режим trusted-proxy. Змішані конфігурації токенів можуть призвести до того, що loopback-запити тихо автентифікуватимуться неправильним шляхом автентифікації.

    Якщо під час запуску ви бачите помилку mixed_trusted_proxy_token:

    • Видаліть спільний токен, коли використовуєте режим trusted-proxy, або
    • Перемкніть gateway.auth.mode на "token", якщо ви маєте намір використовувати автентифікацію на основі токенів.

    Довірені proxy-заголовки ідентичності loopback усе ще завершуються відмовою: виклики з того самого хоста не автентифікуються неявно як користувачі proxy. Внутрішні виклики OpenClaw, що обходять proxy, натомість можуть автентифікуватися через gateway.auth.password / OPENCLAW_GATEWAY_PASSWORD. Резервний варіант із токеном у режимі trusted-proxy навмисно не підтримується.

    Заголовок операторських scopes

    Автентифікація trusted-proxy є HTTP-режимом, що передає ідентичність, тож виклики можуть необов’язково оголошувати операторські scopes за допомогою x-openclaw-scopes в HTTP API-запитах.

    Примітка: WebSocket scopes визначаються handshake протоколу Gateway і прив’язкою ідентичності пристрою. У запитах Control UI на WebSocket upgrade x-openclaw-scopes є лише обмеженням negotiated session scopes, а не наданням прав. Про поведінку WebSocket scope з trusted-proxy див. поведінку pairing Control UI.

    Приклади:

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

    Поведінка:

    • Коли заголовок присутній, OpenClaw враховує оголошений набір scopes.
    • Коли заголовок присутній, але порожній, запит оголошує, що операторських scopes немає.
    • Коли заголовок відсутній, звичайні HTTP API, що передають ідентичність, повертаються до стандартного набору операторських scopes за замовчуванням.
    • Plugin HTTP routes з Gateway-автентифікацією типово мають вужчі права: коли x-openclaw-scopes відсутній, їхній runtime scope повертається до operator.write.
    • HTTP-запити з браузерним origin усе ще мають пройти gateway.controlUi.allowedOrigins (або навмисний fallback-режим Host-заголовка) навіть після успішної trusted-proxy auth.
    • Для WebSocket-сеансів Control UI x-openclaw-scopes є обмеженням scopes, коли він присутній у upgrade-запиті. Порожнє значення не дає жодних scopes.

    Практичне правило: надсилайте x-openclaw-scopes явно, коли хочете, щоб trusted-proxy-запит був вужчим за типові значення, або коли Plugin route з gateway-auth потребує чогось сильнішого за write scope.

    Контрольний список безпеки

    Перш ніж увімкнути trusted-proxy auth, перевірте:

    • [ ] Proxy є єдиним шляхом: порт Gateway закритий firewall для всього, крім вашого proxy.
    • [ ] trustedProxies мінімальний: лише фактичні IP-адреси вашого proxy, а не цілі підмережі.
    • [ ] Джерело loopback proxy є навмисним: trusted-proxy auth завершується відмовою для запитів із loopback-джерела, якщо gateway.auth.trustedProxy.allowLoopback не ввімкнено явно для proxy на тому самому хості.
    • [ ] Proxy прибирає заголовки: ваш proxy перезаписує (а не додає) x-forwarded-* заголовки від клієнтів.
    • [ ] TLS termination: ваш proxy обробляє TLS; користувачі підключаються через HTTPS.
    • [ ] allowedOrigins задано явно: Control UI не з loopback використовує явний gateway.controlUi.allowedOrigins.
    • [ ] allowUsers задано (рекомендовано): обмежте доступ відомими користувачами замість дозволяти всім автентифікованим.
    • [ ] Без змішаної конфігурації токенів: не задавайте одночасно gateway.auth.token і gateway.auth.mode: "trusted-proxy".
    • [ ] Локальний fallback пароля приватний: якщо ви налаштовуєте gateway.auth.password для внутрішніх прямих викликів, тримайте порт Gateway за firewall, щоб віддалені клієнти не через proxy не могли звернутися до нього напряму.

    Аудит безпеки

    openclaw security audit позначить trusted-proxy auth знахідкою з критичною серйозністю. Це навмисно — нагадування, що ви делегуєте безпеку налаштуванню свого proxy.

    Аудит перевіряє:

    • Базове попередження/критичне нагадування gateway.trusted_proxy_auth
    • Відсутню конфігурацію trustedProxies
    • Відсутню конфігурацію userHeader
    • Порожній allowUsers (дозволяє будь-якого автентифікованого користувача)
    • Увімкнений allowLoopback для proxy-джерел на тому самому хості
    • Wildcard або відсутню політику браузерного origin на відкритих поверхнях Control UI

    Усунення несправностей

    trusted_proxy_untrusted_source

    Запит надійшов не з IP у gateway.trustedProxies. Перевірте:

    • Чи правильний IP proxy? (IP контейнерів Docker можуть змінюватися.)
    • Чи є load balancer перед вашим proxy?
    • Використайте docker inspect або kubectl get pods -o wide, щоб знайти фактичні IP-адреси.
    trusted_proxy_loopback_source

    OpenClaw відхилив trusted-proxy-запит із loopback-джерела.

    Перевірте:

    • Чи proxy підключається з 127.0.0.1 / ::1?
    • Чи намагаєтеся ви використати trusted-proxy auth із loopback reverse proxy на тому самому хості?

    Виправлення:

    • Надавайте перевагу token/password auth для внутрішніх клієнтів на тому самому хості, які не проходять через proxy, або
    • Маршрутизуйте через trusted proxy address не з loopback і тримайте цю IP-адресу в gateway.trustedProxies, або
    • Для навмисного reverse 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. Або додайте його, або видаліть allowlist.

    trusted_proxy_origin_not_allowed

    Trusted-proxy auth успішна, але браузерний заголовок Origin не пройшов перевірки origin Control UI.

    Перевірте:

    • gateway.controlUi.allowedOrigins містить точний браузерний origin.
    • Ви не покладаєтеся на wildcard origins, якщо не хочете навмисно дозволити все.
    • Якщо ви навмисно використовуєте fallback-режим Host-заголовка, gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback=true задано свідомо.
    Connection succeeds but methods report missing scope

    WebSocket підключається, але chat.history, sessions.list або models.list завершується помилкою missing scope: operator.read.

    Типові причини:

    • Сеанс Control UI без пристрою: trusted-proxy auth може допустити WebSocket-з’єднання без ідентичності пристрою, але OpenClaw за задумом очищає scopes у сеансах без пристрою.
    • Custom backend client: gateway.controlUi.dangerouslyDisableDeviceAuth має scope Control UI і не надає scopes довільним backend або CLI-подібним WebSocket-клієнтам.
    • Надто вузький x-openclaw-scopes: якщо ваш proxy вставляє цей заголовок у WebSocket upgrade-запит Control UI, scopes сеансу обмежуються цим набором. Порожнє значення заголовка не дає жодних scopes.

    Виправлення:

    • Для Control UI використовуйте HTTPS, щоб браузер міг згенерувати ідентичність пристрою та завершити pairing.
    • Для custom automation використовуйте device identity/pairing, зарезервований direct-local шлях backend helper gateway-client або admin HTTP RPC.
    • Використовуйте gateway.controlUi.dangerouslyDisableDeviceAuth: true лише як тимчасовий break-glass шлях Control UI.
    WebSocket still failing

    Переконайтеся, що ваш proxy:

    • Підтримує WebSocket upgrades (Upgrade: websocket, Connection: upgrade).
    • Передає заголовки ідентичності у WebSocket upgrade-запитах (не лише HTTP).
    • Не має окремого auth path для WebSocket-з’єднань.

    Міграція з token auth

    Якщо ви переходите з token auth на trusted-proxy:

  • Configure the proxy

    Налаштуйте proxy для автентифікації користувачів і передавання заголовків.

  • Test the proxy independently

    Протестуйте налаштування proxy незалежно (curl із заголовками).

  • Update OpenClaw config

    Оновіть конфігурацію OpenClaw з trusted-proxy auth.

  • Restart the Gateway

    Перезапустіть Gateway.

  • Test WebSocket

    Протестуйте WebSocket-з’єднання з Control UI.

  • Audit

    Запустіть openclaw security audit і перегляньте знахідки.

  • Пов’язане

    Was this useful?
    On this page

    On this page