Exec 승인
Exec 승인은 샌드박스된 에이전트가 실제 호스트(gateway 또는 node)에서 명령을 실행할 수 있게 하는 컴패니언 앱 / 노드 호스트 안전장치입니다. 안전 인터록이라고 생각하면 됩니다: 정책 + 허용 목록 + (선택적) 사용자 승인이 모두 동의할 때만 명령이 허용됩니다.
Exec 승인은 도구 정책 및 elevated 게이팅에 추가로 적용됩니다 (elevated가 full로 설정된 경우 승인을 건너뜁니다).
유효 정책은 tools.exec.*와 승인 기본값 중 더 엄격한 것이며, 승인 필드가 생략되면 tools.exec 값이 사용됩니다.
컴패니언 앱 UI가 사용 불가능한 경우, 프롬프트가 필요한 모든 요청은 ask 폴백 (기본값: deny)에 의해 처리됩니다.
적용 범위
Exec 승인은 실행 호스트에서 로컬로 적용됩니다:
- 게이트웨이 호스트 → 게이트웨이 머신의
openclaw프로세스 - 노드 호스트 → 노드 러너 (macOS 컴패니언 앱 또는 헤드리스 노드 호스트)
신뢰 모델 참고:
- 게이트웨이 인증된 호출자는 해당 게이트웨이의 신뢰된 운영자입니다.
- 페어링된 노드는 해당 신뢰된 운영자 기능을 노드 호스트로 확장합니다.
- Exec 승인은 우발적 실행 위험을 줄이지만, 사용자별 인증 경계는 아닙니다.
- 승인된 노드 호스트 실행은 정규 실행 컨텍스트(정규 cwd, 정확한 argv, 존재 시 env 바인딩, 해당 시 고정된 실행 파일 경로)를 바인딩합니다.
- 셸 스크립트 및 직접 인터프리터/런타임 파일 호출의 경우, OpenClaw는 하나의 구체적인 로컬 파일 피연산자를 바인딩하려고 시도합니다. 바인딩된 파일이 승인 후 실행 전에 변경되면, 변경된 내용을 실행하는 대신 실행이 거부됩니다.
- 이 파일 바인딩은 의도적으로 최선 노력이며, 모든 인터프리터/런타임 로더 경로의 완전한 의미론적 모델이 아닙니다. 승인 모드가 바인딩할 정확히 하나의 구체적 로컬 파일을 식별할 수 없으면, 완전한 커버리지인 것처럼 가장하는 대신 승인 기반 실행을 거부합니다.
macOS 분할:
- 노드 호스트 서비스가 로컬 IPC를 통해
system.run을 macOS 앱으로 전달합니다. - macOS 앱이 UI 컨텍스트에서 승인을 적용하고 명령을 실행합니다.
설정 및 저장소
승인은 실행 호스트의 로컬 JSON 파일에 저장됩니다:
~/.openclaw/exec-approvals.json
예시 스키마:
{
"version": 1,
"socket": {
"path": "~/.openclaw/exec-approvals.sock",
"token": "base64url-token"
},
"defaults": {
"security": "deny",
"ask": "on-miss",
"askFallback": "deny",
"autoAllowSkills": false
},
"agents": {
"main": {
"security": "allowlist",
"ask": "on-miss",
"askFallback": "deny",
"autoAllowSkills": true,
"allowlist": [
{
"id": "B0C8C0B3-2C2D-4F8A-9A3C-5A4B3C2D1E0F",
"pattern": "~/Projects/**/bin/rg",
"lastUsedAt": 1737150000000,
"lastUsedCommand": "rg -n TODO",
"lastResolvedPath": "/Users/user/Projects/.../bin/rg"
}
]
}
}
}
정책 설정
Security (exec.security)
- deny: 모든 호스트 exec 요청을 차단.
- allowlist: 허용 목록에 있는 명령만 허용.
- full: 모든 것을 허용 (elevated와 동일).
Ask (exec.ask)
- off: 프롬프트를 표시하지 않음.
- on-miss: 허용 목록에 매칭되지 않을 때만 프롬프트 표시.
- always: 모든 명령에 프롬프트 표시.
Ask 폴백 (askFallback)
프롬프트가 필요하지만 UI에 접근할 수 없는 경우 폴백이 결정합니다:
- deny: 차단.
- allowlist: 허용 목록에 매칭되는 경우에만 허용.
- full: 허용.
허용 목록 (에이전트별)
허용 목록은 에이전트별입니다. 여러 에이전트가 있는 경우 macOS 앱에서 편집할 에이전트를 전환합니다. 패턴은 대소문자를 구분하지 않는 글롭 매칭입니다.
패턴은 바이너리 경로로 해석되어야 합니다 (basename 전용 항목은 무시됩니다).
레거시 agents.default 항목은 로드 시 agents.main으로 마이그레이션됩니다.
예시:
~/Projects/**/bin/peekaboo~/.local/bin/*/opt/homebrew/bin/rg
각 허용 목록 항목은 다음을 추적합니다:
- id: UI 식별을 위한 안정적 UUID (선택적)
- last used: 마지막 사용 타임스탬프
- last used command: 마지막 사용 명령
- last resolved path: 마지막 해석된 경로
스킬 CLI 자동 허용
Auto-allow skill CLIs가 활성화되면, 알려진 스킬이 참조하는 실행 파일이 노드(macOS 노드 또는 헤드리스 노드 호스트)에서 허용 목록으로 취급됩니다. 이는 게이트웨이 RPC를 통해 skills.bins를 사용하여 스킬 바이너리 목록을 가져옵니다. 엄격한 수동 허용 목록을 원하면 비활성화하세요.
중요한 신뢰 관련 참고:
- 이것은 수동 경로 허용 목록 항목과 별개인 암시적 편의 허용 목록입니다.
- 게이트웨이와 노드가 같은 신뢰 경계에 있는 신뢰된 운영자 환경을 위한 것입니다.
- 엄격한 명시적 신뢰가 필요하면
autoAllowSkills: false를 유지하고 수동 경로 허용 목록 항목만 사용하세요.
Safe bins (stdin 전용)
tools.exec.safeBins는 명시적 허용 목록 항목 없이도 allowlist 모드에서 실행할 수 있는 stdin 전용 바이너리(예: jq)의 소규모 목록을 정의합니다. Safe bins는 위치 파일 인수와 경로 유사 토큰을 거부하므로, 들어오는 스트림에서만 작동할 수 있습니다.
이를 스트림 필터를 위한 좁은 빠른 경로로 취급하세요. 일반 신뢰 목록이 아닙니다.
인터프리터 또는 런타임 바이너리(예: python3, node, ruby, bash, sh, zsh)를 safeBins에 추가하지 마세요.
명령이 코드를 평가하거나, 하위 명령을 실행하거나, 설계상 파일을 읽을 수 있는 경우, 명시적 허용 목록 항목을 사용하고 승인 프롬프트를 활성화하세요.
커스텀 safe bins는 tools.exec.safeBinProfiles.<bin>에 명시적 프로필을 정의해야 합니다.
검증은 argv 형태에서만 결정적으로 이루어지며 (호스트 파일시스템 존재 확인 없음), 이는 허용/거부 차이에서 파일 존재 오라클 동작을 방지합니다.
기본 safe bins에서 파일 지향 옵션은 거부됩니다 (예: sort -o, sort --output,
sort --files0-from, sort --compress-program, sort --random-source,
sort --temporary-directory/-T, wc --files0-from, jq -f/--from-file,
grep -f/--file).
Safe bins는 또한 stdin 전용 동작을 깨는 옵션에 대해 명시적 바이너리별 플래그 정책을 적용합니다 (예: sort -o/--output/--compress-program 및 grep 재귀 플래그).
long 옵션은 safe-bin 모드에서 fail-closed로 검증됩니다: 알 수 없는 플래그와 모호한 약어가 거부됩니다.
safe-bin 프로필별 거부 플래그:
grep:--dereference-recursive,--directories,--exclude-from,--file,--recursive,-R,-d,-f,-rjq:--argfile,--from-file,--library-path,--rawfile,--slurpfile,-L,-fsort:--compress-program,--files0-from,--output,--random-source,--temporary-directory,-T,-owc:--files0-from
Safe bins는 또한 실행 시 argv 토큰을 stdin 전용 세그먼트에서 리터럴 텍스트로 취급하므로 (글로빙 및 $VARS 확장 없음), * 또는 $HOME/...과 같은 패턴으로 파일 읽기를 몰래 수행할 수 없습니다.
Safe bins는 또한 신뢰된 바이너리 디렉터리(시스템 기본값 + 선택적 tools.exec.safeBinTrustedDirs)에서 해석되어야 합니다. PATH 항목은 자동으로 신뢰되지 않습니다.
기본 신뢰 safe-bin 디렉터리는 의도적으로 최소합니다: /bin, /usr/bin.
safe-bin 실행 파일이 패키지 관리자/사용자 경로(예: /opt/homebrew/bin, /usr/local/bin, /opt/local/bin, /snap/bin)에 있으면 tools.exec.safeBinTrustedDirs에 명시적으로 추가하세요.
allowlist 모드에서 셸 체이닝과 리디렉션은 자동 허용되지 않습니다.
셸 체이닝(&&, ||, ;)은 모든 최상위 세그먼트가 허용 목록을 만족할 때(safe bins 또는 스킬 자동 허용 포함) 허용됩니다. 리디렉션은 allowlist 모드에서 지원되지 않습니다.
명령 치환($() / 백틱)은 이중 따옴표 내부를 포함하여 허용 목록 파싱 중 거부됩니다. 리터럴 $() 텍스트가 필요하면 작은 따옴표를 사용하세요.
macOS 컴패니언 앱 승인에서, 셸 제어 또는 확장 구문(&&, ||, ;, |, `, $, <, >, (, ))을 포함하는 원시 셸 텍스트는 셸 바이너리 자체가 허용 목록에 없는 한 허용 목록 미스로 취급됩니다.
셸 래퍼(bash|sh|zsh ... -c/-lc)의 경우, 요청 범위 env 오버라이드는 소규모 명시적 허용 목록(TERM, LANG, LC_*, COLORTERM, NO_COLOR, FORCE_COLOR)으로 축소됩니다.
allowlist 모드에서 항상 허용 결정의 경우, 알려진 디스패치 래퍼(env, nice, nohup, stdbuf, timeout)는 래퍼 경로 대신 내부 실행 파일 경로를 저장합니다. 셸 멀티플렉서(busybox, toybox)도 셸 애플릿(sh, ash 등)에 대해 언래핑되어 멀티플렉서 바이너리 대신 내부 실행 파일이 저장됩니다. 래퍼 또는 멀티플렉서를 안전하게 언래핑할 수 없으면 허용 목록 항목이 자동으로 저장되지 않습니다.
기본 safe bins: jq, cut, uniq, head, tail, tr, wc.
grep과 sort는 기본 목록에 없습니다. 옵트인하는 경우, 비 stdin 워크플로에 대한 명시적 허용 목록 항목을 유지하세요.
safe-bin 모드에서 grep의 경우, -e/--regexp로 패턴을 제공하세요. 위치 패턴 형식은 거부되므로 파일 피연산자가 모호한 위치 인수로 몰래 전달될 수 없습니다.
Safe bins vs 허용 목록
| 항목 | tools.exec.safeBins | 허용 목록 (exec-approvals.json) |
|---|---|---|
| 목표 | 좁은 stdin 필터 자동 허용 | 특정 실행 파일을 명시적으로 신뢰 |
| 매칭 유형 | 실행 파일 이름 + safe-bin argv 정책 | 해석된 실행 파일 경로 글롭 패턴 |
| 인수 범위 | safe-bin 프로필 및 리터럴 토큰 규칙에 의해 제한 | 경로 매칭만; 인수는 사용자 책임 |
| 일반적 예시 | jq, head, tail, wc | python3, node, ffmpeg, 커스텀 CLI |
| 최적 용도 | 파이프라인 내 저위험 텍스트 변환 | 더 넓은 동작이나 부작용이 있는 모든 도구 |
설정 위치:
safeBins는 설정에서 가져옵니다 (tools.exec.safeBins또는 에이전트별agents.list[].tools.exec.safeBins).safeBinTrustedDirs는 설정에서 가져옵니다 (tools.exec.safeBinTrustedDirs또는 에이전트별agents.list[].tools.exec.safeBinTrustedDirs).safeBinProfiles는 설정에서 가져옵니다 (tools.exec.safeBinProfiles또는 에이전트별agents.list[].tools.exec.safeBinProfiles). 에이전트별 프로필 키가 전역 키를 오버라이드합니다.- 허용 목록 항목은 호스트 로컬
~/.openclaw/exec-approvals.json의agents.<id>.allowlist하위에 위치합니다 (또는 제어 UI /openclaw approvals allowlist ...를 통해). openclaw security audit는 인터프리터/런타임 바이너리가 명시적 프로필 없이safeBins에 나타나면tools.exec.safe_bins_interpreter_unprofiled로 경고합니다.openclaw doctor --fix는 누락된 커스텀safeBinProfiles.<bin>항목을{}로 스캐폴드할 수 있습니다 (이후 검토 및 강화 필요). 인터프리터/런타임 바이너리는 자동 스캐폴드되지 않습니다.
커스텀 프로필 예시:
{
tools: {
exec: {
safeBins: ["jq", "myfilter"],
safeBinProfiles: {
myfilter: {
minPositional: 0,
maxPositional: 0,
allowedValueFlags: ["-n", "--limit"],
deniedFlags: ["-f", "--file", "-c", "--command"],
},
},
},
},
}
제어 UI 편집
제어 UI → Nodes → Exec approvals 카드를 사용하여 기본값, 에이전트별 오버라이드, 허용 목록을 편집합니다. 범위(Defaults 또는 에이전트)를 선택하고, 정책을 조정하고, 허용 목록 패턴을 추가/제거한 다음 Save합니다. UI는 패턴별 last used 메타데이터를 표시하여 목록을 정리할 수 있게 합니다.
대상 선택기는 Gateway (로컬 승인) 또는 Node를 선택합니다. 노드는 system.execApprovals.get/set을 알려야 합니다 (macOS 앱 또는 헤드리스 노드 호스트). 노드가 아직 exec 승인을 알리지 않는 경우, 로컬 ~/.openclaw/exec-approvals.json을 직접 편집하세요.
CLI: openclaw approvals는 게이트웨이 또는 노드 편집을 지원합니다 (Approvals CLI 참조).
승인 흐름
프롬프트가 필요한 경우, 게이트웨이가 운영자 클라이언트에 exec.approval.requested를 브로드캐스트합니다. 제어 UI 및 macOS 앱이 exec.approval.resolve를 통해 해결한 다음, 게이트웨이가 승인된 요청을 노드 호스트로 전달합니다.
host=node의 경우, 승인 요청에 정규 systemRunPlan 페이로드가 포함됩니다. 게이트웨이는 승인된 system.run 요청을 전달할 때 해당 플랜을 권위 있는 명령/cwd/세션 컨텍스트로 사용합니다.
인터프리터/런타임 명령
승인 기반 인터프리터/런타임 실행은 의도적으로 보수적입니다:
- 정확한 argv/cwd/env 컨텍스트가 항상 바인딩됩니다.
- 직접 셸 스크립트 및 직접 런타임 파일 형식은 하나의 구체적 로컬 파일 스냅샷에 최선 노력으로 바인딩됩니다.
- 여전히 하나의 직접 로컬 파일로 해석되는 일반적인 패키지 관리자 래퍼 형식(예:
pnpm exec,pnpm node,npm exec,npx)은 바인딩 전에 언래핑됩니다. - 인터프리터/런타임 명령에 대해 정확히 하나의 구체적 로컬 파일을 식별할 수 없는 경우(예: 패키지 스크립트, eval 형식, 런타임별 로더 체인, 모호한 다중 파일 형식), 갖고 있지 않은 의미론적 커버리지를 주장하는 대신 승인 기반 실행이 거부됩니다.
- 이러한 워크플로의 경우 샌드박싱, 별도의 호스트 경계, 또는 운영자가 더 넓은 런타임 시맨틱을 수용하는 명시적 신뢰 허용 목록/full 워크플로를 선호하세요.
승인이 필요한 경우, exec 도구는 승인 ID와 함께 즉시 반환합니다. 해당 ID를 사용하여 이후 시스템 이벤트(Exec finished / Exec denied)와 연관시킵니다. 타임아웃 전에 결정이 도착하지 않으면, 요청은 승인 타임아웃으로 처리되고 거부 사유로 표시됩니다.
확인 대화 상자에 포함되는 내용:
- 명령 + 인수
- cwd
- 에이전트 ID
- 해석된 실행 파일 경로
- 호스트 + 정책 메타데이터
액션:
- Allow once → 지금 실행
- Always allow → 허용 목록에 추가 + 실행
- Deny → 차단
채팅 채널로의 승인 전달
exec 승인 프롬프트를 모든 채팅 채널(플러그인 채널 포함)로 전달하고 /approve로 승인할 수 있습니다. 이는 일반적인 아웃바운드 전달 파이프라인을 사용합니다.
설정:
{
approvals: {
exec: {
enabled: true,
mode: "session", // "session" | "targets" | "both"
agentFilter: ["main"],
sessionFilter: ["discord"], // 부분 문자열 또는 정규식
targets: [
{ channel: "slack", to: "U12345678" },
{ channel: "telegram", to: "123456789" },
],
},
},
}
채팅에서 응답:
/approve <id> allow-once
/approve <id> allow-always
/approve <id> deny
기본 제공 채팅 승인 클라이언트
Discord와 Telegram은 채널별 설정으로 명시적 exec 승인 클라이언트로도 작동할 수 있습니다.
- Discord:
channels.discord.execApprovals.* - Telegram:
channels.telegram.execApprovals.*
이러한 클라이언트는 옵트인 방식입니다. 채널에 exec 승인이 활성화되지 않으면, 해당 채널에서 대화가 발생했다는 이유만으로 OpenClaw가 그 채널을 승인 인터페이스로 취급하지 않습니다.
공유 동작:
- 구성된 승인자만 승인 또는 거부 가능
- 요청자가 반드시 승인자일 필요는 없음
- 채널 전달이 활성화된 경우, 승인 프롬프트에 명령 텍스트가 포함됨
- 운영자 UI나 구성된 승인 클라이언트가 요청을 수락할 수 없으면, 프롬프트가
askFallback으로 폴백
Telegram은 기본적으로 승인자 DM(target: "dm")을 사용합니다. 승인 프롬프트가 원래 Telegram 채팅/주제에도 나타나게 하려면 channel 또는 both로 전환할 수 있습니다. Telegram 포럼 주제의 경우, OpenClaw는 승인 프롬프트와 승인 후 후속 작업에 대해 주제를 보존합니다.
참조:
macOS IPC 흐름
Gateway -> Node Service (WS)
| IPC (UDS + token + HMAC + TTL)
v
Mac App (UI + approvals + system.run)
보안 참고:
- Unix 소켓 모드
0600, 토큰은exec-approvals.json에 저장. - 동일 UID 피어 확인.
- 챌린지/응답 (논스 + HMAC 토큰 + 요청 해시) + 짧은 TTL.
시스템 이벤트
Exec 생명주기는 시스템 메시지로 표시됩니다:
Exec running(명령이 running notice 임계값을 초과하는 경우에만)Exec finishedExec denied
이들은 노드가 이벤트를 보고한 후 에이전트의 세션에 게시됩니다.
게이트웨이 호스트 exec 승인은 명령이 완료될 때(그리고 선택적으로 임계값보다 오래 실행될 때) 동일한 생명주기 이벤트를 발생시킵니다.
승인이 적용된 exec는 이러한 메시지에서 승인 ID를 runId로 재사용하여 쉽게 연관시킬 수 있습니다.
시사점
- full은 강력합니다. 가능하면 허용 목록을 사용하세요.
- ask는 빠른 승인을 허용하면서 운영자를 루프에 유지합니다.
- 에이전트별 허용 목록은 한 에이전트의 승인이 다른 에이전트로 누출되는 것을 방지합니다.
- 승인은 인가된 발신자의 호스트 exec 요청에만 적용됩니다. 비인가 발신자는
/exec를 실행할 수 없습니다. /exec security=full은 인가된 운영자를 위한 세션 수준 편의 기능이며 설계상 승인을 건너뜁니다. 호스트 exec를 완전히 차단하려면, 승인 security를deny로 설정하거나 도구 정책을 통해exec도구를 거부하세요.
관련 문서: