콘텐츠로 이동

OpenClaw Trusted Proxy 인증 설정: 보안 가이드

개발을 하다 보면 인증 시스템을 관리하는 게 참 번거로울 때가 많아요. 특히 리버스 프록시 뒤에서 서비스를 운영할 때, 중복으로 인증을 처리하거나 브라우저 제약 때문에 WebSocket 연결이 자꾸 끊기는 문제를 겪어보셨을 거예요. 이런 고민을 해결하고 보안 설정을 간소화할 수 있는 Trusted Proxy Auth 모드에 대해 자세히 알아볼게요.

⚠️ 보안 주의 사항. 이 모드는 인증 권한을 리버스 프록시에 완전히 위임합니다. 설정을 잘못하면 Gateway가 무단 액세스에 노출될 수 있어요. 활성화하기 전에 이 페이지를 꼼꼼히 읽어주세요.

다음과 같은 상황에서 trusted-proxy 인증 모드를 사용하세요.

  • Pomerium, Caddy + OAuth, nginx + oauth2-proxy, Traefik + forward auth 같은 ID 인식 프록시(identity-aware proxy) 뒤에서 OpenClaw를 실행할 때
  • 프록시가 모든 인증을 처리하고 헤더를 통해 사용자 ID를 전달할 때
  • 프록시가 Gateway로 가는 유일한 경로인 Kubernetes 또는 컨테이너 환경일 때
  • 브라우저가 WebSocket 페이로드에 토큰을 담아 보낼 수 없어 발생하는 WebSocket 1008 unauthorized 에러를 해결해야 할 때
  • 프록시가 사용자를 인증하지 않는 경우 (단순한 TLS 터미네이터나 로드 밸런서인 경우)
  • 방화벽 구멍이나 내부 네트워크 접속 등 프록시를 거치지 않고 Gateway에 접속할 수 있는 경로가 있는 경우
  • 프록시가 전달된 헤더를 올바르게 제거하거나 덮어쓰는지 확실하지 않은 경우
  • 개인적인 단일 사용자 접속만 필요한 경우 (더 간단한 설정을 위해 Tailscale Serve + loopback 조합을 고려해 보세요)
  1. 리버스 프록시가 사용자 인증을 처리합니다 (OAuth, OIDC, SAML 등).
  2. 프록시가 인증된 사용자 ID가 담긴 헤더를 추가합니다 (예: x-forwarded-user: nick@example.com).
  3. OpenClaw는 요청이 신뢰할 수 있는 프록시 IP(gateway.trustedProxies에 설정된 IP)에서 왔는지 확인합니다.
  4. OpenClaw가 설정된 헤더에서 사용자 ID를 추출합니다.
  5. 모든 확인이 끝나면 요청이 승인됩니다.

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

참고 사항:

  • 이 모드에서는 페어링이 Control UI 접속을 위한 기본 관문이 아니게 됩니다.
  • 리버스 프록시의 인증 정책과 allowUsers 설정이 실질적인 액세스 제어 역할을 합니다.
  • Gateway 인그레스(ingress)를 신뢰할 수 있는 프록시 IP로만 제한하세요 (gateway.trustedProxies + 방화벽 설정).
{
gateway: {
// Use loopback for same-host proxy setups; use lan/custom for remote proxy hosts
bind: "loopback",
// 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"],
},
},
},
}

만약 gateway.bind가 loopback이라면, gateway.trustedProxies에 루프백 프록시 주소(127.0.0.1, ::1 또는 이에 상응하는 루프백 CIDR)를 포함해야 합니다.

필드필수 여부설명
gateway.trustedProxies예신뢰할 수 있는 프록시 IP 주소 배열입니다. 다른 IP의 요청은 거부됩니다.
gateway.auth.mode예반드시 "trusted-proxy"여야 합니다.
gateway.auth.trustedProxy.userHeader예인증된 사용자 ID가 포함된 헤더 이름입니다.
gateway.auth.trustedProxy.requiredHeaders아니요요청을 신뢰하기 위해 반드시 존재해야 하는 추가 헤더입니다.
gateway.auth.trustedProxy.allowUsers아니요허용된 사용자 ID 목록입니다. 비어 있으면 인증된 모든 사용자를 허용합니다.

하나의 TLS 터미네이션 지점을 사용하고 거기에서 HSTS를 적용하세요.

권장 패턴: 프록시 TLS 터미네이션

섹션 제목: “권장 패턴: 프록시 TLS 터미네이션”

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

  • 외부로 노출되는 배포 환경에 적합합니다.
  • 인증서와 HTTP 보안 정책을 한 곳에서 관리할 수 있습니다.
  • OpenClaw는 프록시 뒤에서 루프백 HTTP 상태를 유지할 수 있습니다.

헤더 값 예시:

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

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를 사용하세요.
  • 루프백 전용 로컬 개발 환경에서는 HSTS의 이점이 없습니다.

