Gateway

신뢰할 수 있는 프록시 인증

사용해야 하는 경우

  • OpenClaw를 ID 인식 프록시(Pomerium, Caddy + OAuth, nginx + oauth2-proxy, Traefik + 전달 인증) 뒤에서 실행하는 경우.
  • 프록시가 모든 인증을 처리하고 헤더를 통해 사용자 ID를 전달하는 경우.
  • 프록시가 Gateway에 접근할 수 있는 유일한 경로인 Kubernetes 또는 컨테이너 환경을 사용하는 경우.
  • 브라우저가 WS 페이로드로 토큰을 전달할 수 없어 WebSocket 1008 unauthorized 오류가 발생하는 경우.

사용하면 안 되는 경우

  • 프록시가 사용자를 인증하지 않는 경우(단순한 TLS 종단점 또는 로드 밸런서인 경우).
  • 프록시를 우회하여 Gateway에 접근할 수 있는 경로가 하나라도 있는 경우(방화벽 허점, 내부 네트워크 접근).
  • 프록시가 전달된 헤더를 올바르게 제거하거나 덮어쓰는지 확실하지 않은 경우.
  • 개인용 단일 사용자 접근만 필요한 경우(대신 Tailscale Serve + local loopback 사용을 고려하세요).

작동 방식

  • 프록시가 사용자를 인증

    리버스 프록시가 사용자 인증(OAuth, OIDC, SAML 등)을 수행합니다.

  • 프록시가 ID 헤더를 추가

    프록시가 인증된 사용자 ID가 포함된 헤더를 추가합니다(예: x-forwarded-user: nick@example.com).

  • Gateway가 신뢰할 수 있는 출처를 확인

    OpenClaw는 요청이 신뢰할 수 있는 프록시 IP(gateway.trustedProxies)에서 왔으며 Gateway 자체의 loopback 또는 로컬 인터페이스 주소에서 오지 않았는지 확인합니다.

  • Gateway가 ID를 추출

    OpenClaw는 필수 헤더를 읽은 다음 구성된 헤더에서 사용자 ID를 읽습니다.

  • 권한 부여

    모든 검사를 통과하고 사용자가 allowUsers(설정된 경우) 검사를 통과하면 요청에 권한이 부여됩니다.

  • 구성

    json5
    {  gateway: {    // 신뢰할 수 있는 프록시 인증에서는 기본적으로 프록시의 출발지 IP가 loopback이 아니어야 합니다    bind: "lan",     // 중요: 여기에 프록시의 IP만 추가하세요    trustedProxies: ["10.0.0.1", "172.17.0.1"],     auth: {      mode: "trusted-proxy",      trustedProxy: {        // 인증된 사용자 ID가 포함된 헤더(필수)        userHeader: "x-forwarded-user",         // 선택 사항: 반드시 존재해야 하는 헤더(프록시 확인)        requiredHeaders: ["x-forwarded-proto", "x-forwarded-host"],         // 선택 사항: 특정 사용자로 제한(비어 있음 = 모두 허용)        allowUsers: ["nick@example.com", "admin@company.org"],         // 선택 사항: 명시적으로 동의한 후 동일 호스트의 loopback 프록시 허용        allowLoopback: false,      },    },  },}

    구성 참조

    gateway.trustedProxiesstring[]required

    신뢰할 프록시 IP 주소(또는 CIDR)의 배열입니다. 다른 IP에서 온 요청은 거부됩니다.

    gateway.auth.modestringrequired

    반드시 "trusted-proxy"여야 합니다.

    gateway.auth.trustedProxy.userHeaderstringrequired

    인증된 사용자 ID가 포함된 헤더 이름입니다.

    gateway.auth.trustedProxy.requiredHeadersstring[]

    요청을 신뢰하기 위해 반드시 존재해야 하는 추가 헤더입니다.

    gateway.auth.trustedProxy.allowUsersstring[]

    사용자 ID 허용 목록입니다. 비어 있으면 인증된 모든 사용자를 허용합니다.

    gateway.auth.trustedProxy.allowLoopbackbooleandefault: false

    동일 호스트의 loopback 리버스 프록시에 대한 선택적 지원입니다.

    Control UI 페어링 동작

    gateway.auth.mode = "trusted-proxy"가 활성화되어 있고 요청이 신뢰할 수 있는 프록시 검사를 통과하면 Control UI WebSocket 세션은 기기 페어링 ID 없이 연결할 수 있습니다.

    범위 관련 영향:

    • 기기 없는 Control UI WebSocket 세션은 연결되지만 기본적으로 운영자 범위를 받지 않습니다. OpenClaw는 승인 및 페어링된 기기/토큰에 바인딩되지 않은 세션이 권한을 자체 선언하지 못하도록 요청된 범위 목록을 []로 초기화합니다.
    • WebSocket 연결에 성공한 후 메서드가 missing scope로 실패하면 브라우저가 기기 ID를 생성하고 페어링을 완료할 수 있도록 HTTPS를 사용하세요. Control UI의 안전하지 않은 HTTP를 참조하세요.
    • 비상시에만 사용: gateway.controlUi.dangerouslyDisableDeviceAuth=true는 기기 ID가 없어도 요청된 범위를 유지합니다. 이는 보안을 심각하게 약화하므로 신속하게 되돌리세요. Control UI의 안전하지 않은 HTTP를 참조하세요.

    리버스 프록시 범위 제한: 프록시가 Control UI WebSocket 업그레이드 요청에 x-openclaw-scopes를 보내면 OpenClaw는 세션 범위를 요청된 범위와 선언된 범위의 교집합으로 제한합니다. 이 헤더는 범위를 부여하지 않으며 세션이 보유할 수 있는 범위만 좁힙니다.

    영향:

    • 이 모드에서는 페어링이 더 이상 Control UI 접근의 기본 게이트가 아닙니다.
    • 리버스 프록시 인증 정책과 allowUsers가 실질적인 접근 제어 수단이 됩니다.
    • Gateway 인그레스는 신뢰할 수 있는 프록시 IP로만 제한하세요(gateway.trustedProxies + 방화벽).

    사용자 지정 WebSocket 클라이언트는 Control UI 세션이 아닙니다. gateway.controlUi.dangerouslyDisableDeviceAuth는 임의의 client.mode: "backend" 또는 CLI 형태의 클라이언트에 범위를 부여하지 않습니다. 사용자 지정 자동화는 기기 ID/페어링, 예약된 직접 로컬 client.id: "gateway-client" 백엔드 도우미 경로 또는 HTTP 요청/응답 인터페이스가 더 적합한 경우 관리자 HTTP RPC Plugin을 사용해야 합니다.

    운영자 범위 헤더

    신뢰할 수 있는 프록시 인증은 ID를 포함하는 HTTP 모드이므로 호출자는 HTTP API 요청에 x-openclaw-scopes를 사용하여 운영자 범위를 선택적으로 선언할 수 있습니다.

    참고: WebSocket 범위는 Gateway 프로토콜 핸드셰이크와 기기 ID 바인딩에 의해 결정됩니다. Control UI WebSocket 업그레이드 요청에서 x-openclaw-scopes는 협상된 세션 범위의 상한일 뿐 범위를 부여하지 않습니다. Control UI 페어링 동작을 참조하세요.

    예:

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

    동작:

    • 헤더가 있으면 OpenClaw는 선언된 범위 집합을 적용합니다.
    • 헤더가 있지만 비어 있으면 요청은 운영자 범위가 없음을 선언합니다.
    • 헤더가 없으면 일반적인 ID 포함 HTTP API는 표준 운영자 기본 범위 집합(operator.admin, operator.read, operator.write, operator.approvals, operator.pairing, operator.talk.secrets)으로 폴백합니다.
    • Gateway 인증 Plugin HTTP 경로는 기본적으로 더 제한적입니다. x-openclaw-scopes가 없으면 런타임 범위는 operator.write로만 폴백합니다.
    • 브라우저 출처 HTTP 요청은 신뢰할 수 있는 프록시 인증에 성공한 후에도 gateway.controlUi.allowedOrigins(또는 의도적으로 설정한 Host 헤더 폴백 모드)를 통과해야 합니다.

    실용적인 규칙: 신뢰할 수 있는 프록시 요청을 기본값보다 제한하려는 경우 또는 Gateway 인증 Plugin 경로에 쓰기 범위보다 강한 권한이 필요한 경우 x-openclaw-scopes를 명시적으로 전송하세요.

    TLS 종단과 HSTS

    TLS 종단 지점을 하나만 사용하고 그곳에서 HSTS를 적용하세요.

    프록시 TLS 종단(권장)

    리버스 프록시가 https://control.example.com의 HTTPS를 처리하는 경우 해당 도메인에 대해 프록시에서 Strict-Transport-Security를 설정하세요.

    • 인터넷에 노출되는 배포에 적합합니다.
    • 인증서와 HTTP 보안 강화 정책을 한곳에서 관리합니다.
    • OpenClaw는 프록시 뒤에서 loopback HTTP로 유지할 수 있습니다.

    헤더 값 예:

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

    Gateway TLS 종단

    OpenClaw 자체에서 HTTPS를 직접 제공하는 경우(TLS 종단 프록시 없음) 다음과 같이 설정하세요.

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

    strictTransportSecurity는 문자열 헤더 값을 허용하며, 명시적으로 비활성화하려면 false를 사용할 수 있습니다.

    출시 적용 지침

    • 트래픽을 검증하는 동안에는 먼저 짧은 최대 기간(예: max-age=300)으로 시작하세요.
    • 충분히 확신한 후에만 장기 값(예: max-age=31536000)으로 늘리세요.
    • 모든 하위 도메인이 HTTPS를 사용할 준비가 된 경우에만 includeSubDomains를 추가하세요.
    • 전체 도메인 집합에 대한 사전 로드 요구 사항을 의도적으로 충족하는 경우에만 사전 로드를 사용하세요.
    • loopback 전용 로컬 개발에는 HSTS가 도움이 되지 않습니다.

    프록시 설정 예

    Pomerium

    Pomerium은 x-pomerium-claim-email(또는 다른 클레임 헤더)에 ID를 전달하고 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: true
    OAuth를 사용하는 Caddy

    caddy-security Plugin을 사용하는 Caddy는 사용자를 인증하고 ID 헤더를 전달할 수 있습니다.

    json5
    {  gateway: {    bind: "lan",    trustedProxies: ["10.0.0.1"], // Caddy/사이드카 프록시 IP    auth: {      mode: "trusted-proxy",      trustedProxy: {        userHeader: "x-forwarded-user",      },    },  },}

    Caddyfile 스니펫:

    caddy
    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
    json5
    {  gateway: {    bind: "lan",    trustedProxies: ["172.17.0.1"], // Traefik 컨테이너 IP    auth: {      mode: "trusted-proxy",      trustedProxy: {        userHeader: "x-forwarded-user",      },    },  },}

    혼합 토큰 구성

    공유 토큰(gateway.auth.token 또는 OPENCLAW_GATEWAY_TOKEN)도 구성되어 있으면 Gateway 시작 시 신뢰 프록시 인증을 거부합니다. 공유 토큰을 사용하면 동일 호스트 호출자가 이 모드에서 강제하려는 프록시 검증 신원과 완전히 다른 경로로 인증할 수 있으므로 두 방식은 상호 배타적입니다.

    시작 시 gateway auth mode is trusted-proxy, but a shared token is also configured와 같은 오류가 발생하면 다음 중 하나를 수행하세요.

    • 신뢰 프록시 모드를 사용할 때 공유 토큰을 제거하거나
    • 토큰 기반 인증을 사용하려는 경우 gateway.auth.mode"token"으로 변경합니다.

    local loopback 신뢰 프록시 신원 헤더도 실패 시 차단됩니다. 동일 호스트 호출자는 프록시 사용자로 자동 인증되지 않습니다. 프록시를 우회하는 내부 OpenClaw 호출자는 대신 gateway.auth.password / OPENCLAW_GATEWAY_PASSWORD로 인증할 수 있습니다. 신뢰 프록시 모드에서는 토큰 대체 인증을 의도적으로 지원하지 않습니다.

    보안 체크리스트

    신뢰 프록시 인증을 활성화하기 전에 다음을 확인하세요.

    • [ ] 프록시가 유일한 경로임: Gateway 포트는 프록시를 제외한 모든 대상에 대해 방화벽으로 차단되어 있습니다.
    • [ ] trustedProxies가 최소 범위임: 전체 서브넷이 아니라 실제 프록시 IP만 포함합니다.
    • [ ] local loopback 프록시 소스를 의도적으로 사용함: 동일 호스트 프록시를 위해 gateway.auth.trustedProxy.allowLoopback을 명시적으로 활성화하지 않으면 local loopback 소스 요청의 신뢰 프록시 인증은 실패 시 차단됩니다.
    • [ ] 프록시가 헤더를 제거함: 프록시는 클라이언트의 x-forwarded-* 헤더에 값을 추가하지 않고 덮어씁니다.
    • [ ] TLS 종료: 프록시가 TLS를 처리하며 사용자는 HTTPS로 연결합니다.
    • [ ] allowedOrigins가 명시적임: local loopback이 아닌 Control UI는 명시적인 gateway.controlUi.allowedOrigins를 사용합니다.
    • [ ] allowUsers가 설정됨(권장): 인증된 모든 사용자를 허용하지 않고 알려진 사용자로 제한합니다.
    • [ ] 혼합 토큰 구성이 없음: gateway.auth.tokengateway.auth.mode: "trusted-proxy"를 함께 설정하지 마세요.
    • [ ] 로컬 비밀번호 대체 인증이 비공개임: 내부 직접 호출자를 위해 gateway.auth.password를 구성하는 경우, 프록시를 거치지 않는 원격 클라이언트가 Gateway 포트에 직접 접근할 수 없도록 방화벽으로 차단하세요.

    보안 감사

    openclaw security audit는 신뢰 프록시 인증을 심각 심각도의 발견 사항으로 표시합니다. 이는 의도된 동작이며, 보안을 프록시 설정에 위임하고 있음을 상기시키기 위한 것입니다.

    감사에서는 다음을 확인합니다.

    • 기본 gateway.trusted_proxy_auth 경고/심각 알림.
    • trustedProxies 구성 누락.
    • userHeader 구성 누락.
    • 비어 있는 allowUsers(인증된 모든 사용자 허용).
    • 동일 호스트 프록시 소스에 대해 활성화된 allowLoopback.

    Control UI가 노출될 때는 신뢰 프록시와 별개인 발견 사항도 적용됩니다. 여기에는 와일드카드이거나 누락된 gateway.controlUi.allowedOrigins와 Host 헤더 출처 대체 동작이 포함됩니다.

    문제 해결

    trusted_proxy_untrusted_source

    요청이 gateway.trustedProxies의 IP에서 오지 않았습니다. 다음을 확인하세요.

    • 프록시 IP가 올바릅니까? (Docker 컨테이너 IP는 변경될 수 있습니다.)
    • 프록시 앞에 로드 밸런서가 있습니까?
    • 실제 IP를 확인하려면 docker inspect 또는 kubectl get pods -o wide를 사용하세요.
    trusted_proxy_loopback_source

    OpenClaw가 local loopback 소스의 신뢰 프록시 요청을 거부했습니다.

    다음을 확인하세요.

    • 프록시가 127.0.0.1 / ::1에서 연결하고 있습니까?
    • 동일 호스트의 local loopback 역방향 프록시에서 신뢰 프록시 인증을 사용하려고 합니까?

    해결 방법:

    • 프록시를 통하지 않는 내부 동일 호스트 클라이언트에는 토큰/비밀번호 인증을 사용하는 것이 좋습니다. 또는
    • local loopback이 아닌 신뢰할 수 있는 프록시 주소를 통해 라우팅하고 해당 IP를 gateway.trustedProxies에 유지합니다. 또는
    • 의도적으로 동일 호스트 역방향 프록시를 사용하는 경우 gateway.auth.trustedProxy.allowLoopback = true를 설정하고, local loopback 주소를 gateway.trustedProxies에 유지하며, 프록시가 신원 헤더를 제거하거나 덮어쓰는지 확인합니다.
    trusted_proxy_local_interface_source / trusted_proxy_local_interface_check_failed

    요청의 소스 IP가 프록시가 아니라 Gateway 호스트 자체의 local loopback이 아닌 네트워크 인터페이스 주소 중 하나와 일치했습니다. 이는 tailnet 또는 Docker 브리지 네트워크에서 동일 호스트 트래픽을 위조하는 것을 방지하기 위한 보호 장치입니다. ..._check_failed는 인터페이스 검색 자체에서 오류가 발생했음을 의미하므로 OpenClaw는 실패 시 차단합니다.

    다음을 확인하세요.

    • Gateway 호스트 자체의 프로세스가 프록시를 우회하여 신원 헤더를 직접 전송하고 있습니까?
    • 프록시가 Gateway와 동일한 네트워크 네임스페이스에서 실행되며 로컬 인터페이스에도 표시되는 IP를 사용합니까?

    해결 방법: Gateway 호스트에도 로컬로 바인딩된 주소가 아닌 주소를 통해 프록시 트래픽을 라우팅하거나, 실제 동일 호스트 프록시 설정에만 allowLoopback을 사용하세요.

    trusted_proxy_user_missing

    사용자 헤더가 비어 있거나 누락되었습니다. 다음을 확인하세요.

    • 프록시가 신원 헤더를 전달하도록 구성되어 있습니까?
    • 헤더 이름이 올바릅니까? (대소문자는 구분하지 않지만 철자는 정확해야 합니다.)
    • 사용자가 실제로 프록시에서 인증되었습니까?
    trusted_proxy_missing_header_*

    필수 헤더가 없습니다. 다음을 확인하세요.

    • 해당 특정 헤더에 대한 프록시 구성.
    • 체인의 어딘가에서 헤더가 제거되고 있는지 여부.
    trusted_proxy_user_not_allowed

    사용자가 인증되었지만 allowUsers에 없습니다. 사용자를 추가하거나 허용 목록을 제거하세요.

    trusted_proxy_no_proxies_configured / trusted_proxy_config_missing

    gateway.auth.mode"trusted-proxy"이지만 gateway.trustedProxies가 비어 있거나 gateway.auth.trustedProxy 자체가 누락되었습니다. 둘 다 설정될 때까지 모든 요청이 거부됩니다.

    trusted_proxy_origin_not_allowed

    신뢰 프록시 인증은 성공했지만 브라우저의 Origin 헤더가 Control UI 출처 검사를 통과하지 못했습니다.

    다음을 확인하세요.

    • gateway.controlUi.allowedOrigins에 정확한 브라우저 출처가 포함되어 있습니다.
    • 모든 출처 허용 동작을 의도하지 않는 한 와일드카드 출처에 의존하지 않습니다.
    • Host 헤더 대체 모드를 의도적으로 사용하는 경우 gateway.controlUi.dangerouslyAllowHostHeaderOriginFallback=true가 의도적으로 설정되어 있습니다.
    연결은 성공하지만 메서드에서 범위 누락을 보고함

    WebSocket은 연결되지만 chat.history, sessions.list 또는 models.listmissing scope: operator.read 오류로 실패합니다.

    일반적인 원인:

    • 기기 없는 Control UI 세션: 신뢰 프록시 인증은 기기 신원 없이 WebSocket 연결을 허용할 수 있지만, OpenClaw는 설계상 기기 없는 세션의 범위를 제거합니다.
    • 사용자 지정 백엔드 클라이언트: gateway.controlUi.dangerouslyDisableDeviceAuth는 Control UI 범위에만 적용되며 임의의 백엔드 또는 CLI 형태 WebSocket 클라이언트에 범위를 부여하지 않습니다.
    • 지나치게 제한적인 x-openclaw-scopes: 프록시가 Control UI WebSocket 업그레이드 요청에 이 헤더를 주입하면 세션 범위가 해당 집합으로 제한됩니다. 헤더 값이 비어 있으면 범위가 부여되지 않습니다.

    해결 방법:

    • Control UI의 경우 브라우저가 기기 신원을 생성하고 페어링을 완료할 수 있도록 HTTPS를 사용하세요.
    • 사용자 지정 자동화에는 기기 신원/페어링, 예약된 직접 로컬 gateway-client 백엔드 도우미 경로 또는 관리자 HTTP RPC를 사용하세요.
    • gateway.controlUi.dangerouslyDisableDeviceAuth: true는 Control UI의 임시 비상 접근 경로로만 사용하세요.
    WebSocket이 계속 실패함

    프록시가 다음을 충족하는지 확인하세요.

    • WebSocket 업그레이드(Upgrade: websocket, Connection: upgrade)를 지원합니다.
    • HTTP뿐만 아니라 WebSocket 업그레이드 요청에도 신원 헤더를 전달합니다.
    • WebSocket 연결에 별도의 인증 경로를 사용하지 않습니다.

    토큰 인증에서 마이그레이션

  • 프록시 구성

    사용자를 인증하고 헤더를 전달하도록 프록시를 구성합니다.

  • 프록시를 독립적으로 테스트

    프록시 설정을 독립적으로 테스트합니다(헤더를 포함한 curl).

  • OpenClaw 구성 업데이트

    신뢰 프록시 인증을 사용하도록 OpenClaw 구성을 업데이트합니다.

  • Gateway 다시 시작

    Gateway를 다시 시작합니다.

  • WebSocket 테스트

    Control UI에서 WebSocket 연결을 테스트합니다.

  • 감사

    openclaw security audit를 실행하고 발견 사항을 검토합니다.

  • 관련 항목

    Was this useful?
    On this page

    On this page