원격 액세스 (SSH, 터널, tailnet)
이 리포지토리는 전용 호스트(데스크톱/서버)에서 단일 Gateway(마스터)를 실행하고 클라이언트를 여기에 연결하는 방식으로 “SSH를 통한 원격”을 지원합니다.- **운영자(사용자 / macOS 앱)**의 경우: SSH 터널링이 범용 fallback입니다.
- **node(iOS/Android 및 향후 디바이스)**의 경우: 필요에 따라 WebSocket Gateway(LAN/tailnet 또는 SSH 터널)를 통해 연결합니다.
핵심 개념
- Gateway WebSocket은 구성된 포트(기본값 18789)에서 loopback에 바인딩됩니다.
- 원격 사용 시에는 이 loopback 포트를 SSH로 포워딩합니다(또는 tailnet/VPN을 사용해 터널 의존도를 줄일 수 있습니다).
일반적인 VPN/tailnet 설정(agent가 있는 위치)
Gateway 호스트를 “agent가 있는 곳”이라고 생각하세요. 이 호스트가 세션, auth profile, 채널, 상태를 소유합니다. 노트북/데스크톱(및 node)은 이 호스트에 연결합니다.1) tailnet 내 항상 켜져 있는 Gateway(VPS 또는 홈 서버)
지속적으로 실행되는 호스트에서 Gateway를 실행하고 Tailscale 또는 SSH를 통해 접근합니다.- 최상의 UX:
gateway.bind: "loopback"을 유지하고 Control UI에는 Tailscale Serve를 사용합니다. - Fallback: loopback을 유지하고 액세스가 필요한 모든 머신에서 SSH 터널을 사용합니다.
- 예시: exe.dev (쉬운 VM) 또는 Hetzner (프로덕션 VPS).
2) 집의 데스크톱에서 Gateway를 실행하고, 노트북은 원격 제어만 수행
노트북은 agent를 실행하지 않습니다. 원격으로 연결만 합니다.- macOS 앱의 Remote over SSH 모드를 사용하세요(Settings → General → “OpenClaw runs”).
- 앱이 터널을 열고 관리하므로 WebChat + 상태 점검이 “그냥 작동”합니다.
3) 노트북에서 Gateway를 실행하고, 다른 머신에서 원격 액세스
Gateway는 로컬에 유지하면서 안전하게 노출합니다.- 다른 머신에서 노트북으로 SSH 터널을 열거나
- Tailscale Serve로 Control UI를 노출하고 Gateway는 loopback 전용으로 유지합니다.
명령 흐름(무엇이 어디에서 실행되는지)
하나의 gateway 서비스가 상태 + 채널을 소유합니다. node는 주변 장치입니다. 흐름 예시(Telegram → node):- Telegram 메시지가 Gateway에 도착합니다.
- Gateway가 agent를 실행하고 node 도구를 호출할지 결정합니다.
- Gateway가 Gateway WebSocket(
node.*RPC)을 통해 node를 호출합니다. - Node가 결과를 반환하고, Gateway가 Telegram으로 다시 응답합니다.
- Node는 gateway 서비스를 실행하지 않습니다. 의도적으로 격리된 profile을 실행하는 경우가 아니라면 호스트당 gateway는 하나만 실행해야 합니다(Multiple gateways 참조).
- macOS 앱의 “node mode”는 Gateway WebSocket을 통한 node 클라이언트일 뿐입니다.
SSH 터널(CLI + 도구)
원격 Gateway WS로 로컬 터널을 생성합니다.openclaw health와openclaw status --deep가 이제ws://127.0.0.1:18789를 통해 원격 gateway에 도달합니다.openclaw gateway status,openclaw gateway health,openclaw gateway probe,openclaw gateway call도 필요할 때--url로 포워딩된 URL을 대상으로 사용할 수 있습니다.
18789는 구성된 gateway.port(또는 --port/OPENCLAW_GATEWAY_PORT)로 바꾸세요.
참고: --url을 전달하면 CLI는 config 또는 환경 자격 증명으로 fallback하지 않습니다.
--token 또는 --password를 명시적으로 포함하세요. 명시적 자격 증명이 없으면 오류입니다.
CLI 원격 기본값
CLI 명령이 기본적으로 사용하도록 원격 대상을 저장할 수 있습니다.ws://127.0.0.1:18789로 유지하고 먼저 SSH 터널을 여세요.
자격 증명 우선순위
Gateway 자격 증명 해상은 call/probe/status 경로와 Discord exec-approval 모니터링 전반에 걸쳐 하나의 공통 계약을 따릅니다. Node-host는 같은 기본 계약을 사용하되 로컬 모드 예외 하나가 있습니다(gateway.remote.*를 의도적으로 무시함).
- 명시적 자격 증명(
--token,--password, 또는 도구gatewayToken)은 명시적 auth를 허용하는 call 경로에서 항상 우선합니다. - URL 재정의 안전 규칙:
- CLI URL 재정의(
--url)는 암묵적인 config/env 자격 증명을 절대 재사용하지 않습니다. - Env URL 재정의(
OPENCLAW_GATEWAY_URL)는 env 자격 증명만 사용할 수 있습니다(OPENCLAW_GATEWAY_TOKEN/OPENCLAW_GATEWAY_PASSWORD).
- CLI URL 재정의(
- 로컬 모드 기본값:
- token:
OPENCLAW_GATEWAY_TOKEN->gateway.auth.token->gateway.remote.token(원격 fallback은 로컬 auth token 입력이 설정되지 않은 경우에만 적용) - password:
OPENCLAW_GATEWAY_PASSWORD->gateway.auth.password->gateway.remote.password(원격 fallback은 로컬 auth password 입력이 설정되지 않은 경우에만 적용)
- token:
- 원격 모드 기본값:
- token:
gateway.remote.token->OPENCLAW_GATEWAY_TOKEN->gateway.auth.token - password:
OPENCLAW_GATEWAY_PASSWORD->gateway.remote.password->gateway.auth.password
- token:
- Node-host 로컬 모드 예외:
gateway.remote.token/gateway.remote.password는 무시됩니다. - 원격 probe/status 토큰 검사는 기본적으로 엄격합니다. 원격 모드를 대상으로 할 때는
gateway.remote.token만 사용합니다(로컬 토큰 fallback 없음). - Gateway env 재정의는
OPENCLAW_GATEWAY_*만 사용합니다.
SSH를 통한 채팅 UI
WebChat은 더 이상 별도의 HTTP 포트를 사용하지 않습니다. SwiftUI 채팅 UI는 직접 Gateway WebSocket에 연결됩니다.- SSH로
18789를 포워딩한 다음(위 참조), 클라이언트를ws://127.0.0.1:18789에 연결하세요. - macOS에서는 앱의 “Remote over SSH” 모드를 사용하는 것이 좋습니다. 이 모드는 터널을 자동으로 관리합니다.
macOS 앱 “Remote over SSH”
macOS 메뉴 바 앱은 동일한 설정을 엔드투엔드로 처리할 수 있습니다(원격 상태 점검, WebChat, Voice Wake 포워딩). 실행 가이드: macOS 원격 액세스.보안 규칙(원격/VPN)
짧게 말하면: 반드시 필요하다고 확신하지 않는 한 Gateway는 loopback 전용으로 유지하세요.- Loopback + SSH/Tailscale Serve가 가장 안전한 기본값입니다(공개 노출 없음).
- 평문
ws://는 기본적으로 loopback 전용입니다. 신뢰할 수 있는 사설 네트워크에서는 비상 우회용으로 클라이언트 프로세스에OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1을 설정하세요. - 비-loopback 바인딩(
lan/tailnet/custom, 또는 loopback을 사용할 수 없을 때의auto)은 gateway auth를 사용해야 합니다: token, password 또는gateway.auth.mode: "trusted-proxy"를 사용하는 identity-aware 리버스 프록시. gateway.remote.token/.password는 클라이언트 자격 증명 소스입니다. 이것만으로 서버 auth를 구성하지는 않습니다.- 로컬 call 경로는
gateway.auth.*가 설정되지 않은 경우에만gateway.remote.*를 fallback으로 사용할 수 있습니다. gateway.auth.token/gateway.auth.password가 SecretRef로 명시적으로 구성되어 있는데 확인되지 않으면, 해상은 안전하게 실패합니다(원격 fallback이 이를 가리지 않음).gateway.remote.tlsFingerprint는wss://사용 시 원격 TLS 인증서를 pinning합니다.- Tailscale Serve는
gateway.auth.allowTailscale: true일 때 identity 헤더를 통해 Control UI/WebSocket 트래픽을 인증할 수 있습니다. HTTP API 엔드포인트는 이 Tailscale 헤더 auth를 사용하지 않으며 대신 gateway의 일반 HTTP auth 모드를 따릅니다. 이 토큰 없는 흐름은 gateway 호스트가 신뢰된다는 것을 전제로 합니다. 어디서나 공유 시크릿 auth를 원한다면 이를false로 설정하세요. - Trusted-proxy auth는 비-loopback identity-aware 프록시 설정 전용입니다.
동일 호스트의 loopback 리버스 프록시는
gateway.auth.mode: "trusted-proxy"조건을 충족하지 않습니다. - browser 제어는 운영자 액세스처럼 취급하세요: tailnet 전용 + 의도적인 node 페어링.
macOS: LaunchAgent를 통한 영구 SSH 터널
원격 gateway에 연결하는 macOS 클라이언트의 경우, 가장 쉬운 영구 설정은 SSHLocalForward config 항목과 LaunchAgent를 사용해 재부팅과 충돌 후에도 터널을 유지하는 것입니다.
1단계: SSH config 추가
~/.ssh/config를 편집하세요.
<REMOTE_IP>와 <REMOTE_USER>를 실제 값으로 바꾸세요.
2단계: SSH 키 복사(1회)
3단계: gateway 토큰 구성
재시작 후에도 유지되도록 config에 토큰을 저장하세요.4단계: LaunchAgent 생성
다음을~/Library/LaunchAgents/ai.openclaw.ssh-tunnel.plist로 저장하세요.
5단계: LaunchAgent 로드
com.openclaw.ssh-tunnel LaunchAgent가 있다면, 언로드하고 삭제하세요.
문제 해결
터널이 실행 중인지 확인:| Config 항목 | 동작 |
|---|---|
LocalForward 18789 127.0.0.1:18789 | 로컬 포트 18789를 원격 포트 18789로 포워딩 |
ssh -N | 원격 명령을 실행하지 않는 SSH(포트 포워딩 전용) |
KeepAlive | 터널이 충돌하면 자동으로 재시작 |
RunAtLoad | 로그인 시 LaunchAgent가 로드되면 터널 시작 |