Pomerium은 x-pomerium-claim-email(또는 다른 클레임 헤더)에 ID를 담아 보내고, 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: nick@example.com
pass_identity_headers: true

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

{
gateway: {
bind: "lan",
trustedProxies: ["127.0.0.1"], // Caddy's IP (if on same host)
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}
}
}

oauth2-proxy는 사용자를 인증하고 x-auth-request-email에 ID를 담아 보냅니다.

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

OpenClaw는 gateway.auth.token(또는 OPENCLAW_GATEWAY_TOKEN)과 trusted-proxy 모드가 동시에 활성화된 모호한 설정을 거부합니다. 토큰 설정이 혼합되면 루프백 요청이 잘못된 인증 경로로 자동 인증될 위험이 있기 때문이에요.

시작할 때 mixed_trusted_proxy_token 에러가 발생한다면 다음을 확인하세요.

  • trusted-proxy 모드를 사용할 때는 공유 토큰을 제거하세요.
  • 토큰 기반 인증을 사용하려면 gateway.auth.mode를 "token"으로 변경하세요.

루프백 trusted-proxy 인증도 엄격하게 제한됩니다. 동일 호스트의 호출자라도 자동으로 인증되지 않으며, 반드시 신뢰할 수 있는 프록시를 통해 설정된 ID 헤더를 제공해야 합니다.

trusted-proxy 인증을 활성화하기 전에 다음 사항을 확인하세요.

  • 프록시가 유일한 경로인가: Gateway 포트가 프록시를 제외한 모든 곳으로부터 방화벽으로 차단되어 있는지 확인하세요.
  • trustedProxies가 최소화되었는가: 전체 서브넷이 아닌 실제 프록시 IP만 등록하세요.
  • 프록시가 헤더를 제거하는가: 프록시가 클라이언트의 x-forwarded-* 헤더를 덧붙이지 않고 덮어쓰는지 확인하세요.
  • TLS 터미네이션: 프록시가 TLS를 처리하고 사용자가 HTTPS로 접속하는지 확인하세요.
  • allowUsers 설정 (권장): 인증된 누구에게나 허용하기보다 알려진 사용자로 제한하세요.
  • 혼합 토큰 설정 없음: gateway.auth.token과 gateway.auth.mode: "trusted-proxy"를 동시에 설정하지 마세요.

openclaw security audit을 실행하면 trusted-proxy 인증이 critical 등급의 보안 이슈로 표시될 거예요. 이는 오류가 아니라, 보안 책임을 프록시 설정에 위임하고 있다는 사실을 상기시키기 위한 의도적인 디자인입니다.

감사 항목은 다음과 같습니다.

  • trustedProxies 설정 누락
  • userHeader 설정 누락
  • allowUsers가 비어 있음 (인증된 모든 사용자 허용)

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

  • 프록시 IP가 정확한가요? (Docker 컨테이너 IP는 변경될 수 있어요)
  • 프록시 앞에 로드 밸런서가 또 있나요?
  • docker inspect 또는 kubectl get pods -o wide 명령어로 실제 IP를 찾아보세요.

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

  • 프록시가 ID 헤더를 전달하도록 설정되어 있나요?
  • 헤더 이름이 정확한가요? (대소문자를 구분하지 않지만 철자는 중요해요)
  • 사용자가 프록시에서 실제로 인증되었나요?

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

  • 해당 헤더에 대한 프록시 설정을 확인하세요.
  • 연결 체인 어딘가에서 헤더가 제거되고 있지 않은지 확인하세요.

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

프록시가 다음 사항을 지원하는지 확인하세요.

  • WebSocket 업그레이드 지원 (Upgrade: websocket, Connection: upgrade)
  • HTTP 요청뿐만 아니라 WebSocket 업그레이드 요청 시에도 ID 헤더 전달
  • WebSocket 연결에 대해 별도의 인증 경로를 사용하고 있지 않은지 확인

토큰 인증에서 trusted-proxy로 전환하려면 다음 순서를 따르세요.

  1. 사용자를 인증하고 헤더를 전달하도록 프록시를 설정합니다.
  2. 프록시 설정을 독립적으로 테스트합니다 (헤더를 포함한 curl 사용).
  3. OpenClaw 설정을 trusted-proxy 인증으로 업데이트합니다.
  4. Gateway를 재시작합니다.
  5. Control UI에서 WebSocket 연결을 테스트합니다.
  6. openclaw security audit을 실행하고 결과를 검토합니다.
  • 보안 — 전체 보안 가이드
  • 설정 — 설정 참조
  • 원격 접속 — 기타 원격 접속 패턴
  • Tailscale — Tailnet 전용 접속을 위한 더 간단한 대안

설정 중에 도움이 필요하시다면 AI Setup Assistant에게 물어보세요!

OpenClaw

OpenClaw Expert

아직 막혀 있나요?

이 문서에서 답을 못 찾았다면 OpenClaw Expert에게 바로 물어보세요.